[jackson-databind] 01/02: Imported Upstream version 2.2.2

Wolodja Wentland babilen-guest at alioth.debian.org
Thu Aug 22 17:47:26 UTC 2013


This is an automated email from the git hooks/post-receive script.

babilen-guest pushed a commit to branch master
in repository jackson-databind.

commit 96ca8425d9451898e6e9dc35b28a11b971e998f4
Author: Wolodja Wentland <babilen at gmail.com>
Date:   Thu Aug 22 16:44:19 2013 +0100

    Imported Upstream version 2.2.2
---
 .gitignore                                         |   21 +
 README.md                                          |  374 +++
 backup/TypeSerializerWrapper.java                  |  142 +
 create-test-report.sh                              |    3 +
 pom.xml                                            |  158 +
 release-notes/CREDITS                              |   88 +
 release-notes/VERSION                              |  387 +++
 .../jackson/databind/AbstractTypeResolver.java     |   52 +
 .../jackson/databind/AnnotationIntrospector.java   | 1135 ++++++++
 .../jackson/databind/BeanDescription.java          |  249 ++
 .../fasterxml/jackson/databind/BeanProperty.java   |  186 ++
 .../jackson/databind/DatabindContext.java          |  165 ++
 .../jackson/databind/DeserializationConfig.java    |  526 ++++
 .../jackson/databind/DeserializationContext.java   |  742 +++++
 .../jackson/databind/DeserializationFeature.java   |  289 ++
 .../jackson/databind/InjectableValues.java         |   85 +
 .../com/fasterxml/jackson/databind/JavaType.java   |  446 +++
 .../jackson/databind/JsonDeserializer.java         |  271 ++
 .../jackson/databind/JsonMappingException.java     |  370 +++
 .../com/fasterxml/jackson/databind/JsonNode.java   |  845 ++++++
 .../jackson/databind/JsonSerializable.java         |   49 +
 .../fasterxml/jackson/databind/JsonSerializer.java |  243 ++
 .../jackson/databind/KeyDeserializer.java          |   29 +
 .../fasterxml/jackson/databind/MapperFeature.java  |  289 ++
 .../jackson/databind/MappingIterator.java          |  266 ++
 .../jackson/databind/MappingJsonFactory.java       |   95 +
 .../com/fasterxml/jackson/databind/Module.java     |  285 ++
 .../fasterxml/jackson/databind/ObjectMapper.java   | 3010 ++++++++++++++++++++
 .../fasterxml/jackson/databind/ObjectReader.java   | 1528 ++++++++++
 .../fasterxml/jackson/databind/ObjectWriter.java   |  813 ++++++
 .../fasterxml/jackson/databind/PropertyName.java   |  173 ++
 .../jackson/databind/PropertyNamingStrategy.java   |  301 ++
 .../databind/RuntimeJsonMappingException.java      |   21 +
 .../jackson/databind/SerializationConfig.java      |  503 ++++
 .../jackson/databind/SerializationFeature.java     |  335 +++
 .../jackson/databind/SerializerProvider.java       |  931 ++++++
 .../databind/annotation/JacksonStdImpl.java        |   23 +
 .../databind/annotation/JsonDeserialize.java       |  135 +
 .../jackson/databind/annotation/JsonNaming.java    |   19 +
 .../databind/annotation/JsonPOJOBuilder.java       |   74 +
 .../jackson/databind/annotation/JsonSerialize.java |  224 ++
 .../databind/annotation/JsonTypeIdResolver.java    |   33 +
 .../databind/annotation/JsonTypeResolver.java      |   25 +
 .../databind/annotation/JsonValueInstantiator.java |   23 +
 .../jackson/databind/annotation/NoClass.java       |   14 +
 .../jackson/databind/annotation/package-info.java  |    7 +
 .../jackson/databind/cfg/BaseSettings.java         |  343 +++
 .../jackson/databind/cfg/ConfigFeature.java        |   20 +
 .../databind/cfg/DeserializerFactoryConfig.java    |  206 ++
 .../jackson/databind/cfg/HandlerInstantiator.java  |  146 +
 .../jackson/databind/cfg/MapperConfig.java         |  372 +++
 .../jackson/databind/cfg/MapperConfigBase.java     |  337 +++
 .../jackson/databind/cfg/PackageVersion.java.in    |   20 +
 .../databind/cfg/SerializerFactoryConfig.java      |   99 +
 .../jackson/databind/cfg/package-info.java         |    8 +
 .../databind/deser/AbstractDeserializer.java       |  182 ++
 .../databind/deser/BasicDeserializerFactory.java   | 1558 ++++++++++
 .../jackson/databind/deser/BeanDeserializer.java   |  802 ++++++
 .../databind/deser/BeanDeserializerBase.java       | 1370 +++++++++
 .../databind/deser/BeanDeserializerBuilder.java    |  416 +++
 .../databind/deser/BeanDeserializerFactory.java    |  852 ++++++
 .../databind/deser/BeanDeserializerModifier.java   |  171 ++
 .../databind/deser/BuilderBasedDeserializer.java   |  737 +++++
 .../databind/deser/ContextualDeserializer.java     |   43 +
 .../databind/deser/ContextualKeyDeserializer.java  |   34 +
 .../jackson/databind/deser/CreatorProperty.java    |  198 ++
 .../jackson/databind/deser/DataFormatReaders.java  |  388 +++
 .../deser/DefaultDeserializationContext.java       |  226 ++
 .../deser/DeserializationProblemHandler.java       |   62 +
 .../jackson/databind/deser/DeserializerCache.java  |  583 ++++
 .../databind/deser/DeserializerFactory.java        |  198 ++
 .../jackson/databind/deser/Deserializers.java      |  299 ++
 .../jackson/databind/deser/KeyDeserializers.java   |   19 +
 .../databind/deser/ResolvableDeserializer.java     |   45 +
 .../databind/deser/SettableAnyProperty.java        |  155 +
 .../databind/deser/SettableBeanProperty.java       |  514 ++++
 .../jackson/databind/deser/ValueInstantiator.java  |  292 ++
 .../jackson/databind/deser/ValueInstantiators.java |   47 +
 .../deser/impl/BeanAsArrayBuilderDeserializer.java |  368 +++
 .../deser/impl/BeanAsArrayDeserializer.java        |  357 +++
 .../databind/deser/impl/BeanPropertyMap.java       |  400 +++
 .../databind/deser/impl/CreatorCollector.java      |  219 ++
 .../databind/deser/impl/ExternalTypeHandler.java   |  317 +++
 .../jackson/databind/deser/impl/FieldProperty.java |  150 +
 .../databind/deser/impl/InnerClassProperty.java    |  126 +
 .../deser/impl/ManagedReferenceProperty.java       |  158 +
 .../databind/deser/impl/MethodProperty.java        |  142 +
 .../jackson/databind/deser/impl/NullProvider.java  |   39 +
 .../databind/deser/impl/ObjectIdReader.java        |   62 +
 .../databind/deser/impl/ObjectIdValueProperty.java |  117 +
 .../databind/deser/impl/PropertyBasedCreator.java  |  168 ++
 .../deser/impl/PropertyBasedObjectIdGenerator.java |   37 +
 .../jackson/databind/deser/impl/PropertyValue.java |  118 +
 .../databind/deser/impl/PropertyValueBuffer.java   |  155 +
 .../databind/deser/impl/ReadableObjectId.java      |   31 +
 .../databind/deser/impl/SetterlessProperty.java    |  142 +
 .../deser/impl/TypeWrappedDeserializer.java        |   58 +
 .../deser/impl/UnwrappedPropertyHandler.java       |   65 +
 .../jackson/databind/deser/impl/ValueInjector.java |   44 +
 .../jackson/databind/deser/impl/package-info.java  |    9 +
 .../jackson/databind/deser/package-info.java       |    5 +
 .../deser/std/ArrayBlockingQueueDeserializer.java  |  134 +
 .../databind/deser/std/ClassDeserializer.java      |   37 +
 .../databind/deser/std/CollectionDeserializer.java |  278 ++
 .../deser/std/ContainerDeserializerBase.java       |   36 +
 .../databind/deser/std/DateDeserializers.java      |  364 +++
 .../databind/deser/std/DelegatingDeserializer.java |  149 +
 .../databind/deser/std/EnumDeserializer.java       |  169 ++
 .../databind/deser/std/EnumMapDeserializer.java    |  196 ++
 .../databind/deser/std/EnumSetDeserializer.java    |  127 +
 .../databind/deser/std/FromStringDeserializer.java |   75 +
 .../databind/deser/std/JacksonDeserializers.java   |  169 ++
 .../databind/deser/std/JdkDeserializers.java       |  422 +++
 .../databind/deser/std/JsonNodeDeserializer.java   |  325 +++
 .../databind/deser/std/MapDeserializer.java        |  521 ++++
 .../databind/deser/std/NullifyingDeserializer.java |   55 +
 .../databind/deser/std/NumberDeserializers.java    |  594 ++++
 .../deser/std/ObjectArrayDeserializer.java         |  248 ++
 .../deser/std/PrimitiveArrayDeserializers.java     |  514 ++++
 .../deser/std/StdDelegatingDeserializer.java       |  185 ++
 .../databind/deser/std/StdDeserializer.java        |  715 +++++
 .../databind/deser/std/StdKeyDeserializer.java     |  450 +++
 .../databind/deser/std/StdKeyDeserializers.java    |  149 +
 .../databind/deser/std/StdScalarDeserializer.java  |   34 +
 .../databind/deser/std/StdValueInstantiator.java   |  444 +++
 .../deser/std/StringArrayDeserializer.java         |  162 ++
 .../deser/std/StringCollectionDeserializer.java    |  234 ++
 .../databind/deser/std/StringDeserializer.java     |   58 +
 .../databind/deser/std/ThrowableDeserializer.java  |  164 ++
 .../deser/std/UntypedObjectDeserializer.java       |  253 ++
 .../jackson/databind/deser/std/package-info.java   |   13 +
 .../databind/exc/InvalidFormatException.java       |   84 +
 .../exc/UnrecognizedPropertyException.java         |  155 +
 .../jackson/databind/ext/CoreXMLDeserializers.java |  120 +
 .../jackson/databind/ext/CoreXMLSerializers.java   |   70 +
 .../jackson/databind/ext/DOMDeserializer.java      |   69 +
 .../jackson/databind/ext/DOMSerializer.java        |   57 +
 .../databind/ext/OptionalHandlerFactory.java       |  174 ++
 .../jackson/databind/ext/package-info.java         |   23 +
 .../jackson/databind/introspect/Annotated.java     |   78 +
 .../databind/introspect/AnnotatedClass.java        | 1018 +++++++
 .../databind/introspect/AnnotatedConstructor.java  |  214 ++
 .../databind/introspect/AnnotatedField.java        |  187 ++
 .../databind/introspect/AnnotatedMember.java       |   90 +
 .../databind/introspect/AnnotatedMethod.java       |  276 ++
 .../databind/introspect/AnnotatedMethodMap.java    |   85 +
 .../databind/introspect/AnnotatedParameter.java    |  187 ++
 .../databind/introspect/AnnotatedWithParams.java   |  168 ++
 .../introspect/AnnotationIntrospectorPair.java     |  774 +++++
 .../jackson/databind/introspect/AnnotationMap.java |  102 +
 .../databind/introspect/BasicBeanDescription.java  |  611 ++++
 .../introspect/BasicClassIntrospector.java         |  188 ++
 .../introspect/BeanPropertyDefinition.java         |  175 ++
 .../databind/introspect/ClassIntrospector.java     |   94 +
 .../introspect/JacksonAnnotationIntrospector.java  |  900 ++++++
 .../jackson/databind/introspect/MemberKey.java     |   83 +
 .../introspect/NopAnnotationIntrospector.java      |   36 +
 .../jackson/databind/introspect/ObjectIdInfo.java  |   50 +
 .../introspect/POJOPropertiesCollector.java        |  837 ++++++
 .../databind/introspect/POJOPropertyBuilder.java   |  799 ++++++
 .../databind/introspect/VisibilityChecker.java     |  395 +++
 .../jackson/databind/introspect/WithMember.java    |    6 +
 .../jackson/databind/introspect/package-info.java  |   12 +
 .../jsonFormatVisitors/JsonAnyFormatVisitor.java   |   11 +
 .../jsonFormatVisitors/JsonArrayFormatVisitor.java |   55 +
 .../JsonBooleanFormatVisitor.java                  |   12 +
 .../jsonFormatVisitors/JsonFormatTypes.java        |   27 +
 .../jsonFormatVisitors/JsonFormatVisitable.java    |   20 +
 .../JsonFormatVisitorWithSerializerProvider.java   |   14 +
 .../JsonFormatVisitorWrapper.java                  |   59 +
 .../JsonIntegerFormatVisitor.java                  |   25 +
 .../jsonFormatVisitors/JsonMapFormatVisitor.java   |   45 +
 .../jsonFormatVisitors/JsonNullFormatVisitor.java  |   10 +
 .../JsonNumberFormatVisitor.java                   |   25 +
 .../JsonObjectFormatVisitor.java                   |   66 +
 .../JsonStringFormatVisitor.java                   |   13 +
 .../jsonFormatVisitors/JsonValueFormat.java        |  124 +
 .../jsonFormatVisitors/JsonValueFormatVisitor.java |   28 +
 .../jackson/databind/jsonschema/JsonSchema.java    |   96 +
 .../jsonschema/JsonSerializableSchema.java         |   70 +
 .../jackson/databind/jsonschema/SchemaAware.java   |   34 +
 .../jackson/databind/jsonschema/package-info.java  |    5 +
 .../jackson/databind/jsontype/NamedType.java       |   55 +
 .../jackson/databind/jsontype/SubtypeResolver.java |   51 +
 .../databind/jsontype/TypeDeserializer.java        |  193 ++
 .../jackson/databind/jsontype/TypeIdResolver.java  |   78 +
 .../databind/jsontype/TypeResolverBuilder.java     |  151 +
 .../jackson/databind/jsontype/TypeSerializer.java  |  231 ++
 .../jsontype/impl/AsArrayTypeDeserializer.java     |  146 +
 .../jsontype/impl/AsArrayTypeSerializer.java       |  176 ++
 .../jsontype/impl/AsExternalTypeDeserializer.java  |   45 +
 .../jsontype/impl/AsExternalTypeSerializer.java    |  205 ++
 .../jsontype/impl/AsPropertyTypeDeserializer.java  |  165 ++
 .../jsontype/impl/AsPropertyTypeSerializer.java    |   96 +
 .../jsontype/impl/AsWrapperTypeDeserializer.java   |  132 +
 .../jsontype/impl/AsWrapperTypeSerializer.java     |  174 ++
 .../jsontype/impl/ClassNameIdResolver.java         |  132 +
 .../jsontype/impl/FailingDeserializer.java         |   31 +
 .../jsontype/impl/MinimalClassNameIdResolver.java  |   67 +
 .../databind/jsontype/impl/StdSubtypeResolver.java |  173 ++
 .../jsontype/impl/StdTypeResolverBuilder.java      |  215 ++
 .../jsontype/impl/TypeDeserializerBase.java        |  206 ++
 .../databind/jsontype/impl/TypeIdResolverBase.java |   38 +
 .../databind/jsontype/impl/TypeNameIdResolver.java |  158 +
 .../databind/jsontype/impl/TypeSerializerBase.java |   42 +
 .../databind/jsontype/impl/package-info.java       |    7 +
 .../jackson/databind/jsontype/package-info.java    |    8 +
 .../module/SimpleAbstractTypeResolver.java         |   89 +
 .../databind/module/SimpleDeserializers.java       |  145 +
 .../databind/module/SimpleKeyDeserializers.java    |   61 +
 .../jackson/databind/module/SimpleModule.java      |  400 +++
 .../jackson/databind/module/SimpleSerializers.java |  226 ++
 .../databind/module/SimpleValueInstantiators.java  |   48 +
 .../jackson/databind/module/package-info.java      |   14 +
 .../fasterxml/jackson/databind/node/ArrayNode.java |  774 +++++
 .../jackson/databind/node/BaseJsonNode.java        |   98 +
 .../jackson/databind/node/BigIntegerNode.java      |  122 +
 .../jackson/databind/node/BinaryNode.java          |  123 +
 .../jackson/databind/node/BooleanNode.java         |   94 +
 .../jackson/databind/node/ContainerNode.java       |  145 +
 .../jackson/databind/node/DecimalNode.java         |  117 +
 .../jackson/databind/node/DoubleNode.java          |  124 +
 .../fasterxml/jackson/databind/node/FloatNode.java |  121 +
 .../fasterxml/jackson/databind/node/IntNode.java   |  133 +
 .../jackson/databind/node/JsonNodeCreator.java     |   49 +
 .../jackson/databind/node/JsonNodeFactory.java     |  326 +++
 .../jackson/databind/node/JsonNodeType.java        |   28 +
 .../fasterxml/jackson/databind/node/LongNode.java  |  111 +
 .../jackson/databind/node/MissingNode.java         |   98 +
 .../jackson/databind/node/NodeCursor.java          |  234 ++
 .../fasterxml/jackson/databind/node/NullNode.java  |   58 +
 .../jackson/databind/node/NumericNode.java         |   72 +
 .../jackson/databind/node/ObjectNode.java          |  811 ++++++
 .../fasterxml/jackson/databind/node/POJONode.java  |  153 +
 .../fasterxml/jackson/databind/node/ShortNode.java |  109 +
 .../fasterxml/jackson/databind/node/TextNode.java  |  301 ++
 .../databind/node/TreeTraversingParser.java        |  410 +++
 .../fasterxml/jackson/databind/node/ValueNode.java |  141 +
 .../jackson/databind/node/package-info.java        |    8 +
 .../fasterxml/jackson/databind/package-info.java   |   34 +
 .../jackson/databind/ser/AnyGetterWriter.java      |   53 +
 .../databind/ser/BasicSerializerFactory.java       |  999 +++++++
 .../jackson/databind/ser/BeanPropertyFilter.java   |   83 +
 .../jackson/databind/ser/BeanPropertyWriter.java   |  714 +++++
 .../jackson/databind/ser/BeanSerializer.java       |  156 +
 .../databind/ser/BeanSerializerBuilder.java        |  196 ++
 .../databind/ser/BeanSerializerFactory.java        |  766 +++++
 .../databind/ser/BeanSerializerModifier.java       |  187 ++
 .../jackson/databind/ser/ContainerSerializer.java  |  135 +
 .../jackson/databind/ser/ContextualSerializer.java |   41 +
 .../databind/ser/DefaultSerializerProvider.java    |  446 +++
 .../jackson/databind/ser/FilterProvider.java       |   21 +
 .../jackson/databind/ser/PropertyBuilder.java      |  239 ++
 .../jackson/databind/ser/ResolvableSerializer.java |   33 +
 .../jackson/databind/ser/SerializerCache.java      |  292 ++
 .../jackson/databind/ser/SerializerFactory.java    |  106 +
 .../jackson/databind/ser/Serializers.java          |  142 +
 .../databind/ser/impl/BeanAsArraySerializer.java   |  210 ++
 .../databind/ser/impl/FailingSerializer.java       |   51 +
 .../ser/impl/FilteredBeanPropertyWriter.java       |  146 +
 .../databind/ser/impl/IndexedListSerializer.java   |  171 ++
 .../ser/impl/IndexedStringListSerializer.java      |  197 ++
 .../databind/ser/impl/IteratorSerializer.java      |   85 +
 .../databind/ser/impl/JsonSerializerMap.java       |   91 +
 .../jackson/databind/ser/impl/ObjectIdWriter.java  |   85 +
 .../ser/impl/PropertyBasedObjectIdGenerator.java   |   77 +
 .../databind/ser/impl/PropertySerializerMap.java   |  230 ++
 .../ser/impl/ReadOnlyClassToSerializerMap.java     |   88 +
 .../ser/impl/SimpleBeanPropertyFilter.java         |  145 +
 .../databind/ser/impl/SimpleFilterProvider.java    |  108 +
 .../databind/ser/impl/StringArraySerializer.java   |  213 ++
 .../ser/impl/StringCollectionSerializer.java       |  195 ++
 .../databind/ser/impl/TypeWrappedSerializer.java   |   51 +
 .../databind/ser/impl/UnknownSerializer.java       |   61 +
 .../ser/impl/UnwrappingBeanPropertyWriter.java     |  141 +
 .../ser/impl/UnwrappingBeanSerializer.java         |  116 +
 .../databind/ser/impl/WritableObjectId.java        |   53 +
 .../jackson/databind/ser/impl/package-info.java    |    5 +
 .../jackson/databind/ser/package-info.java         |    5 +
 .../databind/ser/std/ArraySerializerBase.java      |   74 +
 .../databind/ser/std/AsArraySerializerBase.java    |  287 ++
 .../databind/ser/std/BeanSerializerBase.java       |  740 +++++
 .../databind/ser/std/BooleanSerializer.java        |   59 +
 .../databind/ser/std/CalendarSerializer.java       |   59 +
 .../databind/ser/std/CollectionSerializer.java     |  158 +
 .../jackson/databind/ser/std/DateSerializer.java   |   63 +
 .../databind/ser/std/DateTimeSerializerBase.java   |  151 +
 .../databind/ser/std/EnumMapSerializer.java        |  366 +++
 .../jackson/databind/ser/std/EnumSerializer.java   |  231 ++
 .../databind/ser/std/EnumSetSerializer.java        |   66 +
 .../databind/ser/std/InetAddressSerializer.java    |   49 +
 .../databind/ser/std/IterableSerializer.java       |   89 +
 .../databind/ser/std/JsonValueSerializer.java      |  286 ++
 .../jackson/databind/ser/std/MapSerializer.java    |  555 ++++
 .../ser/std/NonTypedScalarSerializerBase.java      |   32 +
 .../jackson/databind/ser/std/NullSerializer.java   |   47 +
 .../databind/ser/std/NumberSerializers.java        |  324 +++
 .../databind/ser/std/ObjectArraySerializer.java    |  388 +++
 .../jackson/databind/ser/std/RawSerializer.java    |   60 +
 .../databind/ser/std/SerializableSerializer.java   |  116 +
 .../databind/ser/std/SqlDateSerializer.java        |   51 +
 .../databind/ser/std/SqlTimeSerializer.java        |   45 +
 .../databind/ser/std/StaticListSerializerBase.java |   55 +
 .../databind/ser/std/StdArraySerializers.java      |  668 +++++
 .../databind/ser/std/StdContainerSerializers.java  |   81 +
 .../databind/ser/std/StdDelegatingSerializer.java  |  240 ++
 .../databind/ser/std/StdJdkSerializers.java        |  241 ++
 .../jackson/databind/ser/std/StdKeySerializer.java |   51 +
 .../databind/ser/std/StdKeySerializers.java        |   95 +
 .../databind/ser/std/StdScalarSerializer.java      |   61 +
 .../jackson/databind/ser/std/StdSerializer.java    |  257 ++
 .../jackson/databind/ser/std/StringSerializer.java |   54 +
 .../databind/ser/std/TimeZoneSerializer.java       |   35 +
 .../databind/ser/std/ToStringSerializer.java       |   93 +
 .../databind/ser/std/TokenBufferSerializer.java    |   78 +
 .../fasterxml/jackson/databind/type/ArrayType.java |  257 ++
 .../fasterxml/jackson/databind/type/ClassKey.java  |   97 +
 .../jackson/databind/type/CollectionLikeType.java  |  201 ++
 .../jackson/databind/type/CollectionType.java      |  103 +
 .../jackson/databind/type/HierarchicType.java      |   84 +
 .../jackson/databind/type/MapLikeType.java         |  249 ++
 .../fasterxml/jackson/databind/type/MapType.java   |  139 +
 .../jackson/databind/type/SimpleType.java          |  249 ++
 .../fasterxml/jackson/databind/type/TypeBase.java  |  147 +
 .../jackson/databind/type/TypeBindings.java        |  338 +++
 .../jackson/databind/type/TypeFactory.java         | 1049 +++++++
 .../jackson/databind/type/TypeModifier.java        |   36 +
 .../jackson/databind/type/TypeParser.java          |  132 +
 .../jackson/databind/type/package-info.java        |   10 +
 .../jackson/databind/util/Annotations.java         |   24 +
 .../jackson/databind/util/ArrayBuilders.java       |  358 +++
 .../fasterxml/jackson/databind/util/BeanUtil.java  |  194 ++
 .../fasterxml/jackson/databind/util/ClassUtil.java |  688 +++++
 .../fasterxml/jackson/databind/util/Converter.java |   66 +
 .../jackson/databind/util/EmptyIterator.java       |   33 +
 .../jackson/databind/util/EnumResolver.java        |  138 +
 .../jackson/databind/util/EnumValues.java          |   96 +
 .../jackson/databind/util/ISO8601DateFormat.java   |   52 +
 .../jackson/databind/util/ISO8601Utils.java        |  254 ++
 .../jackson/databind/util/JSONPObject.java         |   92 +
 .../jackson/databind/util/JSONWrappedObject.java   |  107 +
 .../fasterxml/jackson/databind/util/LRUMap.java    |   58 +
 .../jackson/databind/util/LinkedNode.java          |   45 +
 .../jackson/databind/util/NameTransformer.java     |  135 +
 .../com/fasterxml/jackson/databind/util/Named.java |    8 +
 .../jackson/databind/util/ObjectBuffer.java        |  252 ++
 .../jackson/databind/util/ObjectIdMap.java         |   38 +
 .../databind/util/PrimitiveArrayBuilder.java       |  180 ++
 .../fasterxml/jackson/databind/util/Provider.java  |   24 +
 .../jackson/databind/util/RootNameLookup.java      |   59 +
 .../util/SimpleBeanPropertyDefinition.java         |  201 ++
 .../jackson/databind/util/StdConverter.java        |   44 +
 .../jackson/databind/util/StdDateFormat.java       |  400 +++
 .../jackson/databind/util/TokenBuffer.java         | 1368 +++++++++
 .../jackson/databind/util/ViewMatcher.java         |   77 +
 .../jackson/databind/util/package-info.java        |    4 +
 src/main/resources/META-INF/LICENSE                |    8 +
 src/main/resources/META-INF/NOTICE                 |   20 +
 .../com.fasterxml.jackson.core.ObjectCodec         |    1 +
 .../fasterxml/jackson/databind/BaseMapTest.java    |  228 ++
 .../jackson/databind/TestFormatSchema.java         |  274 ++
 .../jackson/databind/TestGeneratorUsingMapper.java |   84 +
 .../jackson/databind/TestHandlerInstantiation.java |  267 ++
 .../jackson/databind/TestJDKSerialization.java     |  125 +
 .../jackson/databind/TestNamingStrategy.java       |  257 ++
 .../jackson/databind/TestObjectMapper.java         |  161 ++
 .../databind/TestObjectMapperBeanDeserializer.java |  286 ++
 .../databind/TestObjectMapperBeanSerializer.java   |  237 ++
 .../jackson/databind/TestParserUsingMapper.java    |  174 ++
 .../fasterxml/jackson/databind/TestReadValues.java |  237 ++
 .../fasterxml/jackson/databind/TestRootName.java   |  118 +
 .../jackson/databind/TestStdDateFormat.java        |   26 +
 .../jackson/databind/TestStdNamingStrategies.java  |  269 ++
 .../fasterxml/jackson/databind/TestVersions.java   |   40 +
 .../jackson/databind/access/TestSerAnyGetter.java  |   73 +
 .../contextual/TestContextualDeserialization.java  |  277 ++
 .../contextual/TestContextualKeyTypes.java         |  124 +
 .../contextual/TestContextualSerialization.java    |  291 ++
 .../TestContextualWithAnnDeserializer.java         |  107 +
 .../databind/convert/TestArrayConversions.java     |  216 ++
 .../databind/convert/TestBeanConversions.java      |  220 ++
 .../convert/TestConvertingDeserializer.java        |  183 ++
 .../databind/convert/TestConvertingSerializer.java |  132 +
 .../databind/convert/TestMapConversions.java       |   63 +
 .../convert/TestPolymorphicUpdateValue.java        |   45 +
 .../databind/convert/TestStringConversions.java    |   54 +
 .../jackson/databind/convert/TestUpdateValue.java  |  148 +
 .../databind/creators/TestBuilderSimple.java       |  217 ++
 .../databind/creators/TestConstructFromMap.java    |   96 +
 .../databind/creators/TestCreatorNullValue.java    |   84 +
 .../jackson/databind/creators/TestCreators.java    |  479 ++++
 .../jackson/databind/creators/TestCreators2.java   |  308 ++
 .../databind/creators/TestCreatorsDelegating.java  |  106 +
 .../databind/creators/TestPolymorphicCreators.java |  147 +
 .../databind/creators/TestValueInstantiator.java   |  521 ++++
 .../jackson/databind/deser/TestAbstract.java       |   38 +
 .../databind/deser/TestAnnotationIgnore.java       |   54 +
 .../databind/deser/TestAnnotationUsing.java        |  230 ++
 .../jackson/databind/deser/TestAnyProperties.java  |  202 ++
 .../databind/deser/TestArrayDeserialization.java   |  568 ++++
 .../jackson/databind/deser/TestAutoDetect.java     |   53 +
 .../databind/deser/TestBasicAnnotations.java       |  159 ++
 .../databind/deser/TestBeanDeserializer.java       |  329 +++
 .../jackson/databind/deser/TestBlocking.java       |   40 +
 .../deser/TestCollectionDeserialization.java       |  199 ++
 .../jackson/databind/deser/TestConcurrency.java    |   98 +
 .../jackson/databind/deser/TestConfig.java         |  120 +
 .../databind/deser/TestCustomDeserializers.java    |  229 ++
 .../jackson/databind/deser/TestCustomFactory.java  |  140 +
 .../jackson/databind/deser/TestCyclicTypes.java    |   86 +
 .../databind/deser/TestDateDeserialization.java    |  359 +++
 .../databind/deser/TestEnumDeserialization.java    |  335 +++
 .../deser/TestExceptionDeserialization.java        |  100 +
 .../databind/deser/TestExceptionHandling.java      |  125 +
 .../databind/deser/TestFieldDeserialization.java   |  162 ++
 .../databind/deser/TestGenericCollectionDeser.java |   82 +
 .../databind/deser/TestGenericMapDeser.java        |  170 ++
 .../jackson/databind/deser/TestGenericNumber.java  |  115 +
 .../jackson/databind/deser/TestGenerics.java       |  124 +
 .../databind/deser/TestGenericsBounded.java        |  120 +
 .../jackson/databind/deser/TestIgnoredTypes.java   |   49 +
 .../jackson/databind/deser/TestInjectables.java    |  118 +
 .../jackson/databind/deser/TestInnerClass.java     |   49 +
 .../jackson/databind/deser/TestJacksonTypes.java   |  109 +
 .../jackson/databind/deser/TestJdkTypes.java       |  354 +++
 .../databind/deser/TestMapDeserialization.java     |  489 ++++
 .../jackson/databind/deser/TestNullHandling.java   |   54 +
 .../jackson/databind/deser/TestNumbers.java        |  121 +
 .../jackson/databind/deser/TestOverloaded.java     |  156 +
 .../databind/deser/TestParentChildReferences.java  |  372 +++
 .../databind/deser/TestSetterlessProperties.java   |  130 +
 .../databind/deser/TestSimpleAtomicTypes.java      |   45 +
 .../jackson/databind/deser/TestSimpleTypes.java    |  443 +++
 .../jackson/databind/deser/TestStatics.java        |   36 +
 .../deser/TestTimestampDeserialization.java        |   43 +
 .../databind/deser/TestUntypedDeserialization.java |   94 +
 .../databind/deser/TestValueAnnotations.java       |  397 +++
 .../jackson/databind/ext/TestCoreXMLTypes.java     |  122 +
 .../fasterxml/jackson/databind/ext/TestDOM.java    |  101 +
 .../jackson/databind/ext/TestJDK16OnlyTypes.java   |   27 +
 .../filter/TestIgnorePropsForSerialization.java    |  117 +
 .../filter/TestSimpleSerializationIgnore.java      |  128 +
 .../filter/TestUnknownPropertyDeserialization.java |  272 ++
 .../jackson/databind/interop/TestCglibUsage.java   |   75 +
 .../databind/interop/TestExternalizable.java       |  245 ++
 .../databind/interop/TestFormatDetection.java      |   47 +
 .../jackson/databind/interop/TestGroovyBeans.java  |   76 +
 .../jackson/databind/interop/TestHibernate.java    |   85 +
 .../jackson/databind/interop/TestJDKProxy.java     |   73 +
 .../databind/introspect/TestAnnotationMerging.java |   93 +
 .../databind/introspect/TestAnnotionBundles.java   |   77 +
 .../databind/introspect/TestBuilderMethods.java    |   56 +
 .../databind/introspect/TestInferredMutators.java  |   65 +
 .../TestJacksonAnnotationIntrospector.java         |  216 ++
 .../introspect/TestPOJOPropertiesCollector.java    |  417 +++
 .../jsonschema/TestGenerateJsonSchema.java         |  226 ++
 .../databind/jsonschema/TestReadJsonSchema.java    |   62 +
 .../databind/jsontype/TestAbstractTypeNames.java   |  136 +
 .../jsontype/TestAbstractWithObjectId.java         |   68 +
 .../jsontype/TestCustomTypeIdResolver.java         |  100 +
 .../databind/jsontype/TestDefaultForArrays.java    |  104 +
 .../databind/jsontype/TestDefaultForEnums.java     |   77 +
 .../databind/jsontype/TestDefaultForLists.java     |  146 +
 .../databind/jsontype/TestDefaultForMaps.java      |  100 +
 .../databind/jsontype/TestDefaultForObject.java    |  360 +++
 .../databind/jsontype/TestDefaultForScalars.java   |  112 +
 .../databind/jsontype/TestDefaultWithCreators.java |   61 +
 .../jackson/databind/jsontype/TestEnumTyping.java  |  105 +
 .../jackson/databind/jsontype/TestExternalId.java  |  425 +++
 .../jsontype/TestGenericListSerialization.java     |   93 +
 .../jackson/databind/jsontype/TestNoTypeInfo.java  |   40 +
 .../databind/jsontype/TestPropertyTypeInfo.java    |  201 ++
 .../jackson/databind/jsontype/TestScalars.java     |  106 +
 .../jackson/databind/jsontype/TestSubtypes.java    |  215 ++
 .../jackson/databind/jsontype/TestTypeNames.java   |  153 +
 .../jsontype/TestTypedArrayDeserialization.java    |  114 +
 .../jsontype/TestTypedArraySerialization.java      |  155 +
 .../jsontype/TestTypedContainerSerialization.java  |  159 ++
 .../jsontype/TestTypedDeserialization.java         |  244 ++
 .../TestTypedDeserializationWithDefault.java       |  107 +
 .../databind/jsontype/TestTypedSerialization.java  |  204 ++
 .../databind/jsontype/TestVisibleTypeId.java       |  210 ++
 .../databind/jsontype/TestWithGenerics.java        |  204 ++
 .../databind/mixins/TestMixinDeserForClass.java    |   98 +
 .../databind/mixins/TestMixinDeserForCreators.java |  117 +
 .../databind/mixins/TestMixinDeserForMethods.java  |   55 +
 .../databind/mixins/TestMixinInheritance.java      |   73 +
 .../databind/mixins/TestMixinSerForClass.java      |  118 +
 .../databind/mixins/TestMixinSerForFields.java     |   94 +
 .../databind/mixins/TestMixinSerForMethods.java    |  191 ++
 .../databind/mixins/TestMixinSerWithViews.java     |  205 ++
 .../jackson/databind/module/TestAbstractTypes.java |   79 +
 .../databind/module/TestKeyDeserializers.java      |   49 +
 .../jackson/databind/module/TestSimpleModule.java  |  319 +++
 .../jackson/databind/module/TestTypeModifiers.java |  277 ++
 .../jackson/databind/node/NodeTestBase.java        |   30 +
 .../jackson/databind/node/TestArrayNode.java       |  167 ++
 .../jackson/databind/node/TestConversions.java     |  217 ++
 .../jackson/databind/node/TestDeepCopy.java        |   88 +
 .../jackson/databind/node/TestFindMethods.java     |   73 +
 .../jackson/databind/node/TestJsonNode.java        |  102 +
 .../jackson/databind/node/TestMissingNode.java     |   28 +
 .../jackson/databind/node/TestNullNode.java        |   43 +
 .../jackson/databind/node/TestNumberNodes.java     |  226 ++
 .../jackson/databind/node/TestObjectNode.java      |  232 ++
 .../databind/node/TestTreeDeserialization.java     |  118 +
 .../databind/node/TestTreeMapperDeserializer.java  |  438 +++
 .../jackson/databind/node/TestTreeMapperMaps.java  |   74 +
 .../databind/node/TestTreeMapperSerializer.java    |  223 ++
 .../databind/node/TestTreeTraversingParser.java    |  273 ++
 .../databind/ser/TestAnnotationInheritance.java    |  110 +
 .../jackson/databind/ser/TestAnnotations.java      |  260 ++
 .../jackson/databind/ser/TestAnyGetter.java        |   76 +
 .../databind/ser/TestArraySerialization.java       |   98 +
 .../jackson/databind/ser/TestAutoDetect.java       |  124 +
 .../jackson/databind/ser/TestBeanSerializer.java   |  367 +++
 .../databind/ser/TestCollectionSerialization.java  |  357 +++
 .../fasterxml/jackson/databind/ser/TestConfig.java |  197 ++
 .../databind/ser/TestCustomSerializers.java        |  108 +
 .../jackson/databind/ser/TestCyclicTypes.java      |   78 +
 .../databind/ser/TestDateSerialization.java        |  166 ++
 .../jackson/databind/ser/TestEmptyClass.java       |  100 +
 .../databind/ser/TestEnumSerialization.java        |  282 ++
 .../databind/ser/TestExceptionHandling.java        |  156 +
 .../databind/ser/TestExceptionSerialization.java   |   42 +
 .../jackson/databind/ser/TestFeatures.java         |  269 ++
 .../databind/ser/TestFieldSerialization.java       |  212 ++
 .../jackson/databind/ser/TestFiltering.java        |  126 +
 .../jackson/databind/ser/TestGenericTypes.java     |  158 +
 .../fasterxml/jackson/databind/ser/TestJSONP.java  |   53 +
 .../jackson/databind/ser/TestJacksonTypes.java     |   48 +
 .../jackson/databind/ser/TestJdkTypes.java         |  102 +
 .../jackson/databind/ser/TestJsonRawValue.java     |   67 +
 .../jackson/databind/ser/TestJsonSerialize.java    |  231 ++
 .../jackson/databind/ser/TestJsonSerialize2.java   |  217 ++
 .../jackson/databind/ser/TestJsonSerialize3.java   |   44 +
 .../jackson/databind/ser/TestJsonSerializeAs.java  |   59 +
 .../jackson/databind/ser/TestJsonValue.java        |  254 ++
 .../jackson/databind/ser/TestKeySerializers.java   |   64 +
 .../jackson/databind/ser/TestMapSerialization.java |  128 +
 .../jackson/databind/ser/TestNullProperties.java   |  146 +
 .../databind/ser/TestNullSerialization.java        |   80 +
 .../jackson/databind/ser/TestObjectWriter.java     |   45 +
 .../jackson/databind/ser/TestRootType.java         |  185 ++
 .../databind/ser/TestSerializationOrder.java       |  119 +
 .../databind/ser/TestSerializerProvider.java       |   34 +
 .../databind/ser/TestSimpleAtomicTypes.java        |   42 +
 .../jackson/databind/ser/TestSimpleTypes.java      |  133 +
 .../jackson/databind/ser/TestStatics.java          |   62 +
 .../databind/ser/TestTreeSerialization.java        |  112 +
 .../ser/TestTypedRootValueSerialization.java       |   67 +
 .../databind/ser/TestUntypedSerialization.java     |  103 +
 .../databind/struct/TestFormatForCollections.java  |   61 +
 .../jackson/databind/struct/TestObjectId.java      |  139 +
 .../struct/TestObjectIdDeserialization.java        |  196 ++
 .../databind/struct/TestObjectIdSerialization.java |  261 ++
 .../struct/TestObjectIdWithPolymorphic.java        |  164 ++
 .../jackson/databind/struct/TestPOJOAsArray.java   |  184 ++
 .../databind/struct/TestPOJOAsArrayAdvanced.java   |  123 +
 .../struct/TestPOJOAsArrayWithBuilder.java         |   63 +
 .../jackson/databind/struct/TestUnwrapped.java     |  187 ++
 .../databind/struct/TestUnwrappedWithPrefix.java   |  249 ++
 .../jackson/databind/type/TestAnnotatedClass.java  |   87 +
 .../databind/type/TestGenericFieldInSubtype.java   |   44 +
 .../jackson/databind/type/TestJavaType.java        |  190 ++
 .../jackson/databind/type/TestTypeBindings.java    |   75 +
 .../jackson/databind/type/TestTypeFactory.java     |  537 ++++
 .../jackson/databind/type/TestTypeResolution.java  |   54 +
 .../jackson/databind/util/ArrayBuildersTest.java   |   27 +
 .../databind/util/ISO8601DateFormatTest.java       |   43 +
 .../jackson/databind/util/ISO8601UtilsTest.java    |   61 +
 .../jackson/databind/util/TestClassUtil.java       |  140 +
 .../jackson/databind/util/TestObjectBuffer.java    |   71 +
 .../jackson/databind/util/TestTokenBuffer.java     |  298 ++
 .../databind/views/TestViewDeserialization.java    |  104 +
 .../databind/views/TestViewSerialization.java      |  183 ++
 .../databind/views/TestViewsSerialization2.java    |  161 ++
 .../failing/TestBackRefsWithPolymorphic.java       |  220 ++
 .../jackson/failing/TestExternalTypeId.java        |  110 +
 .../fasterxml/jackson/failing/TestInnerClass.java  |   51 +
 .../fasterxml/jackson/failing/TestIssueGH113.java  |   65 +
 .../fasterxml/jackson/failing/TestJavaType.java    |   24 +
 .../jackson/failing/TestMapJsonValueKey.java       |   49 +
 .../jackson/failing/TestNameConflicts.java         |   41 +
 .../jackson/failing/TestObjectIdSerialization.java |   94 +
 .../fasterxml/jackson/failing/TestPOJOAsArray.java |   29 +
 .../failing/TestSerializationFiltering.java        |   51 +
 .../jackson/failing/TestTreeWithType.java          |   70 +
 .../fasterxml/jackson/failing/TestTypeAliases.java |   42 +
 .../jackson/failing/TestUnwrappedWithPrefix.java   |   53 +
 .../java/com/fasterxml/jackson/test/BaseTest.java  |  411 +++
 .../fasterxml/jackson/test/BrokenStringReader.java |   26 +
 .../fasterxml/jackson/test/BrokenStringWriter.java |   32 +
 src/test/java/perf/ManualObjectWriterPerf.java     |  113 +
 src/test/java/perf/ManualReadPerfWithMedia.java    |   46 +
 src/test/java/perf/ManualReadPerfWithRecord.java   |   24 +
 src/test/java/perf/MediaItem.java                  |  126 +
 src/test/java/perf/NopOutputStream.java            |   22 +
 src/test/java/perf/ObjectReaderBase.java           |  106 +
 src/test/java/perf/Record.java                     |   14 +
 src/test/java/perf/RecordAsArray.java              |   14 +
 src/test/java/perf/RecordBase.java                 |   19 +
 602 files changed, 112237 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84914ec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# use glob syntax.
+syntax: glob
+*.class
+*~
+*.bak
+*.off
+*.old
+.DS_Store
+
+# building
+target
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# IDEA
+*.iml
+*.ipr
+*.iws
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4bc0548
--- /dev/null
+++ b/README.md
@@ -0,0 +1,374 @@
+# Overview
+
+This project contains the general-purpose data-binding functionality
+and tree-model for [Jackson Data Processor](http://wiki.fasterxml.com/JacksonHome)
+It builds on [core streaming parser/generator](https://github.com/FasterXML/jackson-core) package,
+and uses [Jackson Annotations](https://github.com/FasterXML//jackson-annotations) for configuration.
+
+While the original use case for Jackson was JSON data-binding,
+it can now be used for other data formats as well, as long as
+parser and generator implementations exist.
+Naming of classes uses word 'JSON' in many places even though there is no
+actual hard dependency to JSON format.
+
+[![Build Status](https://fasterxml.ci.cloudbees.com/job/jackson-databind-master/badge/icon)](https://fasterxml.ci.cloudbees.com/job/jackson-databind-master/)
+### Differences from Jackson 1.x
+
+Project contains versions 2.0 and above: source code for earlier (1.x) versions is available from [Codehaus](http://jackson.codehaus.org) SVN repository
+Main differences compared to 1.0 "mapper" jar are:
+
+* Maven build instead of Ant
+* Java package is now `com.fasterxml.jackson.databind` (instead of `org.codehaus.jackson.map`)
+
+-----
+
+# Get it!
+
+## Maven
+
+Functionality of this package is contained in Java package `com.fasterxml.jackson.databind`, and can be used using following Maven dependency:
+
+```xml
+<dependency>
+  <groupId>com.fasterxml.jackson.core</groupId>
+  <artifactId>jackson-databind</artifactId>
+  <version>2.2.0</version>
+</dependency>
+```
+
+Since package also depends on '''jackson-core''' and '''jackson-databind''' packages, you will need to download these if not using Maven; and you may also want to add them as Maven dependency to ensure that compatible versions are used.
+If so, also add:
+
+```xml
+<dependency>
+  <groupId>com.fasterxml.jackson.core</groupId>
+  <artifactId>jackson-annotations</artifactId>
+  <version>2.2.0</version>
+</dependency>
+<dependency>
+  <groupId>com.fasterxml.jackson.core</groupId>
+  <artifactId>jackson-core</artifactId>
+  <version>2.2.0</version>
+</dependency>
+```
+
+but note that this is optional, and only necessary if there are conflicts between jackson core dependencies through transitive dependencies.
+
+## Non-Maven
+
+For non-Maven use cases, you download jars from [Central Maven repository](http://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/) or [Download page](https://github.com/FasterXML/jackson-databind/wiki/JacksonDownload).
+
+Databind jar is also a functional OSGi bundle, with proper import/export declarations, so it can be use on OSGi container as is.
+
+-----
+
+# Use It!
+
+While wiki contains more documentation, here are brief introductionary tutorials, in recommended order of reading.
+
+## 1 minute tutorial: POJOs to JSON and back
+
+The most common usage is to take piece of JSON, and construct a Plain Old Java Object ("POJO") out of it. So let's start there.
+
+All data binding starts with a `com.fasterxml.jackson.databind.ObjectMapper` instance, so let's construct one:
+
+```java
+ObjectMapper mapper = new ObjectMapper(); // create once, reuse
+```
+
+The default instance is fine for our use -- we will learn later on how to configure mapper instance if necessary. Usage is simple:
+
+```java
+MyValue value = mapper.readValue(new File("data.json"), MyValue.class);
+// or:
+value = mapper.readValue(new URL("http://some.com/api/entry.json"), MyValue.class);
+// or:
+value = mapper.readValue("{\"name\":\"Bob\", \"age\":13}", MyValue.class);
+```
+
+And if we want to write JSON, we do the reverse:
+
+```java
+mapper.writeValue(new File("result.json"), myResultObject);
+// or:
+byte[] jsonBytes = mapper.writeValueAsBytes(myResultObject);
+// or:
+String jsonString = mapper.writeValueAsString(myResultObject);
+```
+
+So far so good?
+
+## 3 minute tutorial: Generic collections, Tree Model
+
+Beyond dealing with simple Bean-style POJOs, you can also handle JDK `List`s, `Map`s:
+
+```java
+Map<String, Integer> scoreByName = mapper.readValue(jsonSource, Map.class);
+List<String> names = mapper.readValue(jsonSource, List.class);
+
+// and can obviously write out as well
+mapper.writeValue(new File("names.json"), names);
+```
+
+as long as JSON structure matches, and types are simple.
+If you have POJO values, you need to indicate actual type (note: this is NOT needed for POJO properties with `List` etc types):
+
+```java
+Map<String, ResultValue> results = mapper.readValue(jsonSource,
+   new TypeReference<String, ResultValue>() { } );
+// why extra work? Java Type Erasure will prevent type detection otherwise
+```
+
+(note: no extra effort needed for serialization, regardless of generic types)
+
+But wait! There is more!
+
+While dealing with `Map`s, `List`s and other "simple" Object types (Strings, Numbers, Booleans) can be simple, Object traversal can be cumbersome.
+This is where Jackson's [Tree model](https://github.com/FasterXML/jackson-databind/wiki/JacksonTreeModel) can come in handy:
+
+```java
+// can be read as generic JsonNode, if it can be Object or Array; or,
+// if known to be Object, as ObjectNode, if array, ArrayNode etc:
+ObjectNode root = mapper.readTree("stuff.json");
+String name = root.get("name").asText();
+int age = root.get("age").asInt();
+
+// can modify as well: this adds child Object as property 'other', set property 'type'
+root.with("other").put("type", "student");
+String json = mapper.writeValueAsString(root);
+
+// with above, we end up with something like as 'json' String:
+// {
+//   "name" : "Bob", "age" : 13,
+//   "other" : {
+//      "type" : "student"
+//   {
+// }
+```
+
+Tree Model can be more convenient than data-binding, especially in cases where structure is highly dynamic, or does not map nicely to Java classes.
+
+## 5 minute tutorial: Streaming parser, generator
+
+As convenient as data-binding (to/from POJOs) can be; and as flexible as Tree model can be, there is one more canonical processing model available: incremental (aka "streaming") model.
+It is the underlying processing model that data-binding and Tree Model both build upon, but it is also exposed to users who want ultimate performance and/or control over parsing or generation details.
+
+For in-depth explanation, look at [Jackson Core component](https://github.com/FasterXML/jackson-core).
+But let's look at a simple teaser to whet your appetite:
+
+(TO BE COMPLETED)
+
+
+## 10 minute tutorial: configuration
+
+There are two entry-level configuration mechanisms you are likely to use:
+[Features](https://github.com/FasterXML/jackson-databind/wiki/JacksonFeatures) and [Annotations](https://github.com/FasterXML/jackson-annotations).
+
+### Commonly used Features
+
+Here are examples of configuration features that you are most likely to need to know about.
+
+Let's start with higher-level data-binding configuration.
+
+```java
+// SerializationFeature for changing how JSON is written
+
+// to enable standard indentation ("pretty-printing"):
+mapper.enable(SerializationFeature.INDENT_OUTPUT);
+// to allow serialization of "empty" POJOs (no properties to serialize)
+// (without this setting, an exception is thrown in those cases)
+mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+// to write java.util.Date, Calendar as number (timestamp):
+mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+
+// DeserializationFeature for changing how JSON is read as POJOs:
+
+// to prevent exception when encountering unknown property:
+mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+// to allow coercion of JSON empty String ("") to null Object value:
+mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+```
+
+In addition, you may need to change some of low-level JSON parsing, generation details:
+
+```java
+// JsonParser.Feature for configuring parsing settings:
+
+// to allow C/C++ style comments in JSON (non-standard, disabled by default)
+mapper.enable(JsonParser.Feature.ALLOW_COMMENTS);
+// to allow (non-standard) unquoted field names in JSON:
+mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);
+// to allow use of apostrophes (single quotes), non standard
+mapper.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
+
+// JsonGenerator.Feature for configuring low-level JSON generation:
+
+// to force escaping of non-ASCII characters:
+mapper.enable(JsonGenerator.Feature.ESCAPE_NON_ASCII);
+```
+
+Full set of features are explained on [Jackson Features](https://github.com/FasterXML/jackson-databind/wiki/JacksonFeatures) page.
+
+### Annotations: changing property names
+
+The simplest annotation-based approach is to use `@JsonProperty` annotation like so:
+
+```java
+public class MyBean {
+   private String _name;
+
+   // without annotation, we'd get "theName", but we want "name":
+   @JsonProperty("name")
+   public String getTheName() { return _name; }
+
+   // note: it is enough to add annotation on just getter OR setter;
+   // so we can omit it here
+   public void setTheName(String n) { _name = n; }
+}
+```
+
+There are other mechanisms to use for systematic naming changes: see [Custom Naming Convention](https://github.com/FasterXML/jackson-databind/wiki/JacksonCustomNamingConvention) for details.
+
+Note, too, that you can use [Mix-in Annotations](https://github.com/FasterXML/jackson-databind/wiki/JacksonMixinAnnotations) to associate all annotations.
+
+### Annotations: Ignoring properties
+
+There are two main annotations that can be used to to ignore properties: `@JsonIgnore` for individual properties; and `@JsonIgnoreProperties` for per-class definition
+
+```java
+// means that if we see "foo" or "bar" in JSON, they will be quietly skipped
+// regardless of whether POJO has such properties
+ at JsonIgnoreProperties({ "foo", "bar" })
+public class MyBean
+{
+   // will not be written as JSON; nor assigned from JSON:
+   @JsonIgnore
+   public String internal;
+
+   // no annotation, public field is read/written normally
+   public String external;
+
+   @JsonIgnore
+   public void setCode(int c) { _code = c; }
+
+   // note: will also be ignored because setter has annotation!
+   public int getCode() { return _code; }
+}
+```
+
+As with renaming, note that annotations are "shared" between matching fields, getters and setters: if only one has `@JsonIgnore`, it affects others.
+But it is also possible to use "split" annotations, to for example:
+
+```java
+public class ReadButDontWriteProps {
+   private String _name;
+   @JsonProperty public void setName(String n) { _name = n; }
+   @JsonIgnore public String getName() { return _name; }
+}
+```
+
+in this case, no "name" property would be written out (since 'getter' is ignored); but if "name" property was found from JSON, it would be assigned to POJO property!
+
+For a more complete explanation of all possible ways of ignoring properties when writing out JSON, check ["Filtering properties"](http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html) article.
+
+### Annotations: using custom constructor
+
+Unlike many other data-binding packages, Jackson does not require you to define "default constructor" (constructor that does not take arguments).
+While it will use one if nothing else is available, you can easily define that an argument-taking constructor is used:
+
+```java
+public class CtorBean
+{
+  public final String name;
+  public final int age;
+
+  @JsonCreator // constructor can be public, private, whatever
+  private CtorBean(@JsonProperty("name") String name,
+    @JsonProperty("age") int age)
+  {
+      this.name = name;
+      this.age = age;
+  }
+}
+```
+
+Constructors are especially useful in supporting use of
+[Immutable objects](http://www.cowtowncoder.com/blog/archives/2010/08/entry_409.html).
+
+Alternatively, you can also define "factory methods":
+
+```java
+public class FactoryBean
+{
+    // fields etc omitted for brewity
+
+    @JsonCreator
+    public static FactoryBean create(@JsonProperty("name") String name) {
+      // construct and return an instance
+    }
+}
+```
+
+Note that use of a "creator method" does not preclude use of setters: you
+can mix and match properties from constructor/factory method with ones that
+are set via setters or directly using fields.
+
+## Tutorial: fancier stuff, conversions
+
+One useful (but not very widely known) feature of Jackson is its ability
+to do arbitrary POJO-to-POJO conversions. Conceptually you can think of conversions as sequence of 2 steps: first, writing a POJO as JSON, and second, binding that JSON into another kind of POJO. Implementation just skips actual generation of JSON, and uses more efficient intermediate representation.
+
+Conversations work between any compatible types, and invocation is as simple as:
+
+```java
+ResultType result = mapper.convertValue(sourceObject, ResultType.class);
+```
+
+and as long as source and result types are compatible -- that is, if to-JSON, from-JSON sequence would succeed -- things will "just work".
+But here are couple of potentially useful use cases:
+
+```java
+// Convert from int[] to List<Integer>
+List<Integer> sourceList = ...;
+int[] ints = mapper.convertValue(sourceList, int[].class);
+// Convert a POJO into Map!
+Map<String,Object> propertyMap = mapper.convertValue(pojoValue, Map.class);
+// ... and back
+PojoType pojo = mapper.convertValue(propertyMap, PojoType.class);
+// decode Base64! (default byte[] representation is base64-encoded String)
+String base64 = "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz";
+byte[] binary = mapper.convertValue(base64, byte[].class);
+```
+
+Basically, Jackson can work as a replacement for many Apache Commons components, for tasks like base64 encoding/decoding, and handling of "dyna beans" (Maps to/from POJOs).
+
+# Contribute!
+
+We would love to get your contribution, whether it's in form of bug reports, Requests for Enhancement (RFE), documentation, or code patches.
+The primary mechanism for all of above is [GitHub Issues system](https://github.com/FasterXML/jackson-databind/issues).
+
+## Basic rules for Code Contributions
+
+There is really just one main rule, which is that to accept any code contribution, we need to get a filled Contributor License Agreement (CLA) from the author. One CLA is enough for any number of contributions, but we need one. Or, rather, companies that use our code want it. It keeps their lawyers less unhappy about Open Source usage.
+
+## Limitation on Dependencies by Core Components
+
+One additional limitation exists for so-called core components (streaming api, jackson-annotations and jackson-databind): no additional dependendies are allowed beyond:
+
+* Core components may rely on any methods included in the supported JDK: currently core components are limited to JDK 1.5
+* Jackson-databind (this package) depends on the other two (annotations, streaming).
+
+This means that anything that has to rely on additional APIs or libraries needs to be built as an extension, usually a Jackson module.
+
+-----
+
+# Further reading
+
+* [Documentation](https://github.com/FasterXML/jackson-databind/wiki/Documentation)
+
+Related:
+
+* [Core annotations](https://github.com/FasterXML/jackson-annotations) package defines annotations commonly used for configuring databinding details
+* [Core parser/generator](https://github.com/FasterXML/jackson-core) package defines low-level incremental/streaming parsers, generators
+* [Jackson Project Home](http://wiki.fasterxml.com/JacksonHome) has additional documentation (although much of it for Jackson 1.x)
+
diff --git a/backup/TypeSerializerWrapper.java b/backup/TypeSerializerWrapper.java
new file mode 100644
index 0000000..b0ffb29
--- /dev/null
+++ b/backup/TypeSerializerWrapper.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.BeanProperty;
+
+/**
+ * Helper class used in cases where we caller has to override source
+ * for type identifier, for example when serializing a value using
+ * a delegate or surrogate value, in which case type id is to be based
+ * on the original value, but serialization done using surrogate.
+ * 
+ * @since 2.2
+ */
+public class TypeSerializerWrapper
+    extends TypeSerializer
+{
+    /**
+     * Actual TypeSerializer to use
+     */
+    protected final TypeSerializer _delegate;
+
+    protected final Object _value;
+    
+    public TypeSerializerWrapper(TypeSerializer delegate, Object value)
+    {
+        _delegate = delegate;
+        _value = value;
+    }
+    
+    /*
+    /**********************************************************
+    /* TypeSerializer implementation, metadata
+    /**********************************************************
+     */
+    
+    @Override
+    public TypeSerializer forProperty(BeanProperty prop) {
+        TypeSerializer d2 = _delegate.forProperty(prop);
+        if (d2 == _delegate) {
+            return this;
+        }
+        return new TypeSerializerWrapper(d2, _value);
+    }
+
+    @Override
+    public As getTypeInclusion() {
+        return _delegate.getTypeInclusion();
+    }
+
+    @Override
+    public String getPropertyName() {
+        return _delegate.getPropertyName();
+    }
+
+    @Override
+    public TypeIdResolver getTypeIdResolver() {
+        return _delegate.getTypeIdResolver();
+    }
+
+    /*
+    /**********************************************************
+    /* TypeSerializer implementation, actual write methods
+    /**********************************************************
+     */
+    
+    @Override
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypePrefixForScalar(_value, jgen);
+    }
+
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypePrefixForObject(_value, jgen);
+    }
+
+    @Override
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypePrefixForArray(_value, jgen);
+    }
+
+    @Override
+    public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypeSuffixForScalar(_value, jgen);
+    }
+
+    @Override
+    public void writeTypeSuffixForObject(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypeSuffixForObject(_value, jgen);
+    }
+
+    @Override
+    public void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException {
+        _delegate.writeTypeSuffixForArray(_value, jgen);
+    }
+
+    @Override
+    public void writeCustomTypePrefixForScalar(Object value,
+            JsonGenerator jgen, String typeId) throws IOException, JsonProcessingException {
+        _delegate.writeCustomTypePrefixForScalar(_value, jgen, typeId);
+    }
+
+    @Override
+    public void writeCustomTypePrefixForObject(Object value,
+            JsonGenerator jgen, String typeId) throws IOException, JsonProcessingException {
+        _delegate.writeCustomTypePrefixForObject(_value, jgen, typeId);
+    }
+
+    @Override
+    public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen,
+            String typeId) throws IOException, JsonProcessingException {
+        _delegate.writeCustomTypePrefixForArray(_value, jgen, typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForScalar(Object value,
+            JsonGenerator jgen, String typeId) throws IOException, JsonProcessingException {
+        _delegate.writeCustomTypeSuffixForScalar(_value, jgen, typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForObject(Object value,
+            JsonGenerator jgen, String typeId) throws IOException,
+            JsonProcessingException {
+        _delegate.writeCustomTypeSuffixForObject(_value, jgen, typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen,
+            String typeId) throws IOException, JsonProcessingException {
+        _delegate.writeCustomTypeSuffixForArray(_value, jgen, typeId);
+    }
+}
diff --git a/create-test-report.sh b/create-test-report.sh
new file mode 100755
index 0000000..5fef678
--- /dev/null
+++ b/create-test-report.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+mvn surefire-report:report  
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..635395e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>com.fasterxml</groupId>
+    <artifactId>oss-parent</artifactId>
+    <version>10</version>
+  </parent>
+
+  <groupId>com.fasterxml.jackson.core</groupId>
+  <artifactId>jackson-databind</artifactId>
+  <version>2.2.2</version>
+
+  <name>jackson-databind</name>
+  <description>General data-binding functionality for Jackson: works on core streaming API</description>
+  <url>http://wiki.fasterxml.com/JacksonHome</url>
+
+  <scm>
+    <connection>scm:git:git at github.com:FasterXML/jackson-databind.git</connection>
+    <developerConnection>scm:git:git at github.com:FasterXML/jackson-databind.git</developerConnection>
+    <url>http://github.com/FasterXML/jackson-databind</url>
+    <tag>jackson-databind-2.2.2</tag>
+  </scm>
+
+  <properties>
+    <osgi.export>
+com.fasterxml.jackson.databind,
+com.fasterxml.jackson.databind.annotation,
+com.fasterxml.jackson.databind.cfg,
+com.fasterxml.jackson.databind.deser,
+com.fasterxml.jackson.databind.deser.impl,
+com.fasterxml.jackson.databind.deser.std,
+com.fasterxml.jackson.databind.exc,
+com.fasterxml.jackson.databind.ext,
+com.fasterxml.jackson.databind.introspect,
+com.fasterxml.jackson.databind.jsonschema,
+com.fasterxml.jackson.databind.jsonFormatVisitors,
+com.fasterxml.jackson.databind.jsontype,
+com.fasterxml.jackson.databind.jsontype.impl,
+com.fasterxml.jackson.databind.module,
+com.fasterxml.jackson.databind.node,
+com.fasterxml.jackson.databind.ser,
+com.fasterxml.jackson.databind.ser.impl,
+com.fasterxml.jackson.databind.ser.std,
+com.fasterxml.jackson.databind.type,
+com.fasterxml.jackson.databind.util
+    </osgi.export>
+    <osgi.import>
+com.fasterxml.jackson.annotation,
+com.fasterxml.jackson.core,
+com.fasterxml.jackson.core.base,
+com.fasterxml.jackson.core.format,
+com.fasterxml.jackson.core.json,
+com.fasterxml.jackson.core.io,
+com.fasterxml.jackson.core.util,
+com.fasterxml.jackson.core.type,
+org.xml.sax,org.w3c.dom, org.w3c.dom.bootstrap, org.w3c.dom.ls,
+javax.xml.datatype, javax.xml.namespace, javax.xml.parsers
+</osgi.import>
+
+    <!-- Generate PackageVersion.java into this directory. -->
+    <packageVersion.dir>com/fasterxml/jackson/databind/cfg</packageVersion.dir>
+    <packageVersion.package>com.fasterxml.jackson.databind.cfg</packageVersion.package>
+  </properties>
+
+  <dependencies>
+    <!-- Builds on core streaming API; also needs core annotations -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+      <version>2.2.2</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>2.2.2</version>
+    </dependency>
+
+    <!-- and for testing, JUnit is needed, as well as quite a few
+         libs for which we use reflection for code, but direct dep for testing
+      -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>cglib</groupId>
+      <artifactId>cglib</artifactId>
+      <version>2.2.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.groovy</groupId>
+      <artifactId>groovy</artifactId>
+      <version>1.7.9</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency> <!--  from core we just test for repackaged cglib, not hibernate proper -->
+      <groupId>org.hibernate</groupId>
+      <artifactId>hibernate-cglib-repack</artifactId>
+      <version>2.1_3</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>${surefire.version}</version>
+        <configuration>
+          <excludes>
+            <exclude>com/fasterxml/jackson/failing/*.java</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadoc.version}</version>
+        <configuration>
+          <links>
+            <link>http://docs.oracle.com/javase/6/docs/api/</link>
+            <link>http://fasterxml.github.com/jackson-annotations/javadoc/2.1.1/</link>
+            <link>http://fasterxml.github.com/jackson-core/javadoc/2.1.1/</link>
+          </links>
+        </configuration>
+      </plugin>
+      <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>
+    </plugins>
+  </build>
+
+  <profiles>
+    <profile>
+      <id>release</id>
+      <properties>
+        <maven.test.skip>true</maven.test.skip>
+        <skipTests>true</skipTests>
+      </properties>
+    </profile>
+  </profiles>
+
+</project>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
new file mode 100644
index 0000000..6ce4100
--- /dev/null
+++ b/release-notes/CREDITS
@@ -0,0 +1,88 @@
+Here are people who have contributed to development Jackson JSON processor
+databind core component, version 2.x
+(version numbers in brackets indicate release in which the problem was fixed)
+
+(note: for older credits, check out release notes for 1.x versions)
+
+Tatu Saloranta, tatu.saloranta at iki.fi: author
+
+Pascal GŽlinas:
+  * Contributed fixes to 'MappingIterator' handling (Pull#58 and Pull#59)
+   (2.1.0)
+  * Reported #220: ContainerNode missing 'createNumber(BigInteger)'
+   (2.2.2)
+
+Joern Huxhorn:
+  * Suggested [JACKSON-636]: Add 'SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS' to allow
+    forced sorting of Maps during serialization
+   (2.0.0)
+
+James Roper:
+ * Requested [JACKSON-732]: Allow 'AnnotationIntrospector.findContentDeserializer()'
+    (and similar) to return instance, not just Class<?> for instance
+  (2.0.0)
+ * Suggested [JACKSON-800]: Adding a method for letting modules register
+    DeserializationProblemHandlers
+  (2.0.0)
+
+Casey Lucas:
+ * Reported [JACKSON-798]: Problem with external type id, creators
+  (2.0.0)
+
+Tammo van Lessen:
+ * Reported [JACKSON-811]: Problems with @JsonIdentityInfo, abstract types
+  (2.0.0)
+ * Reported [JACKSON-814]: Parsing RFC822/RFC1123 dates failes on non-US locales
+  (2.0.0)
+
+Raymond Myers:
+ * Suggested [JACKSON-810]: Deserialization Feature: Allow unknown Enum values via
+    'DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL'
+  (2.0.0)
+
+Ryan Gardner:
+ * Contributed [pull-5] -- Add support for maps with java.util.Locale keys
+    to the set of StdKeyDeserializers
+  (2.0.1)
+
+Razvan Dragut:
+ * Suggested [JACKSON-850]: Allow use of zero-arg factory methods as "default creator"
+  (2.1.0)
+
+Duncan Atkinson:
+ * Reported [JACKSON-851]: State corruption with ObjectWriter, DefaultPrettyPrinter
+  (2.1.0)
+
+Mark Wolfe:
+ * Suggested [Issue#45]: Add `@JsonNaming()` for per-class naming strategy overrides
+  (2.1.0)
+
+Dmitry Katsubo:
+ * Contributed patch for [Issue#65]: Add getters to `ObjectMapper`, DeserializationContext,
+   DeserializationFactory.
+  (2.1.0)
+
+Francis Galiegue:
+ * Reported [Issue#93] (and suggested fix): bug in `ObjectMapper.setAll(...)'
+  implementation
+  (2.1.1)
+
+kelaneren at github:
+ * Reported [Issue#157], contributed unit test: NPE when registering same module twice.
+  (2.1.4)
+
+Eric Tschetter (cheddar at github):
+  * Reported issues #166, #167, #170 (regressions from 1.9.x to 2.x)
+   (2.1.4)
+
+Thierry D (thierryd at github)
+  * Reported #214: Problem with LICENSE, NOTICE, Android packaging
+   (2.2.2)
+
+Luke G-H (lukegh at github)
+  * Reported #223: Duplicated nulls with @JsonFormat(shape=Shape.ARRAY)
+   (2.2.2)
+
+Karl Moore (karldmoore at github)
+  * Reported #217: JsonProcessingExceptions not all wrapped as expected
+   (2.2.2)
diff --git a/release-notes/VERSION b/release-notes/VERSION
new file mode 100644
index 0000000..fe1746d
--- /dev/null
+++ b/release-notes/VERSION
@@ -0,0 +1,387 @@
+Project: jackson-databind
+Version: 2.2.2 (26-May-2013)
+
+Changes:
+
+#216: Problems with Android, 1.6-only types
+#217: JsonProcessingExceptions not all wrapped as expected
+ (reported by karldmoore at github)
+#220: ContainerNode missing 'createNumber(BigInteger)'
+ (reported by Pascal G)
+#223: Duplicated nulls with @JsonFormat(shape=Shape.ARRAY)
+ (reported by lukegh at github)
+#226: Field mapping fail on deserialization to common referenced object when
+  @JsonUnwrapped is used
+ (reported by ikvia at github)
+#232: Converting bound BigDecimal value to tree fails with WRITE_BIGDECIMAL_AS_PLAIN
+ (reported by celkings at github)
+- Minor fix to handle primitive types for key deserializer lookups
+- Add convenience method `MappingIterator.getCurrentLocation()`
+ (suggested by Tomdz at github)
+
+------------------------------------------------------------------------
+=== History: ===
+------------------------------------------------------------------------
+
+2.2.1 (03-May-2013)
+
+#214: Problem with LICENSE, NOTICE, Android packaging
+ (reported by thierryd at github)
+
+2.2.0 (22-Apr-2013)
+
+Fixes:
+
+#23: Fixing typing of root-level collections
+#118: JsonTypeInfo.as.EXTERNAL_PROPERTY not working correctly
+ with missing type id, scalar types
+#130: TimeZone not set for GregorianCalendar, even if configured
+#144: MissingNode.isValueNode() should return 'false'
+ (reported by 'fge at github')
+#146: Creator properties were not being renamed as expected
+ (contributed by Christoper C)
+#188: Problem with ObjectId serialization, 'alwaysAsId' references
+
+Improvements:
+
+#116: JavaType implements `java.lang.reflect.Type` (as does `TypeReference`)
+#147: Defer reporting of problems with missing creator parameters
+ (contributed by Christoper C)
+#155: Make `ObjectNode` and `ArrayNode` final (other node types already were)
+ (requested by fge at github)
+#161: Add deserializer for java.util.concurrent.ArrayBlockingQueue
+#173: Add 'JsonNode.traverse(ObjectCodec)' for convenience
+#181: Improve error reporting for missing '_valueDeserializer'
+#194: Add `FloatNode` type in tree model (JsonNode)
+ (requested by msteiger at github)
+#199: Allow deserializing `Iterable` instances (as basic `Collection`s)
+ (requested by electrum at github)
+#206: Make 'ObjectMapper.createDeserializationContext()' overridable
+ (requested by noter at github)
+#207: Add explicit support for `short` datatypes, for tree model
+ (contributed by msteiger at github)
+
+New features:
+
+#120: Extend BeanDeserializerModifier to work with non-POJO deserializers
+#121: Extend BeanSerializerModifier to work with non-POJO serializers
+#124: Add support for serialization converters (@JsonSerializer(converter=...))
+#124: Add support for deserialization converters (@JsonDeserializer(converter=...))
+#140: Add 'SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN' to allow forcing
+  of non-scientific notation when serializing BigDecimals.
+ (suggested by phedny at github)
+#148: Add 'DeserializationFeature.FAIL_ON_INVALID_SUBTYPE`, which allows mapping
+  entries with missing or invalid type id into null references (instead of failing).
+  Also allows use of '@JsonTypeInfo.defaultImpl = NoClass.class' as alternative.
+#159: Add more accessors in 'MappingIterator': getParser(), getParserSchema(),
+  readAll()
+ (suggested by Tom D)
+#190: Add 'MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS' (default: true) for
+ pruning out final fields (to avoid using as mutators)
+ (requested by Eric T)
+#195: Add 'MapperFeature.INFER_PROPERTY_MUTATORS' (default: enabled) for finer
+  control of what mutators are auto-detected.
+ (requested by Dain S)
+#198: Add SPI metadata, handling in ObjectMapper (findModules()), for
+  automatic registration of auto-detected extension modules
+ (suggested by 'beamerblvd at github')
+#203: Added new features to support advanced date/time handling:
+  - SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS
+  - DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS
+  - DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
+
+Other:
+
+#126: Update JDK baseline to 1.6
+* API under 'com.fasterxml.jackson.databind.jsonFormatVisitors' changed significantly
+  based on experiences with external JSON Schema generator.
+* Version information accessed via code-generated access class, instead of reading
+  VERSION.txt
+* Added 2 methods in Converter interface: getInputType(), getOutputType(),
+  to allow programmatic overrides (needed by JAXB annotation module)
+
+2.1.4 (26-Feb-2013)
+
+* [JACKSON-887]: StackOverflow with parameterized sub-class field
+ (reported by Alexander M)
+* [#130]: TimeZone not set for GregorianCalendar, when deserializing
+* [#157]: NPE when registering module twice
+* [#162]: JsonNodeFactory: work around an old bug with BigDecimal and zero
+ (submitted by fge at github)
+* [#166]: Incorrect optimization for `ObjectMapper.convertValue(Class)`
+ (reported by Eric T)
+* [#167]: Problems with @JsonValue, polymorphic types (regression from 1.x)
+ (reported by Eric T)
+* [#170]: Problems deserializing `java.io.File` if creator auto-discovery disabled
+ (reported by Eric T)
+* [#175]: NPE for JsonMappingException, if no path is specified
+ (reported by bramp at github)
+
+2.1.3 (19-Jan-2013)
+
+* [Issue#141]: ACCEPT_EMPTY_STRING_AS_NULL_OBJECT not working for enums
+* [Issue#142]: Serialization of class containing EnumMap with polymorphic enum
+  fails to generate class type data
+ (reported by kidavis4 at github)
+
+2.1.2 (04-Dec-2012)
+
+* [Issue#106]: NPE in ObjectArraySerializer.createContextual(...)
+* [Issue#117]: HandlerInstantiator defaulting not working
+ (reported by Alexander B)
+* [Issue#118]: Problems with JsonTypeInfo.As.EXTERNAL_PROPERTY, scalar values
+ (reported by Adva11 at github)
+* [Issue#119]: Problems with @JsonValue, JsonTypeInfo.As.EXTERNAL_PROPERTY
+ (reported by Adva11 at github)
+* [Issue#122]: ObjectMapper.copy() was not copying underlying mix-in map
+ (reported by rzlo at github)
+
+2.1.1 (11-Nov-2012)
+
+Fixes:
+
+* [JACKSON-875]: Enum values not found if Feature.USE_ANNOTATIONS disabled
+ (reported by Laurent P)
+* [Issue#93]: ObjectNode.setAll() broken; would not add anything for
+  empty ObjectNodes.
+ (reported by Francis G)
+* Making things implement java.io.Serializable:
+  - Issues: #94, #99, #100, #102
+    (reported by Sean B)
+* [Issue#96]: Problem with JsonTypeInfo.As.EXTERNAL_PROPERTY, defaultImpl
+ (reported by Adva11 at github)
+
+2.1.0 (08-Oct-2012)
+
+  New minor version for 2.x series. Major improvements in multiple areas,
+  including:
+
+  - Dataformat auto-detection
+  - More `@JsonFormat.shape` variant to serialize Collections as
+    JSON Objects, POJOs as JSON Arrays (csv-like).
+  - Much more configuration accessible via ObjectReader, ObjectWriter
+  - New mechanism for JSON Schema generation, other uses (in future)
+
+Fixes:
+
+* [JACKSON-830]/[Issue#19]: Change OSGi bundle name to be fully-qualified
+* ]JACKSON-847]: Make @JsonIdentityInfo work with property-based creator
+* [JACKSON-851]: State corruption with ObjectWriter, DefaultPrettyPrinter
+ (reported by Duncan A)
+* [Issue#75]: Too aggressive KeySerializer caching
+* Minor fix wrt [Issue#11], coercion needed extra checks
+
+Improvements:
+
+* [JACKSON-758]: Remove 'IOException' from throws clauses of "writeValueAsString"
+  and "writeValueAsBytes" of ObjectMapper/ObjectWriter
+ (suggested by G-T Chen)
+* [JACKSON-839]: Allow "upgrade" of integer number types for
+  UntypedObjectDeserializer, even with default typing enabled.
+* [JACKSON-850]: Allow use of zero-arg factory methods as "default creator"
+  (suggested by Razvan D)
+* [Issue#9]: Implement 'required' JSON Schema attribute for bean properties
+* [Issue#20]: Add new exception type, InvalidFormatException (sub-type of
+  JsonMappingException) to indicate data format problems
+ (suggested by HolySamosa at github)
+* [Issue#30]: ObjectReader and ObjectWriter now try to pre-fetch root
+  (de)serializer if possible; minor performance improvement (2% for small POJOs).
+* [Issue#33]: Simplified/clarified definition of 'ObjectReader.readValues()';
+  minor change in behavior for JSON Array "wrapped" sequences
+* [Issue#60]: Add 'JsonNode.hasNonNull(...)' method(s)
+ (suggested by Jeff S on mailing list) 
+* [Issue#64]: Add new "standard" PropertyNamingStrategy, PascalCaseStrategy
+  (PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE)
+ (contributed by Sean B)
+* [Issue#65]: Add getters to `ObjectMapper`, DeserializationContext/-Factory.
+ (contributed by Dmitry K)
+* [Issue#69]: Add `PropertyName` abstraction, new methods in AnnotationIntrospector
+* [Issue#80]: Make `DecimalNode` normalize input, to make "1.0" and "1.00"equal
+ (reported by fge at github)
+
+New features:
+
+* [Issue#15]: Support data format auto-detection via ObjectReader (added
+  'withFormatDetection(...)' fluent factories)
+* [Issue#21]: Add 'ObjectNode.set(...)' method (and related) to improve
+  chaining, semantic consistency of Tree Model API
+ (suggested by fge at Github)
+* [Issue#22]: Add 'ObjectMapper.setAnnotationIntrospectors()' which allows
+  defining different introspectors for serialization, deserialization
+* [Issue#24]: Allow serialization of Enums as JSON Objects
+ (suggested by rveloso at github)
+* [Issue#28]: Add 'ObjectMapper.copy()', to create non-linked copy of
+  mapper, with same configuration settings
+* [Issue#29]: Allow serializing, deserializing POJOs as JSON Arrays
+  by using `@JsonFormat(shape=Shape.ARRAY)`
+* [Issue#40]: Allow serialization of Collections as JSON Objects
+  (and deserialization from)
+ (suggested by 'rveloso at github')
+* [Issue#42]: Allow specifying Base64 variant to use for Base64-encoded data
+  using ObjectReader.with(Base64Variant), ObjectWriter.with(Base64Variant).
+ (suggested by 'mpfau at github')
+* [Issue#45]: Add '@JsonNaming' annotation to define per-class PropertyNamingStrategy
+ (suggested by Mark W)
+* [Pull#58]: Make 'MappingIterator' implement 'Closable'
+ (contributed by Pascal G)
+* [Issue#72]: Add 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME' to use
+  wrapper name annotations for renaming properties
+* [Issue#87]: Add 'StdDelegatingSerializer', 'StdDelegatingDeserializer' to
+  simplify writing of two-step handlers
+* (issue #4 of jackson-annotations): Add `@JsonIdentityReference(alwaysAsId=true)`
+  to force ALL references to an object written as Object Id, even the first one.
+* Added 'ObjectReader#withHandler' to allow for reconfiguring deserialization
+  problem handler
+ (suggested by 'electricmonk')
+
+Other changes:
+
+* New variant of AnnotationIntrospector.getFormat(), to support class
+  annotations
+* It is now possible to serialize instances of plain old Object, iff
+  'FAIL_ON_EMPTY_BEANS' is disabled.
+* Trying to remove reference to "JSON" in datatype conversion errors
+ (since databinding is format-agnostic)
+
+INCOMPATIBILITIES: (rats!)
+
+* Note that [Issue#33] (see above) is, technically speaking, backwards
+  imcompatible change. It is estimated that it should NOT affect most
+  users, as changes are to edge cases (and undocumented ones at that).
+  However, it can potentially cause problems with upgrade.
+* Implementation of `JsonFormatVisitable` resulting in 2 new methods
+  being added in `BeanPropertyFilter` interface -- this is unfortunate,
+  but was required to support full traversability.
+
+2.0.4 (26-Jun-2012)
+
+* [Issue#6]: element count for PrettyPrinter, endObject wrong
+   (reported by "thebluemountain")
+* [JACKSON-838]: Utf8StreamParser._reportInvalidToken() skips letters
+    from reported token name
+   (reported by Lóránt Pintér)
+* [JACKSON-841] Data is doubled in SegmentedStringWriter output
+   (reported by Scott S)
+* [JACKSON-842] ArrayIndexOutOfBoundsException when skipping C-style comments
+   (reported by Sebastien R)
+
+2.0.3: no version 2.0.3 released -- only used for extension modules
+
+2.0.2 [14-May-2012]
+
+Fixes:
+
+* [Issue#14]: Annotations were not included from parent classes of
+  mix-in classes
+ (reported by @guillaup)
+* [JACKSON-824]: Combination of JSON Views, ObjectMapper.readerForUpdating()
+  was not working
+ (reported by Nir S)
+(and all fixes from 1.9.7)
+
+Improvements:
+
+* [Issue#11]: Improve ObjectMapper.convertValue()/.treeToValue() to use
+  cast if possible
+
+2.0.1 [23-Apr-2012]
+
+Fixes:
+
+* [JACKSON-827] Ensure core packages work on JDK 1.5
+ (reported by Pascal g)
+* [JACKSON-829] Custom serializers not working for List<String> properties,
+  @JsonSerialize(contentUsing)
+ (reported by James R)
+
+Improvements:
+
+* [Issue#5]: Add support for maps with java.util.Locale keys to the set of
+  StdKeyDeserializers
+ (contributed by Ryan G)
+
+2.0.0 [25-Mar-2012]
+
+Fixes:
+
+* [JACKSON-368]: Problems with managed references, abstract types
+* [JACKSON-711]: Delegating @JsonCreator did not work with Injectable values
+* [JACKSON-798]: Problem with external type id, creators
+  (reported by Casey L)
+(and all fixes up until and including 1.9.6)
+
+Improvements:
+
+* [JACKSON-546]: Indicate end-of-input with JsonMappingException instead
+  of EOFException, when there is no parsing exception
+* [JACKSON-664]: Reduce overhead of type resolution by adding caching
+  in TypeFactory
+* [JACKSON-690]: Pass DeserializationContext through ValueInstantiator
+* [JACKSON-695]: Add 'isEmpty(value)' in JsonSerializer to allow
+  customizing handling of serialization of empty values
+* [JACKSON-710]: 'ObjectMapper.convertValue()' should ignore root value
+  wrapping/unwrapping settings
+* [JACKSON-730] Split various features (JsonParser, JsonGenerator,
+  SerializationConfig, DeserializationConfig) into per-factory
+  features (MapperFeature, JsonFactory.Feature) an per
+  instance features (existing ones)
+* [JACKSON-732]: Allow 'AnnotationIntrospector.findContentDeserializer()'
+  (and similar) to return instance, not just Class<?> for instance
+ (requested by James R)
+* [JACKSON-736]: Add (more) access to array, container and map serializers
+* [JACKSON-737]: Allow accessing of "creator properties" for BeanDeserializer
+* [JACKSON-748]: Add 'registerSubtypes' to 'Module.setupContext' (and SimpleModule)
+* [JACKSON-749]: Make @JsonValue work for Enum deserialization
+* [JACKSON-769]: ObjectNode/ArrayNode: change 'put', 'insert', 'add' to return
+  'this node' (unless already returning something)
+* [JACKSON-770]: Simplify method naming for JsonNode, drop unnecessary 'get' prefix
+  from methods like 'getTextValue()' (becomes 'textValue()')
+* [JACKSON-777]: Rename 'SerializationConfig.Feature' as 'SerializationFeature',
+  'DeserializationConfig.Feature' as 'DeserializationFeature'
+* [JACKSON-780]: MissingNode, NullNode should return 'defaultValue' from 'asXxx' methods,
+  (not 0 for numbers), as they are not numeric types
+* [JACKSON-787]: Allow use of @JsonIgnoreProperties for properties (fields, getters, setters)
+* [JACKSON-795]: @JsonValue was not working for Maps, Collections
+* [JACKSON-800]: Add 'Module.SetupContext#addDeserializationProblemHandler'
+ (suggested by James R)
+
+New features:
+
+* [JACKSON-107]: Add support for Object Identity (to handled cycles, shared refs),
+  with @JsonIdentityInfo
+* [JACKSON-435]: Allow per-property Date formatting using @JsonFormat.
+* [JACKSON-437]: Allow injecting of type id as POJO property, by setting
+  new '@JsonTypeInfo.visible' property to true.
+* [JACKSON-469]: Support "Builder pattern" for deserialiation; that is, allow
+  use of separate Builder object for data binding, creating actual value
+* [JACKSON-608]: Allow use of JSON Views for deserialization
+* [JACKSON-636]: Add 'SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS' to allow
+  forced sorting of Maps during serialization
+  (suggested by Joern H)
+* [JACKSON-669]: Allow prefix/suffix for @JsonUnwrapped properties
+ (requested by Aner P)
+* [JACKSON-707]: Add 'JsonNode.deepCopy()', to create safe deep copies
+  of ObjectNodes, ArrayNodes.
+* [JACKSON-714]: Add general-purpose @JsonFormat annotation
+* [JACKSON-718]: Added 'JsonNode.canConvertToInt()', 'JsonNode.canConvertToLong()'
+* [JACKSON-747]: Allow changing of 'SerializationFeature' for ObjectWriter,
+  'DeserializationFeature' for ObjectReader.
+* [JACKSON-752]: Add @JsonInclude (replacement of @JsonSerialize.include)
+* [JACKSON-754]: Add @JacksonAnnotationsInside for creating "annotation
+  bundles" (also: AnnotationIntrospector.isAnnotationBundle())
+* [JACKSON-762]: Allow using @JsonTypeId to specify property to use as
+  type id, instead of using separate type id resolver.
+* [JACKSON-764]: Allow specifying "root name" to use for root wrapping
+  via ObjectReader, ObjectWriter.
+* [JACKSON-772]: Add 'JsonNode.withArray()' to use for traversing Array nodes.
+* [JACKSON-793]: Add support for configurable Locale, TimeZone to use
+  (via SerializationConfig, DeserializationConfig)
+* [JACKSON-805]: Add 'SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED'
+  to improve interoperability with BadgerFish/Jettison
+* [JACKSON-810]: Deserialization Feature: Allow unknown Enum values via
+  'DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL'
+  (suggested by Raymond R)
+* [JACKSON-813]: Add '@JsonSerializableSchema.id' attribute, to indicate
+  'id' value to add to generated JSON Schemas.
+
+[entries for versions 1.x and earlier not retained; refer to earlier releases)
diff --git a/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java
new file mode 100644
index 0000000..e60c474
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/AbstractTypeResolver.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.databind;
+
+
+/**
+ * Defines interface for resolvers that can resolve abstract types into concrete
+ * ones; either by using static mappings, or possibly by materializing
+ * implementations dynamically.
+ */
+public abstract class AbstractTypeResolver
+{
+    /**
+     * Try to locate a subtype for given abstract type, to either resolve
+     * to a concrete type, or at least to a more-specific (and hopefully supported)
+     * abstract type, one which may have registered deserializers.
+     * Method is called before trying to locate registered deserializers
+     * (as well as standard abstract type defaulting that core Jackson does),
+     * so it is typically implemented to add custom mappings of common abstract
+     * types (like specify which concrete implementation to use for binding
+     * {@link java.util.List}s).
+     *<p>
+     * Note that this method does not necessarily have to do full resolution
+     * of bindings; that is, it is legal to return type that could be further
+     * resolved: caller is expected to keep calling this method on registered
+     * resolvers, until a concrete type is located.
+     * 
+     * @param config Configuration in use; should always be of type
+     *    <code>DeserializationConfig</code>
+     */
+    public JavaType findTypeMapping(DeserializationConfig config, JavaType type) {
+        return null;
+    }
+    
+    /**
+     * Method called to try to resolve an abstract type into
+     * concrete type (usually for purposes of deserializing),
+     * when no concrete implementation was found.
+     * It will be called after checking all other possibilities,
+     * including defaulting.
+     * 
+     * @param config Configuration in use; should always be of type
+     *    <code>DeserializationConfig</code>
+     * @param type Type for which materialization maybe needed
+     * 
+     * @return Resolved concrete type (which should retain generic
+     *    type parameters of input type, if any), if resolution succeeds;
+     *    null if resolver does not know how to resolve type
+     */
+    public JavaType resolveAbstractType(DeserializationConfig config,
+            JavaType type) {
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
new file mode 100644
index 0000000..5675a5d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/AnnotationIntrospector.java
@@ -0,0 +1,1135 @@
+package com.fasterxml.jackson.databind;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+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;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.util.Converter;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Abstract class that defines API used for introspecting annotation-based
+ * configuration for serialization and deserialization. Separated
+ * so that different sets of annotations can be supported, and support
+ * plugged-in dynamically.
+ *<p>
+ * NOTE: due to rapid addition of new methods (and changes to existing methods),
+ * it is <b>strongly</b> recommended that custom implementations should not directly
+ * extend this class, but rather extend {@link NopAnnotationIntrospector}.
+ * This way added methods will not break backwards compatibility of custom annotation
+ * introspectors.
+ */
+ at SuppressWarnings("serial")
+public abstract class AnnotationIntrospector
+    implements Versioned, java.io.Serializable
+{    
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Value type used with managed and back references; contains type and
+     * logic name, used to link related references
+     */
+    public static class ReferenceProperty
+    {
+        public enum Type {
+            /**
+             * Reference property that Jackson manages and that is serialized normally (by serializing
+             * reference object), but is used for resolving back references during
+             * deserialization.
+             * Usually this can be defined by using
+             * {@link com.fasterxml.jackson.annotation.JsonManagedReference}
+             */
+            MANAGED_REFERENCE
+    
+            /**
+             * Reference property that Jackson manages by suppressing it during serialization,
+             * and reconstructing during deserialization.
+             * Usually this can be defined by using
+             * {@link com.fasterxml.jackson.annotation.JsonBackReference}
+             */
+            ,BACK_REFERENCE
+            ;
+        }
+
+        private final Type _type;
+        private final String _name;
+
+        public ReferenceProperty(Type t, String n) {
+            _type = t;
+            _name = n;
+        }
+
+        public static ReferenceProperty managed(String name) { return new ReferenceProperty(Type.MANAGED_REFERENCE, name); }
+        public static ReferenceProperty back(String name) { return new ReferenceProperty(Type.BACK_REFERENCE, name); }
+        
+        public Type getType() { return _type; }
+        public String getName() { return _name; }
+
+        public boolean isManagedReference() { return _type == Type.MANAGED_REFERENCE; }
+        public boolean isBackReference() { return _type == Type.BACK_REFERENCE; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Factory method for accessing "no operation" implementation
+     * of introspector: instance that will never find any annotation-based
+     * configuration.
+     */
+    public static AnnotationIntrospector nopInstance() {
+        return NopAnnotationIntrospector.instance;
+    }
+
+    public static AnnotationIntrospector pair(AnnotationIntrospector a1, AnnotationIntrospector a2) {
+        return new AnnotationIntrospectorPair(a1, a2);
+    }
+
+    /*
+    /**********************************************************
+    /* Access to possibly chained introspectors (1.7)
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to collect all "real" introspectors that
+     * this introspector contains, if any; or this introspector
+     * if it is not a container. Used to get access to all container
+     * introspectors in their priority order.
+     *<p>
+     * Default implementation returns a Singleton list with this introspector
+     * as contents.
+     * This usually works for sub-classes, except for proxy or delegating "container
+     * introspectors" which need to override implementation.
+     */
+    public Collection<AnnotationIntrospector> allIntrospectors() {
+        return Collections.singletonList(this);
+    }
+    
+    /**
+     * Method that can be used to collect all "real" introspectors that
+     * this introspector contains, if any; or this introspector
+     * if it is not a container. Used to get access to all container
+     * introspectors in their priority order.
+     *<p>
+     * Default implementation adds this introspector in result; this usually
+     * works for sub-classes, except for proxy or delegating "container
+     * introspectors" which need to override implementation.
+     */
+    public Collection<AnnotationIntrospector> allIntrospectors(Collection<AnnotationIntrospector> result) {
+        result.add(this);
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Default Versioned impl
+    /**********************************************************
+     */
+
+    @Override
+    public abstract Version version();
+    
+    /*
+    /**********************************************************
+    /* Meta-annotations (annotations for annotation types)
+    /**********************************************************
+     */
+    
+    /**
+     * Method called by framework to determine whether given annotation
+     * is handled by this introspector.
+     *
+     * @deprecated Not used since 2.0; deprecated since 2.1
+     */
+    @Deprecated
+    public boolean isHandled(Annotation ann) {
+        return false;
+    }
+
+    /**
+     * Method for checking whether given annotation is considered an
+     * annotation bundle: if so, all meta-annotations it has will
+     * be used instead of annotation ("bundle") itself.
+     * 
+     * @since 2.0
+     */
+    public boolean isAnnotationBundle(Annotation ann) {
+        return false;
+    }
+
+    /*
+    /**********************************************************
+    /* General annotations (for classes, properties)
+    /**********************************************************
+     */
+    
+    /**
+     * Method for checking whether given annotated thing
+     * (type, or accessor) indicates that values
+     * referenced (values of type of annotated class, or
+     * values referenced by annotated property; latter
+     * having precedence) should include Object Identifier,
+     * and if so, specify details of Object Identity used.
+     * 
+     * @since 2.0
+     */
+    public ObjectIdInfo findObjectIdInfo(Annotated ann) {
+        return null;
+    }
+
+    /**
+     * Method for figuring out additional properties of an Object Identity reference
+     * 
+     * @since 2.1
+     */
+    public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
+        return objectIdInfo;
+    }
+    
+    /*
+    /**********************************************************
+    /* General class annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for locating name used as "root name" (for use by
+     * some serializers when outputting root-level object -- mostly
+     * for XML compatibility purposes) for given class, if one
+     * is defined. Returns null if no declaration found; can return
+     * explicit empty String, which is usually ignored as well as null.
+     *<p> 
+     * NOTE: method signature changed in 2.1, to return {@link PropertyName}
+     * instead of String.
+     */
+    public PropertyName findRootName(AnnotatedClass ac) {
+        return null;
+    }
+
+    /**
+     * Method for finding list of properties to ignore for given class
+     * (null is returned if not specified).
+     * List of property names is applied
+     * after other detection mechanisms, to filter out these specific
+     * properties from being serialized and deserialized.
+     */
+    public String[] findPropertiesToIgnore(Annotated ac) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether an annotation indicates that all unknown properties
+     */
+    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.
+     * 
+     * @param ac Type to check
+     * 
+     * @return Boolean.TRUE if properties of type should be ignored;
+     *   Boolean.FALSE if they are not to be ignored, null for default
+     *   handling (which is 'do not ignore')
+     */
+    public Boolean isIgnorableType(AnnotatedClass ac) {
+        return null;
+    }
+
+    /**
+     * Method for finding if annotated class has associated filter; and if so,
+     * to return id that is used to locate filter.
+     * 
+     * @return Id of the filter to use for filtering properties of annotated
+     *    class, if any; or null if none found.
+     */
+    public Object findFilterId(AnnotatedClass ac) {
+        return null;
+    }
+
+    /**
+     * Method for finding {@link PropertyNamingStrategy} for given
+     * class, if any specified by annotations; and if so, either return
+     * a {@link PropertyNamingStrategy} instance, or Class to use for
+     * creating instance
+     * 
+     * @return Sub-class or instance of {@link PropertyNamingStrategy}, if one
+     *   is specified for given class; null if not.
+     * 
+     * @since 2.1
+     */
+    public Object findNamingStrategy(AnnotatedClass ac) {
+        return null;
+    }    
+
+    /*
+    /**********************************************************
+    /* Property auto-detection
+    /**********************************************************
+     */
+
+    /**
+     * Method for checking if annotations indicate changes to minimum visibility levels
+     * needed for auto-detecting property elements (fields, methods, constructors).
+     * A baseline checker is given, and introspector is to either return it as is
+     * (if no annotations are found), or build and return a derived instance (using
+     * checker's build methods).
+     */
+    public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
+            VisibilityChecker<?> checker) {
+        return checker;
+    }
+    
+    /*
+    /**********************************************************
+    /* Class annotations for Polymorphic type handling (1.5+)
+    /**********************************************************
+    */
+    
+    /**
+     * Method for checking if given class has annotations that indicate
+     * that specific type resolver is to be used for handling instances.
+     * This includes not only
+     * 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
+     * 
+     * @return Type resolver builder for given type, if one found; null if none
+     */
+    public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
+            AnnotatedClass ac, JavaType baseType) {
+        return null;
+    }
+
+    /**
+     * Method for checking if given property entity (field or method) has annotations
+     * that indicate that specific type resolver is to be used for handling instances.
+     * This includes not only
+     * 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 am Annotated member (field or method) to check for annotations
+     * @param baseType Base java type of property for which resolver is to be found
+     * 
+     * @return Type resolver builder for properties of given entity, if one found;
+     *    null if none
+     */
+    public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType baseType) {
+        return null;
+    }
+
+    /**
+     * Method for checking if given structured property entity (field or method that
+     * has nominal value of Map, Collection or array type) has annotations
+     * that indicate that specific type resolver is to be used for handling type
+     * information of contained values.
+     * This includes not only
+     * 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 am Annotated member (field or method) to check for annotations
+     * @param containerType Type of property for which resolver is to be found (must be a container type)
+     * 
+     * @return Type resolver builder for values contained in properties of given entity,
+     *    if one found; null if none
+     */    
+    public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType containerType) {
+        return null;
+    }
+
+    /**
+     * Method for locating annotation-specified subtypes related to annotated
+     * entity (class, method, field). Note that this is only guaranteed to be
+     * a list of directly
+     * declared subtypes, no recursive processing is guarantees (i.e. caller
+     * has to do it if/as necessary)
+     * 
+     * @param a Annotated entity (class, field/method) to check for annotations
+     */
+    public List<NamedType> findSubtypes(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method for checking if specified type has explicit name.
+     * 
+     * @param ac Class to check for type name annotations
+     */
+    public String findTypeName(AnnotatedClass ac) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* General member (field, method/constructor) annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for checking if given member indicates that it is part
+     * of a reference (parent/child).
+     */
+    public ReferenceProperty findReferenceType(AnnotatedMember member) {
+        return null;
+    }
+
+    /**
+     * Method called to check whether given property is marked to be "unwrapped"
+     * when being serialized (and appropriately handled in reverse direction,
+     * i.e. expect unwrapped representation during deserialization).
+     * Return value is the name transformation to use, if wrapping/unwrapping
+     * should  be done, or null if not -- note that transformation may simply
+     * be identity transformation (no changes).
+     */
+    public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member) {
+        return null;
+    }
+
+    /**
+     * Method called to check whether given property is marked to
+     * be ignored. This is used to determine whether to ignore
+     * properties, on per-property basis, usually combining
+     * annotations from multiple accessors (getters, setters, fields,
+     * constructor parameters).
+     */
+    public boolean hasIgnoreMarker(AnnotatedMember m) {
+        return false;
+    }
+
+    /**
+     * Method called to find out whether given member expectes a value
+     * to be injected, and if so, what is the identifier of the value
+     * to use during injection.
+     * Type if identifier needs to be compatible with provider of
+     * values (of type {@link InjectableValues}); often a simple String
+     * id is used.
+     * 
+     * @param m Member to check
+     * 
+     * @return Identifier of value to inject, if any; null if no injection
+     *   indicator is found
+     */
+    public Object findInjectableValueId(AnnotatedMember m) {
+        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.
+     * If null is returned, no view definitions exist and property is always
+     * included (or always excluded as per default view inclusion configuration);
+     * 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)
+     * 
+     * @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;
+     *    if null, always included (same as returning array containing <code>Object.class</code>)
+     */
+    public Class<?>[] findViews(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method for finding format annotations for given member.
+     * Return value is typically used by serializers and/or
+     * deserializers to customize presentation aspects of the
+     * serialized value.
+     * 
+     * @since 2.0
+     * 
+     * @deprecated Since 2.1, use {@link #findFormat(Annotated)} instead.
+     */
+    @Deprecated
+    public JsonFormat.Value findFormat(AnnotatedMember member) {
+        return null;
+    }
+
+    /**
+     * Method for finding format annotations for property or class.
+     * Return value is typically used by serializers and/or
+     * deserializers to customize presentation aspects of the
+     * serialized value.
+     * 
+     * @since 2.1
+     */
+    public JsonFormat.Value findFormat(Annotated memberOrClass) {
+        if (memberOrClass instanceof AnnotatedMember) {
+            return findFormat((AnnotatedMember) memberOrClass);
+        }
+        return null;
+    }
+    
+    /**
+     * Method for checking whether given accessor claims to represent
+     * type id: if so, its value may be used as an override,
+     * instead of generated type id.
+     * 
+     * @since 2.0
+     */
+    public Boolean isTypeId(AnnotatedMember member) {
+        return null;
+    }
+
+
+    /**
+     * Method used to check if specified property has annotation that indicates
+     * that it should be wrapped in an element; and if so, name to use.
+     * Note that not all serializers and deserializers support use this method:
+     * currently (2.1) it is only used by XML-backed handlers.
+     * 
+     * @return Wrapper name to use, if any, or {@link PropertyName#USE_DEFAULT}
+     *   to indicate that no wrapper element should be used.
+     * 
+     * @since 2.1
+     */
+    public PropertyName findWrapperName(Annotated ann) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization: general annotations
+    /**********************************************************
+     */
+    
+    /**
+     * Method for getting a serializer definition on specified method
+     * or field. Type of definition is either instance (of type
+     * {@link JsonSerializer}) or Class (of type
+     * <code>Class<JsonSerializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findSerializer(Annotated am) {
+        return null;
+    }
+
+    /**
+     * Method for getting a serializer definition for keys of associated <code>Map</code> property.
+     * Type of definition is either instance (of type
+     * {@link JsonSerializer}) or Class (of type
+     * <code>Class<JsonSerializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findKeySerializer(Annotated am) {
+        return null;
+    }
+
+    /**
+     * Method for getting a serializer definition for content (values) of
+     * associated <code>Collection</code>, <code>array</code> or <code>Map</code> property.
+     * Type of definition is either instance (of type
+     * {@link JsonSerializer}) or Class (of type
+     * <code>Class<JsonSerializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findContentSerializer(Annotated am) {
+        return null;
+    }
+    
+    /**
+     * Method for checking whether given annotated entity (class, method,
+     * field) defines which Bean/Map properties are to be included in
+     * serialization.
+     * If no annotation is found, method should return given second
+     * argument; otherwise value indicated by the annotation
+     *
+     * @return Enumerated value indicating which properties to include
+     *   in serialization
+     */
+    public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue) {
+        return defValue;
+    }
+
+    /**
+     * 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.
+     *
+     * @return Class to use instead of runtime type
+     */
+    public Class<?> findSerializationType(Annotated a) {
+        return null;
+    }
+
+    /**
+     * 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
+     */
+    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType) {
+        return null;
+    }
+
+    /**
+     * 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
+     */
+    public Class<?> findSerializationContentType(Annotated am, JavaType baseType) {
+        return null;
+    }
+    
+    /**
+     * Method for accessing declared typing mode annotated (if any).
+     * This is used for type detection, unless more granular settings
+     * (such as actual exact type; or serializer to use which means
+     * no type information is needed) take precedence.
+     *
+     * @return Typing mode to use, if annotation is found; null otherwise
+     */
+    public JsonSerialize.Typing findSerializationTyping(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method for finding {@link Converter} that annotated entity
+     * (property or class) has indicated to be used as part of
+     * serialization. If not null, either has to be actual
+     * {@link Converter} instance, or class for such converter;
+     * and resulting converter will be used first to convert property
+     * value to converter target type, and then serializer for that
+     * type is used for actual serialization.
+     *<p>
+     * This feature is typically used to convert internal values into types
+     * that Jackson can convert.
+     *<p>
+     * Note also that this feature does not necessarily work well with polymorphic
+     * type handling, or object identity handling; if such features are needed
+     * an explicit serializer is usually better way to handle serialization.
+     * 
+     * @param a Annotated property (field, method) or class to check for
+     *   annotations
+     *   
+     * @since 2.2
+     */
+    public Object findSerializationConverter(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method for finding {@link Converter} that annotated property
+     * has indicated needs to be used for values of container type
+     * (this also means that method should only be called for properties
+     * of container types, List/Map/array properties).
+     *<p>
+     * If not null, either has to be actual
+     * {@link Converter} instance, or class for such converter;
+     * and resulting converter will be used first to convert property
+     * value to converter target type, and then serializer for that
+     * type is used for actual serialization.
+     *<p>
+     * Other notes are same as those for {@link #findSerializationConverter}
+     * 
+     * @param a Annotated property (field, method) to check.
+     *   
+     * @since 2.2
+     */
+    public Object findSerializationContentConverter(AnnotatedMember a) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization: class annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing defined property serialization order (which may be
+     * partial). May return null if no ordering is defined.
+     */
+    public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether an annotation indicates that serialized properties
+     * for which no explicit is defined should be alphabetically (lexicograpically)
+     * ordered
+     */
+    public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization: property annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for checking whether given property accessors (method,
+     * field) has an annotation that suggests property name to use
+     * for serialization.
+     * Should return null if no annotation
+     * is found; otherwise a non-null name (possibly
+     * {@link PropertyName#USE_DEFAULT}, which means "use default heuristics").
+     * 
+     * @param a Property accessor to check
+     * 
+     * @return Name to use if found; null if not.
+     * 
+     * @since 2.1
+     */
+    public PropertyName findNameForSerialization(Annotated a)
+    {
+        // [Issue#69], need bit of delegation 
+        // !!! TODO: in 2.2, remove old methods?
+        String name;
+        if (a instanceof AnnotatedField) {
+            name = findSerializationName((AnnotatedField) a);
+        } else if (a instanceof AnnotatedMethod) {
+            name = findSerializationName((AnnotatedMethod) a);
+        } else {
+            name = null;
+        }
+        if (name != null) {
+            if (name.length() == 0) { // empty String means 'default'
+                return PropertyName.USE_DEFAULT;
+            }
+            return new PropertyName(name);
+        }
+        return null;
+    }
+    
+    /**
+     * Method for checking whether given method has an annotation
+     * that suggests property name associated with method that
+     * may be a "getter". Should return null if no annotation
+     * is found; otherwise a non-null String.
+     * If non-null value is returned, it is used as the property
+     * name, except for empty String ("") which is taken to mean
+     * "use standard bean name detection if applicable;
+     * method name if not".
+     * 
+     * @deprecated Since 2.1 should use {@link #findNameForSerialization} instead
+     */
+    @Deprecated
+    public String findSerializationName(AnnotatedMethod am) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether given member field represent
+     * a serializable logical property; and if so, returns the
+     * name of that property.
+     * Should return null if no annotation is found (indicating it
+     * is not a serializable field); otherwise a non-null String.
+     * If non-null value is returned, it is used as the property
+     * name, except for empty String ("") which is taken to mean
+     * "use the field name as is".
+     * 
+     * @deprecated Since 2.1 should use {@link #findNameForSerialization} instead
+     */
+    @Deprecated
+    public String findSerializationName(AnnotatedField af) {
+        return null;
+    }
+    
+    /**
+     * Method for checking whether given method has an annotation
+     * that suggests that the return value of annotated method
+     * 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
+     */
+    public boolean hasAsValueAnnotation(AnnotatedMethod am) {
+        return false;
+    }
+    
+    /**
+     * Method for determining the String value to use for serializing
+     * given enumeration entry; used when serializing enumerations
+     * as Strings (the standard method).
+     *
+     * @return Serialized enum value.
+     */
+    public String findEnumValue(Enum<?> value) {
+        // as per [JACKSON-875], should use default here
+        return value.name();
+    }
+    /*
+    /**********************************************************
+    /* Deserialization: general annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for getting a deserializer definition on specified method
+     * or field.
+     * Type of definition is either instance (of type
+     * {@link JsonDeserializer}) or Class (of type
+     * <code>Class<JsonDeserializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findDeserializer(Annotated am) {
+        return null;
+    }
+
+    /**
+     * Method for getting a deserializer definition for keys of
+     * associated <code>Map</code> property.
+     * Type of definition is either instance (of type
+     * {@link JsonDeserializer}) or Class (of type
+     * <code>Class<JsonDeserializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findKeyDeserializer(Annotated am) {
+        return null;
+    }
+
+    /**
+     * Method for getting a deserializer definition for content (values) of
+     * associated <code>Collection</code>, <code>array</code> or
+     * <code>Map</code> property.
+     * Type of definition is either instance (of type
+     * {@link JsonDeserializer}) or Class (of type
+     * <code>Class<JsonDeserializer></code>); if value of different
+     * type is returned, a runtime exception may be thrown by caller.
+     */
+    public Object findContentDeserializer(Annotated am) {
+        return null;
+    }
+
+    /**
+     * Method for accessing annotated type definition that a
+     * method can have, to be used as the type for serialization
+     * instead of the runtime type.
+     * Type must be a narrowing conversion
+     * (i.e.subtype of declared type).
+     * Declared return type of the method is also considered acceptable.
+     *
+     * @param baseType Assumed type before considering annotations
+     *
+     * @return Class to use for deserialization instead of declared type
+     */
+    public Class<?> findDeserializationType(Annotated am, JavaType baseType) {
+        return null;
+    }
+
+    /**
+     * Method for accessing additional narrowing type definition that a
+     * method can have, to define more specific key type to use.
+     * It should be only be used with {@link java.util.Map} types.
+     * 
+     * @param baseKeyType Assumed key type before considering annotations
+     *
+     * @return Class specifying more specific type to use instead of
+     *   declared type, if annotation found; null if not
+     */
+    public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType) {
+        return null;
+    }
+
+    /**
+     * Method for accessing additional narrowing type definition that a
+     * method can have, to define more specific content type to use;
+     * content refers to Map values and Collection/array elements.
+     * It should be only be used with Map, Collection and array types.
+     * 
+     * @param baseContentType Assumed content (value) type before considering annotations
+     *
+     * @return Class specifying more specific type to use instead of
+     *   declared type, if annotation found; null if not
+     */
+    public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType) {
+        return null;
+    }
+
+    /**
+     * Method for finding {@link Converter} that annotated entity
+     * (property or class) has indicated to be used as part of
+     * deserialization.
+     * If not null, either has to be actual
+     * {@link Converter} instance, or class for such converter;
+     * and resulting converter will be used after Jackson has deserializer
+     * data into intermediate type (Converter input type), and Converter
+     * needs to convert this into its target type to be set as property value.
+     *<p>
+     * This feature is typically used to convert intermediate Jackson types
+     * (that default deserializers can produce) into custom type instances.
+     *<p>
+     * Note also that this feature does not necessarily work well with polymorphic
+     * type handling, or object identity handling; if such features are needed
+     * an explicit deserializer is usually better way to handle deserialization.
+     * 
+     * @param a Annotated property (field, method) or class to check for
+     *   annotations
+     *   
+     * @since 2.2
+     */
+    public Object findDeserializationConverter(Annotated a) {
+        return null;
+    }
+
+    /**
+     * Method for finding {@link Converter} that annotated property
+     * has indicated needs to be used for values of container type
+     * (this also means that method should only be called for properties
+     * of container types, List/Map/array properties).
+     *<p>
+     * If not null, either has to be actual
+     * {@link Converter} instance, or class for such converter;
+     * and resulting converter will be used after Jackson has deserializer
+     * data into intermediate type (Converter input type), and Converter
+     * needs to convert this into its target type to be set as property value.
+     *<p>
+     * Other notes are same as those for {@link #findDeserializationConverter}
+     * 
+     * @param a Annotated property (field, method) to check.
+     *   
+     * @since 2.2
+     */
+    public Object findDeserializationContentConverter(AnnotatedMember a) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization: class annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method getting {@link ValueInstantiator} to use for given
+     * type (class): return value can either be an instance of
+     * instantiator, or class of instantiator to create.
+     */
+    public Object findValueInstantiator(AnnotatedClass ac) {
+        return null;
+    }
+
+    /**
+     * Method for finding Builder object to use for constructing
+     * value instance and binding data (sort of combining value
+     * instantiators that can construct, and deserializers
+     * that can bind data).
+     *<p>
+     * Note that unlike accessors for some helper Objects, this
+     * method does not allow returning instances: the reason is
+     * that builders have state, and a separate instance needs
+     * to be created for each deserialization call.
+     * 
+     * @since 2.0
+     */
+    public Class<?> findPOJOBuilder(AnnotatedClass ac) {
+    	return null;
+    }
+
+    /**
+     * @since 2.0
+     */
+    public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization: property annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method for checking whether given property accessors (method,
+     * field) has an annotation that suggests property name to use
+     * for deserialization (reading JSON into POJOs).
+     * Should return null if no annotation
+     * is found; otherwise a non-null name (possibly
+     * {@link PropertyName#USE_DEFAULT}, which means "use default heuristics").
+     * 
+     * @param a Property accessor to check
+     * 
+     * @return Name to use if found; null if not.
+     * 
+     * @since 2.1
+     */
+    public PropertyName findNameForDeserialization(Annotated a)
+    {
+        // [Issue#69], need bit of delegation 
+        // !!! TODO: in 2.2, remove old methods?
+        String name;
+        if (a instanceof AnnotatedField) {
+            name = findDeserializationName((AnnotatedField) a);
+        } else if (a instanceof AnnotatedMethod) {
+            name = findDeserializationName((AnnotatedMethod) a);
+        } else if (a instanceof AnnotatedParameter) {
+            name = findDeserializationName((AnnotatedParameter) a);
+        } else {
+            name = null;
+        }
+        if (name != null) {
+            if (name.length() == 0) { // empty String means 'default'
+                return PropertyName.USE_DEFAULT;
+            }
+            return new PropertyName(name);
+        }
+        return null;
+    }
+    
+    /**
+     * Method for checking whether given method has an annotation
+     * that suggests property name associated with method that
+     * may be a "setter". Should return null if no annotation
+     * is found; otherwise a non-null String.
+     * If non-null value is returned, it is used as the property
+     * name, except for empty String ("") which is taken to mean
+     * "use standard bean name detection if applicable;
+     * method name if not".
+     * 
+     * @deprecated Since 2.1 should use {@link #findNameForDeserialization} instead
+     */
+    @Deprecated
+    public String findDeserializationName(AnnotatedMethod am) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether given member field represent
+     * a deserializable logical property; and if so, returns the
+     * name of that property.
+     * Should return null if no annotation is found (indicating it
+     * is not a deserializable field); otherwise a non-null String.
+     * If non-null value is returned, it is used as the property
+     * name, except for empty String ("") which is taken to mean
+     * "use the field name as is".
+     * 
+     * @deprecated Since 2.1 should use {@link #findNameForDeserialization} instead
+     */
+    @Deprecated
+    public String findDeserializationName(AnnotatedField af) {
+        return null;
+    }
+
+    /**
+     * Method for checking whether given set of annotations indicates
+     * property name for associated parameter.
+     * No actual parameter object can be passed since JDK offers no
+     * representation; just annotations.
+     * 
+     * @deprecated Since 2.1 should use {@link #findNameForDeserialization} instead
+     */
+    @Deprecated
+    public String findDeserializationName(AnnotatedParameter param) {
+        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 setting values of any properties for
+     * which no dedicated setter method is found.
+     *
+     * @return True if such annotation is found (and is not disabled),
+     *   false otherwise
+     */
+    public boolean hasAnySetterAnnotation(AnnotatedMethod am) {
+        return false;
+    }
+
+    /**
+     * 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
+     */
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am) {
+        return false;
+    }
+    
+    /**
+     * Method for checking whether given annotated item (method, constructor)
+     * has an annotation
+     * that suggests that the method is a "creator" (aka factory)
+     * method to be used for construct new instances of deserialized
+     * values.
+     *
+     * @return True if such annotation is found (and is not disabled),
+     *   false otherwise
+     */
+    public boolean hasCreatorAnnotation(Annotated a) {
+        return false;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Old version of {@link AnnotationIntrospectorPair}.
+     * 
+     * @deprecated Starting with 2.1, use {@link AnnotationIntrospectorPair} instead.
+     */
+    @Deprecated
+    public static class Pair
+        extends AnnotationIntrospectorPair
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Deprecated
+        public Pair(AnnotationIntrospector p, AnnotationIntrospector s) {
+            super(p, s);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
new file mode 100644
index 0000000..c5d85c9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanDescription.java
@@ -0,0 +1,249 @@
+package com.fasterxml.jackson.databind;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Basic container for information gathered by {@link ClassIntrospector} to
+ * help in constructing serializers and deserializers.
+ * Note that the main implementation type is
+ * {@link com.fasterxml.jackson.databind.introspect.BasicBeanDescription},
+ * meaning that it is safe to upcast to this type.
+ */
+public abstract class BeanDescription
+{
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Bean type information, including raw class and possible
+     * * generics information
+     */
+    protected final JavaType _type;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected BeanDescription(JavaType type)
+    {
+    	_type = type;
+    }
+
+    /*
+    /**********************************************************
+    /* Simple accesors
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing declared type of bean being introspected,
+     * including full generic type information (from declaration)
+     */
+    public JavaType getType() { return _type; }
+
+    public Class<?> getBeanClass() { return _type.getRawClass(); }
+
+    /**
+     * Method for accessing low-level information about Class this
+     * item describes.
+     */
+    public abstract AnnotatedClass getClassInfo();
+
+    /**
+     * Accessor for getting information about Object Id expected to
+     * be used for this POJO type, if any.
+     */
+    public abstract ObjectIdInfo getObjectIdInfo();
+    
+    /**
+     * Method for checking whether class being described has any
+     * annotations recognized by registered annotation introspector.
+     */
+    public abstract boolean hasKnownClassAnnotations();
+
+    /**
+     * Accessor for type bindings that may be needed to fully resolve
+     * types of member object, such as return and argument types of
+     * methods and constructors, and types of fields.
+     */
+    public abstract TypeBindings bindingsForBeanType();
+
+    /**
+     * Method for resolving given JDK type, using this bean as the
+     * generic type resolution context.
+     */
+    public abstract JavaType resolveType(java.lang.reflect.Type jdkType);
+    
+    /**
+     * Method for accessing collection of annotations the bean
+     * class has.
+     */
+    public abstract Annotations getClassAnnotations();
+   
+    /*
+    /**********************************************************
+    /* Basic API for finding properties
+    /**********************************************************
+     */
+    
+    /**
+     * @return Ordered Map with logical property name as key, and
+     *    matching getter method as value.
+     */
+    public abstract List<BeanPropertyDefinition> findProperties();
+    
+    /**
+     * Method for locating all back-reference properties (setters, fields) bean has
+     */
+    public abstract Map<String,AnnotatedMember> findBackReferenceProperties();
+
+    public abstract Set<String> getIgnoredPropertyNames();
+    
+    /*
+    /**********************************************************
+    /* Basic API for finding creator members
+    /**********************************************************
+     */
+    
+    public abstract List<AnnotatedConstructor> getConstructors();
+    
+    public abstract List<AnnotatedMethod> getFactoryMethods();
+    
+    /**
+     * Method that will locate the no-arg constructor for this class,
+     * if it has one, and that constructor has not been marked as
+     * ignorable.
+     */
+    public abstract AnnotatedConstructor findDefaultConstructor();
+
+    /**
+     * Method that can be called to locate a single-arg constructor that
+     * takes specified exact type (will not accept supertype constructors)
+     *
+     * @param argTypes Type(s) of the argument that we are looking for
+     */
+    public abstract Constructor<?> findSingleArgConstructor(Class<?>... argTypes);
+
+    /**
+     * Method that can be called to find if introspected class declares
+     * a static "valueOf" factory method that returns an instance of
+     * introspected type, given one of acceptable types.
+     *
+     * @param expArgTypes Types that the matching single argument factory
+     *   method can take: will also accept super types of these types
+     *   (ie. arg just has to be assignable from expArgType)
+     */
+    public abstract Method findFactoryMethod(Class<?>... expArgTypes);
+
+    /*
+    /**********************************************************
+    /* 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 for locating the getter method that is annotated with
+     * {@link com.fasterxml.jackson.annotation.JsonValue} annotation,
+     * if any. If multiple ones are found,
+     * an error is reported by throwing {@link IllegalArgumentException}
+     */
+    public abstract AnnotatedMethod findJsonValueMethod();
+
+    public abstract AnnotatedMethod findMethod(String name, Class<?>[] paramTypes);
+    
+    /*
+    /**********************************************************
+    /* Basic API, class configuration
+    /**********************************************************
+     */
+
+    public abstract JsonInclude.Include findSerializationInclusion(JsonInclude.Include defValue);
+
+    /**
+     * Method for checking what is the expected format for POJO, as
+     * defined by defaults and possible annotations.
+     * Note that this may be further refined by per-property annotations.
+     * 
+     * @since 2.1
+     */
+    public abstract JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue);
+
+    /**
+     * Method for finding {@link Converter} used for serializing instances
+     * of this class.
+     * 
+     * @since 2.2
+     */
+    public abstract Converter<Object,Object> findSerializationConverter();
+
+    /**
+     * Method for finding {@link Converter} used for serializing instances
+     * of this class.
+     * 
+     * @since 2.2
+     */
+    public abstract Converter<Object,Object> findDeserializationConverter();
+
+    /*
+    /**********************************************************
+    /* Basic API, other
+    /**********************************************************
+     */
+
+    public abstract Map<Object, AnnotatedMember> findInjectables();
+    
+    /**
+     * Method for checking if the POJO type has annotations to
+     * indicate that a builder is to be used for instantiating
+     * instances and handling data binding, instead of standard
+     * bean deserializer.
+     */
+    public abstract Class<?> findPOJOBuilder();
+
+    /**
+     * Method for finding configuration for POJO Builder class.
+     */
+    public abstract JsonPOJOBuilder.Value findPOJOBuilderConfig();
+    
+    /**
+     * Method called to create a "default instance" of the bean, currently
+     * only needed for obtaining default field values which may be used for
+     * suppressing serialization of fields that have "not changed".
+     * 
+     * @param fixAccess If true, method is allowed to fix access to the
+     *   default constructor (to be able to call non-public constructor);
+     *   if false, has to use constructor as is.
+     *
+     * @return Instance of class represented by this descriptor, if
+     *   suitable default constructor was found; null otherwise.
+     */
+    public abstract Object instantiateBean(boolean fixAccess);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
new file mode 100644
index 0000000..470ed3c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/BeanProperty.java
@@ -0,0 +1,186 @@
+package com.fasterxml.jackson.databind;
+
+import java.lang.annotation.Annotation;
+
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.Named;
+
+/**
+ * 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, contstructor parametrers).
+ * Instances allow access to annotations directly associated
+ * to property (via field or method), as well as contextual
+ * annotations (annotations for class that contains properties).
+ *<p>
+ * Instances are not typically passed when constructing serializers
+ * and deserializers, but rather only passed when context
+ * is known when
+ * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer} and
+ * {@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
+ * serializers/deserializers or structured types.
+ */
+public interface BeanProperty extends Named
+{
+    /**
+     * Method to get logical name of the property
+     */
+    @Override
+    public String getName();
+    
+    /**
+     * Method to get declared type of the property.
+     */
+    public JavaType getType();
+
+    /**
+     * If property is indicated to be wrapped, name of
+     * wrapper element to use.
+     * 
+     * @since 2.2
+     */
+    public PropertyName getWrapperName();
+    
+    /**
+     * Whether value for property is marked as required using
+     * annotations or associated schema.
+     * 
+     * @since 2.2
+     */
+    public boolean isRequired();
+    
+    /**
+     * Method for finding annotation associated with this property;
+     * meaning annotation associated with one of entities used to
+     * access property.
+     */
+    public <A extends Annotation> A getAnnotation(Class<A> acls);
+
+    /**
+     * Method for finding annotation associated with context of
+     * this property; usually class in which member is declared
+     * (or its subtype if processing subtype).
+     */
+    public <A extends Annotation> A getContextAnnotation(Class<A> acls);
+
+    /**
+     * Method for accessing primary physical entity that represents the property;
+     * annotated field, method or constructor property.
+     */
+    public AnnotatedMember getMember();
+
+    /**
+     * Method that can be called to visit the type structure that this
+     * property is part of.
+     * Note that not all implementations support traversal with this
+     * method; those that do not should throw
+     * {@link UnsupportedOperationException}.
+     * 
+     * @param objectVisitor Visitor to used as the callback handler
+     * 
+     * @since 2.2
+     */
+    public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor)
+        throws JsonMappingException;
+    
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Simple stand-alone implementation, useful as a placeholder
+     * or base class for more complex implementations.
+     */
+    public static class Std implements BeanProperty
+    {
+        protected final String _name;
+        protected final JavaType _type;
+        protected final PropertyName _wrapperName;
+        
+        protected final boolean _isRequired;
+
+        /**
+         * Physical entity (field, method or constructor argument) that
+         * is used to access value of property (or in case of constructor
+         * property, just placeholder)
+         */
+        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(String name, JavaType type, PropertyName wrapperName,
+                Annotations contextAnnotations, AnnotatedMember member,
+                boolean isRequired)
+        {
+            _name = name;
+            _type = type;
+            _wrapperName = wrapperName;
+            _isRequired = isRequired;
+            _member = member;
+            _contextAnnotations = contextAnnotations;
+        }
+        
+        public Std withType(JavaType type) {
+            return new Std(_name, type, _wrapperName, _contextAnnotations, _member, _isRequired);
+        }
+        
+        @Override
+        public <A extends Annotation> A getAnnotation(Class<A> acls) {
+            return (_member == null) ? null : _member.getAnnotation(acls);
+        }
+
+        @Override
+        public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
+            return (_contextAnnotations == null) ? null : _contextAnnotations.get(acls);
+        }
+        
+        @Override
+        public String getName() {
+            return _name;
+        }
+
+        @Override
+        public JavaType getType() {
+            return _type;
+        }
+
+        @Override
+        public PropertyName getWrapperName() {
+            return _wrapperName;
+        }
+        
+        @Override
+        public boolean isRequired() {
+            return _isRequired;
+        }
+        
+        @Override
+        public AnnotatedMember getMember() {
+            return _member;
+        }
+
+        /**
+         * Implementation of this method throws
+         * {@link UnsupportedOperationException}, since instances of this
+         * implementation should not be used as part of actual structure
+         * visited. Rather, other implementations should handle it.
+         */
+        @Override
+        public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor) {
+            throw new UnsupportedOperationException("Instances of "+getClass().getName()
+                    +" should not get visited");
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
new file mode 100644
index 0000000..5f6f793
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/DatabindContext.java
@@ -0,0 +1,165 @@
+package com.fasterxml.jackson.databind;
+
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Shared base class for {@link DeserializationContext} and
+ * {@link SerializerProvider}, context objects passed through data-binding
+ * process. Designed so that some of implementations can rely on shared
+ * aspects like access to secondary contextual objects like type factories
+ * or handler instantiators.
+ * 
+ * @since 2.2
+ */
+public abstract class DatabindContext
+{
+    /*
+    /**********************************************************
+    /* Generic config access
+    /**********************************************************
+     */
+
+    /**
+     * Accessor to currently active configuration (both per-request configs
+     * and per-mapper config).
+     */
+    public abstract MapperConfig<?> getConfig();
+
+    /**
+     * Convenience method for accessing serialization view in use (if any); equivalent to:
+     *<pre>
+     *   getConfig().getAnnotationIntrospector();
+     *</pre>
+     */
+    public abstract AnnotationIntrospector getAnnotationIntrospector();
+    
+    /*
+    /**********************************************************
+    /* Access to specific config settings
+    /**********************************************************
+     */
+    
+    /**
+     * Convenience method for checking whether specified serialization
+     * feature is enabled or not.
+     * Shortcut for:
+     *<pre>
+     *  getConfig().isEnabled(feature);
+     *</pre>
+     */
+    public final boolean isEnabled(MapperFeature feature) {
+        return getConfig().isEnabled(feature);
+    }
+
+    /**
+     * Convenience method for accessing serialization view in use (if any); equivalent to:
+     *<pre>
+     *   getConfig().canOverrideAccessModifiers();
+     *</pre>
+     */
+    public final boolean canOverrideAccessModifiers() {
+        return getConfig().canOverrideAccessModifiers();
+    }
+
+    /**
+     * Accessor for locating currently active view, if any;
+     * returns null if no view has been set.
+     */
+    public abstract Class<?> getActiveView();
+    
+    /*
+    /**********************************************************
+    /* Type instantiation/resolution
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for constructing {@link JavaType} for given JDK
+     * type (usually {@link java.lang.Class})
+     */
+    public JavaType constructType(Type type) {
+         return getTypeFactory().constructType(type);
+    }
+
+    /**
+     * Convenience method for constructing subtypes, retaining generic
+     * type parameter (if any)
+     */
+    public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) {
+        return getConfig().constructSpecializedType(baseType, subclass);
+    }
+
+    public abstract TypeFactory getTypeFactory();
+
+    /*
+    /**********************************************************
+    /* Helper object construction
+    /**********************************************************
+     */
+
+    public ObjectIdGenerator<?> objectIdGeneratorInstance(Annotated annotated,
+            ObjectIdInfo objectIdInfo)
+        throws JsonMappingException
+    {
+        Class<?> implClass = objectIdInfo.getGeneratorType();
+        final MapperConfig<?> config = getConfig();
+        HandlerInstantiator hi = config.getHandlerInstantiator();
+        ObjectIdGenerator<?> gen = (hi == null) ? null : hi.objectIdGeneratorInstance(config, annotated, implClass);
+        if (gen == null) {
+            gen = (ObjectIdGenerator<?>) ClassUtil.createInstance(implClass,
+                    config.canOverrideAccessModifiers());
+        }
+        return gen.forScope(objectIdInfo.getScope());
+    }
+    
+    /**
+     * Helper method to use to construct a {@link Converter}, given a definition
+     * that may be either actual converter instance, or Class for instantiating one.
+     * 
+     * @since 2.2
+     */
+    @SuppressWarnings("unchecked")
+    public Converter<Object,Object> converterInstance(Annotated annotated,
+            Object converterDef)
+        throws JsonMappingException
+    {
+        if (converterDef == null) {
+            return null;
+        }
+        if (converterDef instanceof Converter<?,?>) {
+            return (Converter<Object,Object>) converterDef;
+        }
+        if (!(converterDef instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type "
+                    +converterDef.getClass().getName()+"; expected type Converter or Class<Converter> instead");
+        }
+        Class<?> converterClass = (Class<?>)converterDef;
+        // there are some known "no class" markers to consider too:
+        if (converterClass == Converter.None.class || converterClass == NoClass.class) {
+            return null;
+        }
+        if (!Converter.class.isAssignableFrom(converterClass)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Class "
+                    +converterClass.getName()+"; expected Class<Converter>");
+        }
+        final MapperConfig<?> config = getConfig();
+        HandlerInstantiator hi = config.getHandlerInstantiator();
+        Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(config, annotated, converterClass);
+        if (conv == null) {
+            conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
+                    config.canOverrideAccessModifiers());
+        }
+        return (Converter<Object,Object>) conv;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
new file mode 100644
index 0000000..4824d7d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationConfig.java
@@ -0,0 +1,526 @@
+package com.fasterxml.jackson.databind;
+
+import java.text.DateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.cfg.BaseSettings;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfigBase;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+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.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.type.ClassKey;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.LinkedNode;
+
+/**
+ * Object that contains baseline configuration for deserialization
+ * process. An instance is owned by {@link ObjectMapper}, which
+ * passes an immutable instance to be used for deserialization process.
+ *<p>
+ * Note that instances are considered immutable and as such no copies
+ * should need to be created for sharing; all copying is done with
+ * "fluent factory" methods.
+ * Note also that unlike with Jackson 1, these instances can not be
+ * assigned to {@link ObjectMapper}; in fact, application code should
+ * rarely interact directly with these instance (unlike core Jackson code)
+ */
+public final class DeserializationConfig
+    extends MapperConfigBase<DeserializationFeature, DeserializationConfig>
+    implements java.io.Serializable // since 2.1
+{
+    // for 2.1.0
+    private static final long serialVersionUID = -4227480407273773599L;
+
+    /**
+     * Set of features enabled; actual type (kind of features)
+     * depends on sub-classes.
+     */
+    protected final int _deserFeatures;
+
+    /**
+     * 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.
+     */
+    protected final JsonNodeFactory _nodeFactory;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, constructors
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by ObjectMapper to create default configuration object instance.
+     */
+    public DeserializationConfig(BaseSettings base,
+            SubtypeResolver str, Map<ClassKey,Class<?>> mixins)
+    {
+        super(base, str, mixins);
+        _deserFeatures = collectFeatureDefaults(DeserializationFeature.class);
+        _nodeFactory = JsonNodeFactory.instance;
+        _problemHandlers = null;
+    }
+
+    /**
+     * Copy constructor used to create a non-shared instance with given mix-in
+     * annotation definitions and subtype resolver.
+     */
+    private DeserializationConfig(DeserializationConfig src, SubtypeResolver str)
+    {
+        super(src, str);
+        _deserFeatures = src._deserFeatures;
+        _nodeFactory = src._nodeFactory;
+        _problemHandlers = src._problemHandlers;
+    }
+
+    private DeserializationConfig(DeserializationConfig src,
+            int mapperFeatures, int deserFeatures)
+    {
+        super(src, mapperFeatures);
+        _deserFeatures = deserFeatures;
+        _nodeFactory = src._nodeFactory;
+        _problemHandlers = src._problemHandlers;
+    }
+    
+    private DeserializationConfig(DeserializationConfig src, BaseSettings base)
+    {
+        super(src, base);
+        _deserFeatures = src._deserFeatures;
+        _nodeFactory = src._nodeFactory;
+        _problemHandlers = src._problemHandlers;
+    }
+    
+    private DeserializationConfig(DeserializationConfig src, JsonNodeFactory f)
+    {
+        super(src);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = src._problemHandlers;
+        _nodeFactory = f;
+    }
+
+    private DeserializationConfig(DeserializationConfig src,
+            LinkedNode<DeserializationProblemHandler> problemHandlers)
+    {
+        super(src);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = problemHandlers;
+        _nodeFactory = src._nodeFactory;
+    }
+
+    private DeserializationConfig(DeserializationConfig src, String rootName)
+    {
+        super(src, rootName);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = src._problemHandlers;
+        _nodeFactory = src._nodeFactory;
+    }
+
+    private DeserializationConfig(DeserializationConfig src, Class<?> view)
+    {
+        super(src, view);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = src._problemHandlers;
+        _nodeFactory = src._nodeFactory;
+    }
+
+    /**
+     * @since 2.1
+     */
+    protected DeserializationConfig(DeserializationConfig src, Map<ClassKey,Class<?>> mixins)
+    {
+        super(src, mixins);
+        _deserFeatures = src._deserFeatures;
+        _problemHandlers = src._problemHandlers;
+        _nodeFactory = src._nodeFactory;
+    }
+    
+    // for unit tests only:
+    protected BaseSettings getBaseSettings() { return _base; }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, factory methods from MapperConfig
+    /**********************************************************
+     */
+
+    @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);
+    }
+
+    @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);
+    }
+    
+    @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));
+    }
+
+    @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(String rootName) {
+        if (rootName == null) {
+            if (_rootName == null) {
+                return this;
+            }
+        } else if (rootName.equals(_rootName)) {
+            return this;
+        }
+        return new DeserializationConfig(this, rootName);
+    }
+    
+    @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));
+    }
+    
+    private final DeserializationConfig _withBase(BaseSettings newBase) {
+        return (_base == newBase) ? this : new DeserializationConfig(this, newBase);
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, deserialization-specific factory methods
+    /**********************************************************
+     */
+
+    /**
+     * Fluent factory method that will construct a new instance with
+     * specified {@link JsonNodeFactory}
+     */
+    public DeserializationConfig with(JsonNodeFactory f) {
+        if (_nodeFactory == f) {
+            return this;
+        }
+        return new DeserializationConfig(this, f);
+    }
+
+    /**
+     * Method that can be used to add a handler that can (try to)
+     * resolve non-fatal deserialization problems.
+     */
+    public DeserializationConfig withHandler(DeserializationProblemHandler h)
+    {
+        // Sanity check: let's prevent adding same handler multiple times
+        if (LinkedNode.contains(_problemHandlers, h)) {
+            return this;
+        }
+        return new DeserializationConfig(this,
+                new LinkedNode<DeserializationProblemHandler>(h, _problemHandlers));
+    }
+
+    /**
+     * Method for removing all configured problem handlers; usually done to replace
+     * existing handler(s) with different one(s)
+     */
+    public DeserializationConfig withNoProblemHandlers() {
+        if (_problemHandlers == null) {
+            return this;
+        }
+        return new DeserializationConfig(this,
+                (LinkedNode<DeserializationProblemHandler>) null);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    public DeserializationConfig with(DeserializationFeature feature)
+    {
+        int newDeserFeatures = (_deserFeatures | feature.getMask());
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    public DeserializationConfig with(DeserializationFeature first,
+            DeserializationFeature... features)
+    {
+        int newDeserFeatures = _deserFeatures | first.getMask();
+        for (DeserializationFeature f : features) {
+            newDeserFeatures |= f.getMask();
+        }
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    public DeserializationConfig withFeatures(DeserializationFeature... features)
+    {
+        int newDeserFeatures = _deserFeatures;
+        for (DeserializationFeature f : features) {
+            newDeserFeatures |= f.getMask();
+        }
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+    
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified feature disabled.
+     */
+    public DeserializationConfig without(DeserializationFeature feature)
+    {
+        int newDeserFeatures = _deserFeatures & ~feature.getMask();
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features disabled.
+     */
+    public DeserializationConfig without(DeserializationFeature first,
+            DeserializationFeature... features)
+    {
+        int newDeserFeatures = _deserFeatures & ~first.getMask();
+        for (DeserializationFeature f : features) {
+            newDeserFeatures &= ~f.getMask();
+        }
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features disabled.
+     */
+    public DeserializationConfig withoutFeatures(DeserializationFeature... features)
+    {
+        int newDeserFeatures = _deserFeatures;
+        for (DeserializationFeature f : features) {
+            newDeserFeatures &= ~f.getMask();
+        }
+        return (newDeserFeatures == _deserFeatures) ? this :
+            new DeserializationConfig(this, _mapperFeatures, newDeserFeatures);
+    }
+    
+    /*
+    /**********************************************************
+    /* MapperConfig implementation
+    /**********************************************************
+     */
+    
+    /**
+     * 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;
+    }
+
+    @Override
+    public boolean useRootWrapping()
+    {
+        if (_rootName != null) { // empty String disables wrapping; non-empty enables
+            return (_rootName.length() > 0);
+        }
+        return isEnabled(DeserializationFeature.UNWRAP_ROOT_VALUE);
+    }
+    
+    /**
+     * 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);
+    }
+
+    @Override
+    public VisibilityChecker<?> getDefaultVisibilityChecker()
+    {
+        VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
+        if (!isEnabled(MapperFeature.AUTO_DETECT_SETTERS)) {
+            vchecker = vchecker.withSetterVisibility(Visibility.NONE);
+        }
+        if (!isEnabled(MapperFeature.AUTO_DETECT_CREATORS)) {
+            vchecker = vchecker.withCreatorVisibility(Visibility.NONE);
+        }
+        if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
+            vchecker = vchecker.withFieldVisibility(Visibility.NONE);
+        }
+        return vchecker;
+    }
+
+    public final boolean isEnabled(DeserializationFeature f) {
+        return (_deserFeatures & f.getMask()) != 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Other configuration
+    /**********************************************************
+     */
+
+    public final int getDeserializationFeatures() {
+        return _deserFeatures;
+    }
+    
+    /**
+     * Method for getting head of the problem handler chain. May be null,
+     * if no handlers have been added.
+     */
+    public LinkedNode<DeserializationProblemHandler> getProblemHandlers() {
+        return _problemHandlers;
+    }
+
+    public final JsonNodeFactory getNodeFactory() {
+        return _nodeFactory;
+    }
+    
+    /*
+    /**********************************************************
+    /* Introspection methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that will introspect full bean properties for the purpose
+     * of building a bean deserializer
+     *
+     * @param type Type of class to be introspected
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends BeanDescription> T introspect(JavaType type) {
+        return (T) getClassIntrospector().forDeserialization(this, type, this);
+    }
+
+    /**
+     * Method that will introspect subset of bean properties needed to
+     * construct bean instance.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends BeanDescription> T introspectForCreation(JavaType type) {
+        return (T) getClassIntrospector().forCreation(this, type, this);
+    }
+
+    /**
+     * @since 2.0
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends BeanDescription> T introspectForBuilder(JavaType type) {
+        return (T) getClassIntrospector().forDeserializationWithBuilder(this, type, this);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
new file mode 100644
index 0000000..6a0bbb4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationContext.java
@@ -0,0 +1,742 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
+import com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.*;
+
+/**
+ * Context for the process of deserialization a single root-level value.
+ * 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
+ * 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
+ * ({@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.
+ *<p>
+ * Defined as abstract class so that implementations must define methods
+ * for reconfiguring blueprints and creating instances.
+ */
+public abstract class DeserializationContext
+    extends DatabindContext
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = -7727373309391091315L;
+
+    /**
+     * 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
+    /**********************************************************
+     */
+    
+    /**
+     * Object that handle details of {@link JsonDeserializer} caching.
+     */
+    protected final DeserializerCache _cache;
+
+    /*
+    /**********************************************************
+    /* Configuration, changeable via fluent factories
+    /**********************************************************
+     */
+
+    /**
+     * Read-only factory instance; exposed to let
+     * owners (<code>ObjectMapper</code>, <code>ObjectReader</code>)
+     * access it.
+     */
+    protected final DeserializerFactory _factory;
+
+    /*
+    /**********************************************************
+    /* Configuration that gets set for instances (not blueprints)
+    /* (partly denormalized for performance)
+    /**********************************************************
+     */
+
+    /**
+     * Generic deserialization processing configuration
+     */
+    protected final DeserializationConfig _config;
+
+    /**
+     * Bitmap of {@link DeserializationFeature}s that are enabled
+     */
+    protected final int _featureFlags;
+
+    /**
+     * Currently active view, if any.
+     */
+    protected final Class<?> _view;
+
+    /**
+     * Currently active parser used for deserialization.
+     * May be different from the outermost parser
+     * when content is buffered.
+     */
+    protected transient JsonParser _parser;
+    
+    /**
+     * Object used for resolving references to injectable
+     * values.
+     */
+    protected final InjectableValues _injectableValues;
+    
+    /*
+    /**********************************************************
+    /* Per-operation reusable helper objects (not for blueprints)
+    /**********************************************************
+     */
+
+    protected transient ArrayBuilders _arrayBuilders;
+
+    protected transient ObjectBuffer _objectBuffer;
+
+    protected transient DateFormat _dateFormat;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected DeserializationContext(DeserializerFactory df) {
+        this(df, null);
+    }
+    
+    protected DeserializationContext(DeserializerFactory df,
+            DeserializerCache cache)
+    {
+        if (df == null) {
+            throw new IllegalArgumentException("Can not pass null DeserializerFactory");
+        }
+        _factory = df;
+        _cache = (cache == null) ? new DeserializerCache() : cache;
+        
+        _featureFlags = 0;
+        _config = null;
+        _injectableValues = null;
+        _view = null;
+    }
+
+    protected DeserializationContext(DeserializationContext src,
+            DeserializerFactory factory)
+    {
+        _cache = src._cache;
+        _factory = factory;
+        
+        _config = src._config;
+        _featureFlags = src._featureFlags;
+        _view = src._view;
+        _parser = src._parser;
+        _injectableValues = src._injectableValues;
+    }
+    
+    protected DeserializationContext(DeserializationContext src,
+            DeserializationConfig config, JsonParser jp,
+            InjectableValues injectableValues)
+    {
+        _cache = src._cache;
+        _factory = src._factory;
+        
+        _config = config;
+        _featureFlags = config.getDeserializationFeatures();
+        _view = config.getActiveView();
+        _parser = jp;
+        _injectableValues = injectableValues;
+    }
+
+    /*
+    /**********************************************************
+    /* DatabindContext implementation
+    /**********************************************************
+     */
+
+    @Override
+    public DeserializationConfig getConfig() { return _config; }
+
+    @Override
+    public final Class<?> getActiveView() { return _view; }
+
+    @Override
+    public final AnnotationIntrospector getAnnotationIntrospector() {
+        return _config.getAnnotationIntrospector();
+    }
+
+    @Override
+    public final TypeFactory getTypeFactory() {
+        return _config.getTypeFactory();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Method for getting current {@link DeserializerFactory}.
+     */
+    public DeserializerFactory getFactory() {
+        return _factory;
+    }
+    
+    /**
+     * Convenience method for checking whether specified on/off
+     * feature is enabled
+     */
+    public final boolean isEnabled(DeserializationFeature feat) {
+        /* 03-Dec-2010, tatu: minor shortcut; since this is called quite often,
+         *   let's use a local copy of feature settings:
+         */
+        return (_featureFlags & feat.getMask()) != 0;
+    }
+
+    /**
+     * Method for accessing the currently active parser.
+     * May be different from the outermost parser
+     * when content is buffered.
+     *<p>
+     * Use of this method is discouraged: if code has direct access
+     * to the active parser, that should be used instead.
+     */
+    public final JsonParser getParser() { return _parser; }
+
+    public final Object findInjectableValue(Object valueId,
+            BeanProperty forProperty, Object beanInstance)
+    {
+        if (_injectableValues == null) {
+            throw new IllegalStateException("No 'injectableValues' configured, can not inject value with id ["+valueId+"]");
+        }
+        return _injectableValues.findInjectableValue(valueId, this, forProperty, beanInstance);
+    }
+
+    /**
+     * Convenience method for accessing the default Base64 encoding
+     * used for decoding base64 encoded binary content.
+     * Same as calling:
+     *<pre>
+     *  getConfig().getBase64Variant();
+     *</pre>
+     */
+    public final Base64Variant getBase64Variant() {
+        return _config.getBase64Variant();
+    }
+
+    /**
+     * Convenience method, functionally equivalent to:
+     *<pre>
+     *  getConfig().getNodeFactory();
+     * </pre>
+     */
+    public final JsonNodeFactory getNodeFactory() {
+        return _config.getNodeFactory();
+    }
+
+    /**
+     * Method for accessing default Locale to use: convenience method for
+     *<pre>
+     *   getConfig().getLocale();
+     *</pre>
+     */
+    public Locale getLocale() {
+        return _config.getLocale();
+    }
+
+    /**
+     * Method for accessing default TimeZone to use: convenience method for
+     *<pre>
+     *   getConfig().getTimeZone();
+     *</pre>
+     */
+    public TimeZone getTimeZone() {
+        return _config.getTimeZone();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, pass-through to DeserializerCache
+    /**********************************************************
+     */
+
+    /**
+     * Method for checking whether we could find a deserializer
+     * for given type.
+     */
+    public boolean hasValueDeserializerFor(JavaType type) {
+        return _cache.hasValueDeserializerFor(this, _factory, type);
+    }
+    
+    
+    /**
+     * Method for finding a value deserializer, and creating a contextual
+     * version if necessary, for value reached via specified property.
+     */
+    @SuppressWarnings("unchecked")
+    public final JsonDeserializer<Object> findContextualValueDeserializer(JavaType type,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<Object> deser = _cache.findValueDeserializer(this,
+                _factory, type);
+        if (deser != null) {
+            if (deser instanceof ContextualDeserializer) {
+                deser = (JsonDeserializer<Object>)((ContextualDeserializer) deser).createContextual(this, property);
+            }
+        }
+        return deser;
+    }
+    
+    /**
+     * Method for finding a deserializer for root-level value.
+     */
+    @SuppressWarnings("unchecked")
+    public final JsonDeserializer<Object> findRootValueDeserializer(JavaType type)
+            throws JsonMappingException
+    {
+        JsonDeserializer<Object> deser = _cache.findValueDeserializer(this,
+                _factory, type);
+        if (deser == null) { // can this occur?
+            return null;
+        }
+        if (deser instanceof ContextualDeserializer) {
+            deser = (JsonDeserializer<Object>)((ContextualDeserializer) deser).createContextual(this, null);
+        }
+        TypeDeserializer typeDeser = _factory.findTypeDeserializer(_config, type);
+        if (typeDeser != null) {
+            // important: contextualize to indicate this is for root value
+            typeDeser = typeDeser.forProperty(null);
+            return new TypeWrappedDeserializer(typeDeser, deser);
+        }
+        return deser;
+    }
+
+    /**
+     * Convenience method, functionally same as:
+     *<pre>
+     *  getDeserializerProvider().findKeyDeserializer(getConfig(), propertyType, property);
+     *</pre>
+     */
+    public final KeyDeserializer findKeyDeserializer(JavaType keyType,
+            BeanProperty property) throws JsonMappingException {
+        KeyDeserializer kd = _cache.findKeyDeserializer(this,
+                _factory, keyType);
+        // Second: contextualize?
+        if (kd instanceof ContextualKeyDeserializer) {
+            kd = ((ContextualKeyDeserializer) kd).createContextual(this, property);
+        }
+        return kd;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, ObjectId handling
+    /**********************************************************
+     */
+
+    /**
+     * Method called to find and return entry corresponding to given
+     * Object Id: will add an entry if necessary, and never returns null
+     */
+    public abstract ReadableObjectId findObjectId(Object id,
+            ObjectIdGenerator<?> generator);
+
+    /*
+    /**********************************************************
+    /* Public API, type handling
+    /**********************************************************
+     */
+    
+    /**
+     * Convenience method, functionally equivalent to:
+     *<pre>
+     *  getConfig().constructType(cls);
+     * </pre>
+     */
+    public final JavaType constructType(Class<?> cls) {
+        return _config.constructType(cls);
+    }
+
+    /**
+     * Helper method to use for locating Class for given name. Should be used
+     * instead of basic <code>Class.forName(className);</code> as it can
+     * try using contextual class loader, or use platform-specific workarounds
+     * (like on Android, GAE).
+     */
+    public Class<?> findClass(String className) throws ClassNotFoundException
+    {
+        // By default, delegate to ClassUtil: can be overridden with custom handling
+        return ClassUtil.findClass(className);
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API: handler instantiation
+    /**********************************************************
+     */
+
+    public abstract JsonDeserializer<Object> deserializerInstance(Annotated annotated,
+            Object deserDef)
+        throws JsonMappingException;
+
+    public abstract KeyDeserializer keyDeserializerInstance(Annotated annotated,
+            Object deserDef)
+        throws JsonMappingException;
+
+    /*
+    /**********************************************************
+    /* Public API, helper object recycling
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to get access to a reusable ObjectBuffer,
+     * useful for efficiently constructing Object arrays and Lists.
+     * Note that leased buffers should be returned once deserializer
+     * is done, to allow for reuse during same round of deserialization.
+     */
+    public final ObjectBuffer leaseObjectBuffer()
+    {
+        ObjectBuffer buf = _objectBuffer;
+        if (buf == null) {
+            buf = new ObjectBuffer();
+        } else {
+            _objectBuffer = null;
+        }
+        return buf;
+    }
+
+    /**
+     * Method to call to return object buffer previously leased with
+     * {@link #leaseObjectBuffer}.
+     * 
+     * @param buf Returned object buffer
+     */
+    public final void returnObjectBuffer(ObjectBuffer buf)
+    {
+        /* Already have a reusable buffer? Let's retain bigger one
+         * (or if equal, favor newer one, shorter life-cycle)
+         */
+        if (_objectBuffer == null
+            || buf.initialCapacity() >= _objectBuffer.initialCapacity()) {
+            _objectBuffer = buf;
+        }
+    }
+
+    /**
+     * Method for accessing object useful for building arrays of
+     * primitive types (such as int[]).
+     */
+    public final ArrayBuilders getArrayBuilders()
+    {
+        if (_arrayBuilders == null) {
+            _arrayBuilders = new ArrayBuilders();
+        }
+        return _arrayBuilders;
+    }
+
+    /*
+    /**********************************************************
+    /* Parsing methods that may use reusable/-cyclable objects
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for parsing a Date from given String, using
+     * currently configured date format (accessed using
+     * {@link DeserializationConfig#getDateFormat()}).
+     *<p>
+     * Implementation will handle thread-safety issues related to
+     * date formats such that first time this method is called,
+     * date format is cloned, and cloned instance will be retained
+     * for use during this deserialization round.
+     */
+    public Date parseDate(String dateStr)
+        throws IllegalArgumentException
+    {
+        try {
+            return getDateFormat().parse(dateStr);
+        } catch (ParseException e) {
+            throw new IllegalArgumentException("Failed to parse Date value '"+dateStr+"': "+e.getMessage());
+        }
+    }
+
+    /**
+     * Convenience method for constructing Calendar instance set
+     * to specified time, to be modified and used by caller.
+     */
+    public Calendar constructCalendar(Date d)
+    {
+        /* 08-Jan-2008, tatu: not optimal, but should work for the
+         *   most part; let's revise as needed.
+         */
+        Calendar c = Calendar.getInstance(getTimeZone());
+        c.setTime(d);
+        return c;
+    }
+
+    /*
+    /**********************************************************
+    /* Methods for problem handling, reporting
+    /**********************************************************
+     */
+
+    /**
+     * Method deserializers can call to inform configured {@link DeserializationProblemHandler}s
+     * of an unrecognized property.
+     * 
+     * @return True if there was a configured problem handler that was able to handle the
+     *   problem
+     */
+    /**
+     * Method deserializers can call to inform configured {@link DeserializationProblemHandler}s
+     * of an unrecognized property.
+     */
+    public boolean handleUnknownProperty(JsonParser jp, JsonDeserializer<?> deser,
+            Object instanceOrClass, String propName)
+        throws IOException, JsonProcessingException
+    {
+        LinkedNode<DeserializationProblemHandler> h = _config.getProblemHandlers();
+        if (h != null) {
+            while (h != null) {
+                // Can bail out if it's handled
+                if (h.value().handleUnknownProperty(this, jp, deser, instanceOrClass, propName)) {
+                    return true;
+                }
+                h = h.next();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Helper method for reporting a problem with unhandled unknown exception
+     * 
+     * @param instanceOrClass Either value being populated (if one has been
+     *   instantiated), or Class that indicates type that would be (or
+     *   have been) instantiated
+     * @param deser Deserializer that had the problem, if called by deserializer
+     *   (or on behalf of one)
+     */
+    public void reportUnknownProperty(Object instanceOrClass, String fieldName,
+            JsonDeserializer<?> deser)
+        throws JsonMappingException
+    {
+        if (!isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
+            return;
+        }
+        // Do we know properties that are expected instead?
+        Collection<Object> propIds = (deser == null) ? null : deser.getKnownPropertyNames();
+        throw UnrecognizedPropertyException.from(_parser,
+                instanceOrClass, fieldName, propIds);
+    }
+    
+    /*
+    /**********************************************************
+    /* Methods for constructing exceptions
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method for constructing generic mapping exception for specified type
+     */
+    public JsonMappingException mappingException(Class<?> targetClass) {
+        return mappingException(targetClass, _parser.getCurrentToken());
+    }
+
+    public JsonMappingException mappingException(Class<?> targetClass, JsonToken token)
+    {
+        String clsName = _calcName(targetClass);
+        return JsonMappingException.from(_parser,
+                "Can not deserialize instance of "+clsName+" out of "+token+" token");
+    }
+    
+    /**
+     * Helper method for constructing generic mapping exception with specified
+     * message and current location information
+     */
+    public JsonMappingException mappingException(String message) {
+        return JsonMappingException.from(getParser(), message);
+    }
+    
+    /**
+     * Helper method for constructing instantiation exception for specified type,
+     * to indicate problem with physically constructing instance of
+     * specified class (missing constructor, exception from constructor)
+     */
+    public JsonMappingException instantiationException(Class<?> instClass, Throwable t)
+    {
+        return JsonMappingException.from(_parser,
+                "Can not construct instance of "+instClass.getName()+", problem: "+t.getMessage(),
+                t);
+    }
+
+    public JsonMappingException instantiationException(Class<?> instClass, String msg) {
+        return JsonMappingException.from(_parser, "Can not construct instance of "+instClass.getName()+", problem: "+msg);
+    }
+    
+    /**
+     * Method that will construct an exception suitable for throwing when
+     * some String values are acceptable, but the one encountered is not.
+     * 
+     * 
+     * @deprecated Since 2.1 should use variant that takes value
+     */
+    @Deprecated
+    public JsonMappingException weirdStringException(Class<?> instClass, String msg) {
+        return weirdStringException(null, instClass, msg);
+    }
+
+    /**
+     * Method that will construct an exception suitable for throwing when
+     * some String values are acceptable, but the one encountered is not.
+     * 
+     * @param value String value from input being deserialized
+     * @param instClass Type that String should be deserialized into
+     * @param msg Message that describes specific problem
+     * 
+     * @since 2.1
+     */
+    public JsonMappingException weirdStringException(String value, Class<?> instClass, String msg) {
+        return InvalidFormatException.from(_parser,
+                "Can not construct instance of "+instClass.getName()+" from String value '"+_valueDesc()+"': "+msg,
+                value, instClass);
+    }
+
+    /**
+     * Helper method for constructing exception to indicate that input JSON
+     * Number was not suitable for deserializing into given type.
+     */
+    @Deprecated
+    public JsonMappingException weirdNumberException(Class<?> instClass, String msg) {
+        return weirdStringException(null, instClass, msg);
+    }
+
+    /**
+     * Helper method for constructing exception to indicate that input JSON
+     * Number was not suitable for deserializing into given target type.
+     */
+    public JsonMappingException weirdNumberException(Number value, Class<?> instClass, String msg) {
+        return InvalidFormatException.from(_parser,
+                "Can not construct instance of "+instClass.getName()+" from number value ("+_valueDesc()+"): "+msg,
+                null, instClass);
+    }
+    
+    /**
+     * Helper method for constructing exception to indicate that given JSON
+     * Object field name was not in format to be able to deserialize specified
+     * key type.
+     */
+    public JsonMappingException weirdKeyException(Class<?> keyClass, String keyValue, String msg)
+    {
+        return InvalidFormatException.from(_parser,
+                "Can not construct Map key of type "+keyClass.getName()+" from String \""+_desc(keyValue)+"\": "+msg,
+                keyValue, keyClass);
+    }
+
+    /**
+     * Helper method for indicating that the current token was expected to be another
+     * token.
+     */
+    public JsonMappingException wrongTokenException(JsonParser jp, JsonToken expToken, String msg)
+    {
+        return JsonMappingException.from(jp, "Unexpected token ("+jp.getCurrentToken()+"), expected "+expToken+": "+msg);
+    }
+
+    /**
+     * Helper method for constructing exception to indicate that given
+     * type id (parsed from JSON) could not be converted to a Java type.
+     */
+    public JsonMappingException unknownTypeException(JavaType type, String id)
+    {
+        return JsonMappingException.from(_parser, "Could not resolve type id '"+id+"' into a subtype of "+type);
+    }
+
+    public JsonMappingException endOfInputException(Class<?> instClass)
+    {
+        return JsonMappingException.from(_parser, "Unexpected end-of-input when trying to deserialize a "
+                +instClass.getName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable internal methods
+    /**********************************************************
+     */
+
+    protected DateFormat getDateFormat()
+    {
+        if (_dateFormat != null) {
+            return _dateFormat;
+        }
+        /* 24-Feb-2012, tatu: At this point, all timezone configuration
+         *    should have occured, with respect to default dateformat
+         *    and timezone configuration. But we still better clone
+         *    an instance as formatters may be stateful.
+         */
+        DateFormat df = _config.getDateFormat();
+        _dateFormat = df = (DateFormat) df.clone();
+        return df;
+    }
+
+    protected String determineClassName(Object instance)
+    {
+        return ClassUtil.getClassDescription(instance);
+    }
+    
+    /*
+    /**********************************************************
+    /* Other internal methods
+    /**********************************************************
+     */
+
+    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)
+    {
+        // !!! 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;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
new file mode 100644
index 0000000..3385097
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/DeserializationFeature.java
@@ -0,0 +1,289 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.databind.cfg.ConfigFeature;
+
+/**
+ * Enumeration that defines simple on/off features that affect
+ * the way Java objects are deserialized from JSON
+ *<p>
+ * Note that features can be set both through
+ * {@link ObjectMapper} (as sort of defaults) and through
+ * {@link ObjectReader}.
+ * In first case these defaults must follow "config-then-use" patterns
+ * (i.e. defined once, not changed afterwards); all per-call
+ * changes must be done using {@link ObjectReader}.
+ */
+public enum DeserializationFeature implements ConfigFeature
+{
+    /*
+    /******************************************************
+    /* Type conversion features
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether JSON floating point numbers
+     * are to be deserialized into {@link java.math.BigDecimal}s
+     * if only generic type description (either {@link Object} or
+     * {@link Number}, or within untyped {@link java.util.Map}
+     * or {@link java.util.Collection} context) is available.
+     * If enabled such values will be deserialized as {@link java.math.BigDecimal}s;
+     * if disabled, will be deserialized as {@link Double}s.
+     * <p>
+     * Feature is disabled by default, meaning that "untyped" floating
+     * point numbers will by default be deserialized as {@link Double}s
+     * (choice is for performance reason -- BigDecimals are slower than
+     * Doubles).
+     */
+    USE_BIG_DECIMAL_FOR_FLOATS(false),
+
+    /**
+     * Feature that determines whether JSON integral (non-floating-point)
+     * numbers are to be deserialized into {@link java.math.BigInteger}s
+     * if only generic type description (either {@link Object} or
+     * {@link Number}, or within untyped {@link java.util.Map}
+     * or {@link java.util.Collection} context) is available.
+     * If enabled such values will be deserialized as
+     * {@link java.math.BigInteger}s;
+     * if disabled, will be deserialized as "smallest" available type,
+     * which is either {@link Integer}, {@link Long} or
+     * {@link java.math.BigInteger}, depending on number of digits.
+     * <p>
+     * Feature is disabled by default, meaning that "untyped" floating
+     * point numbers will by default be deserialized using whatever
+     * is the most compact integral type, to optimize efficiency.
+     */
+    USE_BIG_INTEGER_FOR_INTS(false),
+
+    // [JACKSON-652]
+    /**
+     * Feature that determines whether JSON Array is mapped to
+     * <code>Object[]</code> or <code>List<Object></code> when binding
+     * "untyped" objects (ones with nominal type of <code>java.lang.Object</code>).
+     * If true, binds as <code>Object[]</code>; if false, as <code>List<Object></code>.
+     *<p>
+     * Feature is disabled by default, meaning that JSON arrays are bound as
+     * {@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
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether encountering of unknown
+     * properties (ones that do not map to a property, and there is
+     * no "any setter" or handler that can handle it)
+     * should result in a failure (by throwing a
+     * {@link JsonMappingException}) or not.
+     * This setting only takes effect after all other handling
+     * methods for unknown properties have been tried, and
+     * property remains unhandled.
+     *<p>
+     * Feature is enabled by default (meaning that a
+     * {@link JsonMappingException} will be thrown if an unknown property
+     * is encountered).
+     */
+    FAIL_ON_UNKNOWN_PROPERTIES(true),
+
+    /**
+     * Feature that determines whether encountering of JSON null
+     * is an error when deserializing into Java primitive types
+     * (like 'int' or 'double'). If it is, a JsonProcessingException
+     * is thrown to indicate this; if not, default value is used
+     * (0 for 'int', 0.0 for double, same defaulting as what JVM uses).
+     *<p>
+     * Feature is disabled by default.
+     */
+    FAIL_ON_NULL_FOR_PRIMITIVES(false),
+
+    /**
+     * Feature that determines whether JSON integer numbers are valid
+     * values to be used for deserializing Java enum values.
+     * If set to 'false' numbers are acceptable and are used to map to
+     * ordinal() of matching enumeration value; if 'true', numbers are
+     * not allowed and a {@link JsonMappingException} will be thrown.
+     * Latter behavior makes sense if there is concern that accidental
+     * mapping from integer values to enums might happen (and when enums
+     * are always serialized as JSON Strings)
+     *<p>
+     * Feature is disabled by default.
+     */
+    FAIL_ON_NUMBERS_FOR_ENUMS(false),
+
+    /**
+     * 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);
+     * 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
+     * type information.
+     * 
+     * @since 2.2
+     */
+    FAIL_ON_INVALID_SUBTYPE(true),
+    
+    /**
+     * Feature that determines whether Jackson code should catch
+     * and wrap {@link Exception}s (but never {@link Error}s!)
+     * to add additional information about
+     * location (within input) of problem or not. If enabled,
+     * most exceptions will be caught and re-thrown (exception
+     * specifically being that {@link java.io.IOException}s may be passed
+     * as is, since they are declared as throwable); this can be
+     * convenient both in that all exceptions will be checked and
+     * declared, and so there is more contextual information.
+     * However, sometimes calling application may just want "raw"
+     * unchecked exceptions passed as is.
+     *<p>
+     * Feature is enabled by default.
+     */
+    WRAP_EXCEPTIONS(true),
+    
+    /*
+    /******************************************************
+    /* Structural conversion features
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether it is acceptable to coerce non-array
+     * (in JSON) values to work with Java collection (arrays, java.util.Collection)
+     * types. If enabled, collection deserializers will try to handle non-array
+     * values as if they had "implicit" surrounding JSON array.
+     * This feature is meant to be used for compatibility/interoperability reasons,
+     * to work with packages (such as XML-to-JSON converters) that leave out JSON
+     * array in cases where there is just a single element in array.
+     *<p>
+     * Feature is disabled by default.
+     */
+    ACCEPT_SINGLE_VALUE_AS_ARRAY(false),
+
+    /**
+     * Feature to allow "unwrapping" root-level JSON value, to match setting of
+     * {@link SerializationFeature#WRAP_ROOT_VALUE} used for serialization.
+     * Will verify that the root JSON value is a JSON Object, and that it has
+     * a single property with expected root name. If not, a
+     * {@link JsonMappingException} is thrown; otherwise value of the wrapped property
+     * will be deserialized as if it was the root value.
+     *<p>
+     * Feature is disabled by default.
+     */
+    UNWRAP_ROOT_VALUE(false),
+
+    /*
+    /******************************************************
+    /* Value conversion features
+    /******************************************************
+     */
+    
+    /**
+     * 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
+     * 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 enable, empty JSON String can be taken
+     * to be equivalent of JSON null.
+     *<p>
+     * Feature is enabled by default.
+     */
+    ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),
+    
+    /**
+     * Feature that allows unknown Enum values to be parsed as null values. 
+     * If disabled, unknown Enum values will throw exceptions.
+     *<p>
+     * Note that in some cases this will basically ignore unknown Enum values;
+     * this is the keys for keys of {@link java.util.EnumMap} and values
+     * of {@link java.util.EnumSet} (because nulls are not accepted in these
+     * cases).
+     *<p>
+     * Feature is disabled by default.
+     * 
+     * @since 2.0
+     */
+    READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
+
+    /**
+     * Feature that controls whether numeric timestamp values are expected
+     * to be written using nanosecond timestamps (enabled) or not (disabled),
+     * <b>if and only if</b> datatype supports such resolution.
+     * Only newer datatypes (such as Java8 Date/Time) support such resolution --
+     * older types (pre-Java8 <b>java.util.Date</b> etc) and Joda do not --
+     * and this setting <b>has no effect</b> on such types.
+     *<p>
+     * If disabled, standard millisecond timestamps are assumed.
+     * This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}.
+     *<p>
+     * Feature is enabled by default, to support most accurate time values possible.
+     * 
+     * @since 2.2
+     */
+    READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
+
+    /**
+     * Feature that specifies whether context provided {@link java.util.TimeZone}
+     * ({@link DeserializationContext#getTimeZone()} should be used to adjust Date/Time
+     * values on deserialization, even if value itself contains timezone information.
+     * If enabled, contextual <code>TimeZone</code> will essentially override any other
+     * TimeZone information; if disabled, it will only be used if value itself does not
+     * contain any TimeZone information.
+     * 
+     * @since 2.2
+     */
+    ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true),
+
+    /*
+    /******************************************************
+    /* Other
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether {@link ObjectReader} should
+     * try to eagerly fetch necessary {@link JsonDeserializer} when
+     * possible. This improves performance in cases where similarly
+     * configured {@link ObjectReader} instance is used multiple
+     * times; and should not significantly affect single-use cases.
+     *<p>
+     * Note that there should not be any need to normally disable this
+     * feature: only consider that if there are actual perceived problems.
+     *<p>
+     * Feature is enabled by default.
+     * 
+     * @since 2.1
+     */
+    EAGER_DESERIALIZER_FETCH(true)
+    
+    ;
+
+    private final boolean _defaultState;
+    
+    private DeserializationFeature(boolean defaultState) {
+        _defaultState = defaultState;
+    }
+
+    @Override
+    public boolean enabledByDefault() { return _defaultState; }
+
+    @Override
+    public int getMask() { return (1 << ordinal()); }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java
new file mode 100644
index 0000000..64d69de
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/InjectableValues.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.*;
+
+/**
+ * Abstract class that defines API for objects that provide value to
+ * "inject" during deserialization. An instance of this object
+ */
+public abstract class InjectableValues
+{
+    /**
+     * Method called to find value identified by id <code>valueId</code> to
+     * inject as value of specified property during deserialization, passing
+     * POJO instance in which value will be injected if it is available
+     * (will be available when injected via field or setter; not available
+     * when injected via constructor or factory method argument).
+     * 
+     * @param valueId Object that identifies value to inject; may be a simple
+     *   name or more complex identifier object, whatever provider needs
+     * @param ctxt Deserialization context
+     * @param forProperty Bean property in which value is to be injected
+     * @param beanInstance Bean instance that contains property to inject,
+     *    if available; null if bean has not yet been constructed.
+     */
+    public abstract Object findInjectableValue(Object valueId,
+            DeserializationContext ctxt, BeanProperty forProperty,
+            Object beanInstance);
+
+    /*
+    /**********************************************************
+    /* Standard implementation
+    /**********************************************************
+     */
+
+    /**
+     * Simple standard implementation which uses a simple Map to
+     * store values to inject, identified by simple String keys.
+     */
+    public static class Std
+        extends InjectableValues
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final Map<String,Object> _values;
+        
+        public Std() {
+            this(new HashMap<String,Object>());
+        }
+
+        public Std(Map<String,Object> values) {
+            _values = values;
+        }
+
+        public Std addValue(String key, Object value)
+        {
+            _values.put(key, value);
+            return this;
+        }
+
+        public Std addValue(Class<?> classKey, Object value)
+        {
+            _values.put(classKey.getName(), value);
+            return this;
+        }
+        
+        @Override
+        public Object findInjectableValue(Object valueId,
+                DeserializationContext ctxt, BeanProperty forProperty,
+                Object beanInstance)
+        {
+            if (!(valueId instanceof String)) {
+                String type = (valueId == null) ? "[null]" : valueId.getClass().getName();
+                throw new IllegalArgumentException("Unrecognized inject value id type ("+type+"), expecting String");
+            }
+            String key = (String) valueId;
+            Object ob = _values.get(key);
+            if (ob == null && !_values.containsKey(key)) {
+                throw new IllegalArgumentException("No injectable id with value '"+key+"' found (for property '"
+                        +forProperty.getName()+"')");
+            }
+            return ob;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JavaType.java b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
new file mode 100644
index 0000000..828666f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JavaType.java
@@ -0,0 +1,446 @@
+package com.fasterxml.jackson.databind;
+
+import java.lang.reflect.Modifier;
+
+import com.fasterxml.jackson.core.type.ResolvedType;
+
+/**
+ * Base class for type token classes used both to contain information
+ * and as keys for deserializers.
+ *<p>
+ * Instances can (only) be constructed by
+ * <code>com.fasterxml.jackson.databind.TypeFactory</code>.
+ *<p>
+ * Since 2.2 this implements {@link java.lang.reflect.Type} to allow
+ * it to be pushed through interfaces that only expose that type.
+ */
+public abstract class JavaType
+    extends ResolvedType
+    implements java.io.Serializable, // 2.1
+        java.lang.reflect.Type // 2.2
+{
+    private static final long serialVersionUID = 6774285981275451126L;
+
+    /**
+     * This is the nominal type-erased Class that would be close to the
+     * type represented (but not exactly type, due to type erasure: type
+     * instance may have more information on this).
+     * May be an interface or abstract class, so instantiation
+     * may not be possible.
+     */
+    protected final Class<?> _class;
+
+    protected final int _hashCode;
+
+    /**
+     * Optional handler (codec) that can be attached to indicate 
+     * what to use for handling (serializing, deserializing) values of
+     * this specific type.
+     *<p>
+     * Note: untyped (i.e. caller has to cast) because it is used for
+     * different kinds of handlers, with unrelated types.
+     */
+    protected final Object _valueHandler;
+
+    /**
+     * Optional handler that can be attached to indicate how to handle
+     * additional type metadata associated with this type.
+     *<p>
+     * Note: untyped (i.e. caller has to cast) because it is used for
+     * different kinds of handlers, with unrelated types.
+     */
+    protected final Object _typeHandler;
+
+    /**
+     * Whether entities defined with this type should be handled using
+     * static typing (as opposed to dynamic runtime type) or not.
+     * 
+     * @since 2.2
+     */
+    protected final boolean _asStatic;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @param raw "Raw" (type-erased) class for this type
+     * @param additionalHash Additional hash code to use, in addition
+     *   to hash code of the class name 
+     */
+    protected JavaType(Class<?> raw, int additionalHash,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        _class = raw;
+        _hashCode = raw.getName().hashCode() + additionalHash;
+        _valueHandler = valueHandler;
+        _typeHandler = typeHandler;
+        _asStatic = asStatic;
+    }
+    
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified type handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withTypeHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that its content type will have specified
+     * type handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withContentTypeHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified value handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withValueHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified content value handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withContentValueHandler(Object h);
+
+    /**
+     * Method that can be called to get a type instance that indicates
+     * that values of the type should be handled using "static typing" for purposes
+     * of serialization (as opposed to "dynamic" aka runtime typing):
+     * meaning that no runtime information is needed for determining serializers to use.
+     * The main use case is to allow forcing of specific root value serialization type,
+     * and specifically in resolving serializers for contained types (element types
+     * for arrays, Collections and Maps).
+     * 
+     * @since 2.2
+     */
+    public abstract JavaType withStaticTyping();
+    
+    /*
+    /**********************************************************
+    /* Type coercion fluent factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to do a "narrowing" conversions; that is,
+     * to return a type with a raw class that is assignable to the raw
+     * class of this type. If this is not possible, an
+     * {@link IllegalArgumentException} is thrown.
+     * If class is same as the current raw class, instance itself is
+     * returned.
+     */
+    public JavaType narrowBy(Class<?> subclass)
+    {
+        // First: if same raw class, just return this instance
+        if (subclass == _class) {
+            return this;
+        }
+        // Otherwise, ensure compatibility
+        _assertSubclass(subclass, _class);
+        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;
+    }
+
+    /**
+     * More efficient version of {@link #narrowBy}, called by
+     * internal framework in cases where compatibility checks
+     * are to be skipped.
+     */
+    public JavaType forcedNarrowBy(Class<?> subclass)
+    {
+        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;
+    }
+
+    /**
+     * Method that can be called to do a "widening" conversions; that is,
+     * to return a type with a raw class that could be assigned from this
+     * type.
+     * If such conversion is not possible, an
+     * {@link IllegalArgumentException} is thrown.
+     * If class is same as the current raw class, instance itself is
+     * returned.
+     */
+    public JavaType widenBy(Class<?> superclass)
+    {
+        // First: if same raw class, just return this instance
+        if (superclass == _class) {
+            return this;
+        }
+        // Otherwise, ensure compatibility
+        _assertSubclass(_class, superclass);
+        return _widen(superclass);
+    }
+
+    protected abstract JavaType _narrow(Class<?> subclass);
+
+    /**
+     *<p>
+     * Default implementation is just to call {@link #_narrow}, since
+     * underlying type construction is usually identical
+     */
+    protected JavaType _widen(Class<?> superclass) {
+        return _narrow(superclass);
+    }
+
+    public abstract JavaType narrowContentsBy(Class<?> contentClass);
+
+    public abstract JavaType widenContentsBy(Class<?> contentClass);
+    
+    /*
+    /**********************************************************
+    /* Implementation of ResolvedType API
+    /**********************************************************
+     */
+
+    @Override
+    public final Class<?> getRawClass() { return _class; }
+
+    /**
+     * Method that can be used to check whether this type has
+     * specified Class as its type erasure. Put another way, returns
+     * true if instantiation of this Type is given (type-erased) Class.
+     */
+    @Override
+    public final boolean hasRawClass(Class<?> clz) {
+        return _class == clz;
+    }
+
+    @Override
+    public boolean isAbstract() {
+        return Modifier.isAbstract(_class.getModifiers());
+    }
+
+    /**
+     * Convenience method for checking whether underlying Java type
+     * is a concrete class or not: abstract classes and interfaces
+     * are not.
+     */
+    @Override
+    public boolean isConcrete() {
+        int mod = _class.getModifiers();
+        if ((mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0) {
+            return true;
+        }
+        /* 19-Feb-2010, tatus: Holy mackarel; primitive types
+         *    have 'abstract' flag set...
+         */
+        if (_class.isPrimitive()) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isThrowable() {
+        return Throwable.class.isAssignableFrom(_class);
+    }
+
+    @Override
+    public boolean isArrayType() { return false; }
+
+    @Override
+    public final boolean isEnumType() { return _class.isEnum(); }
+
+    @Override
+    public final boolean isInterface() { return _class.isInterface(); }
+
+    @Override
+    public final boolean isPrimitive() { return _class.isPrimitive(); }
+
+    @Override
+    public final boolean isFinal() { return Modifier.isFinal(_class.getModifiers()); }
+
+    /**
+     * @return True if type represented is a container type; this includes
+     *    array, Map and Collection types.
+     */
+    @Override
+    public abstract boolean isContainerType();
+
+    /**
+     * @return True if type is either true {@link java.util.Collection} type,
+     *    or something similar (meaning it has at least one type parameter,
+     *    which describes type of contents)
+     */
+    @Override
+    public boolean isCollectionLikeType() { return false; }
+
+    /**
+     * @return True if type is either true {@link java.util.Map} type,
+     *    or something similar (meaning it has at least two type parameter;
+     *    first one describing key type, second value type)
+     */
+    @Override
+    public boolean isMapLikeType() { return false; }
+
+    /**
+     * Accessor for checking whether handlers for dealing with values of
+     * this type should use static typing (as opposed to dynamic typing).
+     * Note that while value of 'true' does mean that static typing is to
+     * be used, value of 'false' may still be overridden by other settings.
+     * 
+     * @since 2.2
+     */
+    public final boolean useStaticType() {
+        return _asStatic;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, type parameter access; pass-through
+    /**********************************************************
+     */
+
+    @Override
+    public boolean hasGenericTypes()
+    {
+        return containedTypeCount() > 0;
+    }
+
+    @Override
+    public JavaType getKeyType() { return null; }
+
+    @Override
+    public JavaType getContentType() { return null; }
+
+    @Override
+    public int containedTypeCount() { return 0; }
+
+    @Override
+    public JavaType containedType(int index) { return null; }
+
+    @Override
+    public String containedTypeName(int index) { return null; }
+
+    /*
+    /**********************************************************
+    /* Semi-public API, accessing handlers
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing value handler associated with this type, if any
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getValueHandler() { return (T) _valueHandler; }
+
+    /**
+     * Method for accessing type handler associated with this type, if any
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getTypeHandler() { return (T) _typeHandler; }
+
+    /*
+    /**********************************************************
+    /* Support for producing signatures
+    /**********************************************************
+     */
+    
+    //public abstract String toCanonical();
+
+    /**
+     * Method for accessing signature that contains generic
+     * type information, in form compatible with JVM 1.5
+     * as per JLS. It is a superset of {@link #getErasedSignature},
+     * in that generic information can be automatically removed
+     * if necessary (just remove outermost
+     * angle brackets along with content inside)
+     */
+    public String getGenericSignature() {
+        StringBuilder sb = new StringBuilder(40);
+        getGenericSignature(sb);
+        return sb.toString();        
+    }
+
+    /**
+     * 
+     * @param sb StringBuilder to append signature to
+     * 
+     * @return StringBuilder that was passed in; returned to allow
+     * call chaining
+     */
+    public abstract StringBuilder getGenericSignature(StringBuilder sb);
+    
+    /**
+     * Method for accessing signature without generic
+     * type information, in form compatible with all versions
+     * of JVM, and specifically used for type descriptions
+     * when generating byte code.
+     */
+    public String getErasedSignature() {
+        StringBuilder sb = new StringBuilder(40);
+        getErasedSignature(sb);
+        return sb.toString();
+    }
+
+    /**
+     * Method for accessing signature without generic
+     * type information, in form compatible with all versions
+     * of JVM, and specifically used for type descriptions
+     * when generating byte code.
+     * 
+     * @param sb StringBuilder to append signature to
+     * 
+     * @return StringBuilder that was passed in; returned to allow
+     * call chaining
+     */
+    public abstract StringBuilder getErasedSignature(StringBuilder sb);
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected void _assertSubclass(Class<?> subclass, Class<?> superClass)
+    {
+        if (!_class.isAssignableFrom(subclass)) {
+            throw new IllegalArgumentException("Class "+subclass.getName()+" is not assignable to "+_class.getName());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods; let's make them abstract to force override
+    /**********************************************************
+     */
+
+    @Override
+    public abstract String toString();
+
+    @Override
+    public abstract boolean equals(Object o);
+
+    @Override
+    public final int hashCode() { return _hashCode; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
new file mode 100644
index 0000000..0a2f9bb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonDeserializer.java
@@ -0,0 +1,271 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Abstract class that defines API used by {@link ObjectMapper} (and
+ * other chained {@link JsonDeserializer}s too) to deserialize Objects of
+ * arbitrary types from JSON, using provided {@link JsonParser}.
+ *<p>
+ * Custom deserializers should usually not directly extend this class,
+ * but instead extend {@link com.fasterxml.jackson.databind.deser.std.StdDeserializer}
+ * (or its subtypes like {@link com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer}).
+ *<p>
+ * If deserializer is an aggregate one -- meaning it delegates handling of some
+ * of its contents by using other deserializer(s) -- it typically also needs
+ * to implement {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer},
+ * which can locate dependant deserializers. This is important to allow dynamic
+ * overrides of deserializers; separate call interface is needed to separate
+ * resolution of dependant deserializers (which may have cyclic link back
+ * to deserializer itself, directly or indirectly).
+ *<p>
+ * In addition, to support per-property annotations (to configure aspects
+ * of deserialization on per-property basis), deserializers may want
+ * to implement 
+ * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer},
+ * which allows specialization of deserializers: call to
+ * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer#createContextual}
+ * is passed information on property, and can create a newly configured
+ * deserializer for handling that particular property.
+ *<p>
+ * If both
+ * {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer} and
+ * {@link com.fasterxml.jackson.databind.deser.ContextualDeserializer}
+ * are implemented, resolution of deserializers occurs before
+ * contextualization.
+ */
+public abstract class JsonDeserializer<T>
+{
+    /*
+    /**********************************************************
+    /* Main deserialization methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to ask implementation to deserialize
+     * JSON content into the value type this serializer handles.
+     * Returned instance is to be constructed by method itself.
+     *<p>
+     * Pre-condition for this method is that the parser points to the
+     * first event that is part of value to deserializer (and which 
+     * is never JSON 'null' literal, more on this below): for simple
+     * types it may be the only value; and for structured types the
+     * Object start marker.
+     * Post-condition is that the parser will point to the last
+     * event that is part of deserialized value (or in case deserialization
+     * fails, event that was not recognized or usable, which may be
+     * the same event as the one it pointed to upon call).
+     *<p>
+     * Note that this method is never called for JSON null literal,
+     * and thus deserializers need (and should) not check for it.
+     *
+     * @param jp Parsed used for reading JSON content
+     * @param ctxt Context that can be used to access information about
+     *   this deserialization activity.
+     *
+     * @return Deserializer value
+     */
+    public abstract T deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Alternate deserialization method (compared to the most commonly
+     * used, {@link #deserialize(JsonParser, DeserializationContext)}),
+     * which takes in initialized value instance, to be
+     * configured and/or populated by deserializer.
+     * Method is not necessarily used for all supported types; most commonly
+     * it is used
+     * for Collections and Maps.
+     *<p>
+     * Default implementation just throws
+     * {@link UnsupportedOperationException}, to indicate that types
+     * that do not explicitly add support do not necessarily support
+     * update-existing-value operation (esp. immutable types)
+     */
+    public T deserialize(JsonParser jp, DeserializationContext ctxt,
+                         T intoValue)
+        throws IOException, JsonProcessingException
+    {
+        throw new UnsupportedOperationException("Can not update object of type "
+                +intoValue.getClass().getName()+" (by deserializer of type "+getClass().getName()+")");
+    }
+
+    /**
+     * Deserialization called when type being deserialized is defined to
+     * contain additional type identifier, to allow for correctly
+     * instantiating correct subtype. This can be due to annotation on
+     * type (or its supertype), or due to global settings without
+     * annotations.
+     *<p>
+     * Default implementation may work for some types, but ideally subclasses
+     * should not rely on current default implementation.
+     * Implementation is mostly provided to avoid compilation errors with older
+     * code.
+     * 
+     * @param typeDeserializer Deserializer to use for handling type information
+     */
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // We could try calling 
+        return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+    }
+
+    /*
+    /**********************************************************
+    /* Fluent factory methods for constructing decorated versions
+    /**********************************************************
+     */
+
+    /**
+     * Method that will return deserializer instance that is able
+     * to handle "unwrapped" value instances
+     * If no unwrapped instance can be constructed, will simply
+     * return this object as-is.
+     *<p>
+     * Default implementation just returns 'this'
+     * indicating that no unwrapped variant exists
+     */
+    public JsonDeserializer<T> unwrappingDeserializer(NameTransformer unwrapper) {
+        return this;
+    }
+
+    /**
+     * Method that can be called to try to replace deserializer this deserializer
+     * delegates calls to. If not supported (either this deserializer does not
+     * delegate anything; or it does not want any changes), should either
+     * throw {@link UnsupportedOperationException} (if operation does not
+     * make sense or is not allowed); or return this deserializer as is.
+     * 
+     * @since 2.1
+     */
+    public JsonDeserializer<?> replaceDelegatee(JsonDeserializer<?> delegatee) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /*
+    /**********************************************************
+    /* Other accessors
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to determine value to be used for
+     * representing null values (values deserialized when JSON token
+     * is {@link JsonToken#VALUE_NULL}). Usually this is simply
+     * Java null, but for some types (especially primitives) it may be
+     * necessary to use non-null values.
+     *<p>
+     * Note that deserializers are allowed to call this just once and
+     * then reuse returned value; that is, method is not guaranteed to
+     * be called once for each conversion.
+     *<p>
+     * Default implementation simply returns null.
+     */
+    public T getNullValue() { return null; }
+
+    /**
+     * 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
+     * is usually simply Java null), but it can be overridden
+     * for types. Or, if type should never be converted from empty
+     * String, method can also throw an exception.
+     *<p>
+     * Default implementation simple calls {@link #getNullValue} and
+     * returns value.
+     */
+    public T getEmptyValue() { return getNullValue(); }
+
+    /**
+     * Method that will
+     * either return null to indicate that type being deserializers
+     * has no concept of properties; or a collection of identifiers
+     * for which <code>toString</code> will give external property
+     * name.
+     * This is only to be used for error reporting and diagnostics
+     * purposes (most commonly, to accompany "unknown property"
+     * exception).
+     * 
+     * @since 2.0
+     */
+    public Collection<Object> getKnownPropertyNames() {
+        return null;
+    }
+    
+    /**
+     * Method called to see if deserializer instance is cachable and
+     * usable for other properties of same type (type for which instance
+     * was created).
+     *<p>
+     * Note that cached instances are still resolved on per-property basis,
+     * if instance implements {@link com.fasterxml.jackson.databind.deser.ResolvableDeserializer}:
+     * cached instance is just as the base. This means that in most cases it is safe to
+     * cache instances; however, it only makes sense to cache instances
+     * if instantiation is expensive, or if instances are heavy-weight.
+     *<p>
+     * Default implementation returns false, to indicate that no caching
+     * is done.
+     */
+    public boolean isCachable() { return false; }
+
+    /**
+     * 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
+     * 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
+     * configuration of instance (which is based on type, and referring property).
+     * 
+     * @return ObjectIdReader used for resolving possible Object Identifier
+     *    value, instead of full value serialization, if deserializer can do that;
+     *    null if no Object Id is expected.
+     * 
+     * @since 2.0
+     */
+    public ObjectIdReader getObjectIdReader() { return null; }
+
+    /**
+     * Accessor that can be used to determine if this deserializer uses
+     * another deserializer for actual deserialization, by delegating
+     * calls. If so, will return immediate delegate (which itself may
+     * delegate to further deserializers); otherwise will return null.
+     * 
+     * @return Deserializer this deserializer delegates calls to, if null;
+     *   null otherwise.
+     * 
+     * @since 2.1
+     */
+    public JsonDeserializer<?> getDelegatee() {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * This marker class is only to be used with annotations, to
+     * indicate that <b>no deserializer is configured</b>.
+     *<p>
+     * Specifically, this class is to be used as the marker for
+     * annotation {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}
+     */
+    public abstract static class None extends JsonDeserializer<Object> {
+        private None() { } // not to be instantiated
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
new file mode 100644
index 0000000..98d7bdf
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonMappingException.java
@@ -0,0 +1,370 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Checked exception used to signal fatal problems with mapping of
+ * content.
+ *<p>
+ * One additional feature is the ability to denote relevant path
+ * of references (during serialization/deserialization) to help in
+ * troubleshooting.
+ */
+public class JsonMappingException
+    extends JsonProcessingException
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Let's limit length of reference chain, to limit damage in cases
+     * of infinite recursion.
+     */
+    final static int MAX_REFS_TO_LIST = 1000;
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Simple bean class used to contain references. References
+     * can be added to indicate execution/reference path that
+     * lead to the problem that caused this exception to be
+     * thrown.
+     */
+    public static class Reference implements Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Object through which reference was resolved. Can be either
+         * actual instance (usually the case for serialization), or
+         * Class (usually the case for deserialization).
+         */
+        protected Object _from;
+
+        /**
+         * Name of field (for beans) or key (for Maps) that is part
+         * of the reference. May be null for Collection types (which
+         * generally have {@link #_index} defined), or when resolving
+         * Map classes without (yet) having an instance to operate on.
+         */
+        protected String _fieldName;
+
+        /**
+         * Index within a {@link Collection} instance that contained
+         * the reference; used if index is relevant and available.
+         * If either not applicable, or not available, -1 is used to
+         * denote "not known".
+         */
+        protected int _index = -1;
+
+        /**
+         * Default constructor for deserialization/sub-classing purposes
+         */
+        protected Reference() { }
+
+        public Reference(Object from) { _from = from; }
+
+        public Reference(Object from, String fieldName) {
+            _from = from;
+            if (fieldName == null) {
+                throw new NullPointerException("Can not pass null fieldName");
+            }
+            _fieldName = fieldName;
+        }
+
+        public Reference(Object from, int index) {
+            _from = from;
+            _index = index;
+        }
+
+        public void setFrom(Object o) { _from = o; }
+        public void setFieldName(String n) { _fieldName = n; }
+        public void setIndex(int ix) { _index = ix; }
+
+        public Object getFrom() { return _from; }
+        public String getFieldName() { return _fieldName; }
+        public int getIndex() { return _index; }
+
+        @Override public String toString() {
+            StringBuilder sb = new StringBuilder();
+            Class<?> cls = (_from instanceof Class<?>) ?
+                ((Class<?>)_from) : _from.getClass();
+            /* Hmmh. Although Class.getName() is mostly ok, it does look
+             * butt-ugly for arrays. So let's use getSimpleName() instead;
+             * but have to prepend package name too.
+             */
+            Package pkg = cls.getPackage();
+            if (pkg != null) {
+                sb.append(pkg.getName());
+                sb.append('.');
+            }
+            sb.append(cls.getSimpleName());
+            sb.append('[');
+            if (_fieldName != null) {
+                sb.append('"');
+                sb.append(_fieldName);
+                sb.append('"');
+            } else if (_index >= 0) {
+                sb.append(_index);
+            } else {
+                sb.append('?');
+            }
+            sb.append(']');
+            return sb.toString();
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* State/configuration
+    /**********************************************************
+     */
+
+    /**
+     * Path through which problem that triggering throwing of
+     * this exception was reached.
+     */
+    protected LinkedList<Reference> _path;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public JsonMappingException(String msg)
+    {
+        super(msg);
+    }
+
+    public JsonMappingException(String msg, Throwable rootCause)
+    {
+        super(msg, rootCause);
+    }
+
+    public JsonMappingException(String msg, JsonLocation loc)
+    {
+        super(msg, loc);
+    }
+
+    public JsonMappingException(String msg, JsonLocation loc, Throwable rootCause)
+    {
+        super(msg, loc, rootCause);
+    }
+
+    public static JsonMappingException from(JsonParser jp, String msg)
+    {
+        return new JsonMappingException(msg, ((jp == null) ? null : jp.getTokenLocation()));
+    }
+
+    public static JsonMappingException from(JsonParser jp, String msg,
+            Throwable problem)
+    {
+        return new JsonMappingException(msg, ((jp == null) ? null : jp.getTokenLocation()), problem);
+    }
+    
+    /**
+     * Factory method used when "upgrading" an {@link IOException} into
+     * {@link JsonMappingException}: usually only needed to comply with
+     * a signature.
+     * 
+     * @since 2.1
+     */
+    public static JsonMappingException fromUnexpectedIOE(IOException src)
+    {
+        return new JsonMappingException("Unexpected IOException (of type "
+                +src.getClass().getName()+"): "+src.getMessage(), (JsonLocation)null, src);
+    }
+    
+    /**
+     * Method that can be called to either create a new JsonMappingException
+     * (if underlying exception is not a JsonMappingException), or augment
+     * given exception with given path/reference information.
+     *
+     * This version of method is called when the reference is through a
+     * non-indexed object, such as a Map or POJO/bean.
+     */
+    public static JsonMappingException wrapWithPath(Throwable src, Object refFrom,
+                                                    String refFieldName)
+    {
+        return wrapWithPath(src, new Reference(refFrom, refFieldName));
+    }
+
+    /**
+     * Method that can be called to either create a new JsonMappingException
+     * (if underlying exception is not a JsonMappingException), or augment
+     * given exception with given path/reference information.
+     *
+     * This version of method is called when the reference is through an
+     * index, which happens with arrays and Collections.
+     */
+    public static JsonMappingException wrapWithPath(Throwable src, Object refFrom,
+                                                    int index)
+    {
+        return wrapWithPath(src, new Reference(refFrom, index));
+    }
+
+    /**
+     * Method that can be called to either create a new JsonMappingException
+     * (if underlying exception is not a JsonMappingException), or augment
+     * given exception with given path/reference information.
+     */
+    public static JsonMappingException wrapWithPath(Throwable src, Reference ref)
+    {
+        JsonMappingException jme;
+        if (src instanceof JsonMappingException) {
+            jme = (JsonMappingException) src;
+        } else {
+            String msg = src.getMessage();
+            /* Related to [JACKSON-62], let's use a more meaningful placeholder
+             * if all we have is null
+             */
+            if (msg == null || msg.length() == 0) {
+                msg = "(was "+src.getClass().getName()+")";
+            }
+            jme = new JsonMappingException(msg, null, src);
+        }
+        jme.prependPath(ref);
+        return jme;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors/mutators
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing full structural path within type hierarchy
+     * down to problematic property.
+     */
+    public List<Reference> getPath()
+    {
+        if (_path == null) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(_path);
+    }
+
+    /**
+     * Method for accesing description of path that lead to the
+     * problem that triggered this exception
+     */
+    public String getPathReference()
+    {
+        return getPathReference(new StringBuilder()).toString();
+    }
+
+    public StringBuilder getPathReference(StringBuilder sb)
+    {
+        _appendPathDesc(sb);
+        return sb;
+    }
+    
+    /**
+     * Method called to prepend a reference information in front of
+     * current path
+     */
+    public void prependPath(Object referrer, String fieldName)
+    {
+        Reference ref = new Reference(referrer, fieldName);
+        prependPath(ref);
+    }
+    /**
+     * Method called to prepend a reference information in front of
+     * current path
+     */
+    public void prependPath(Object referrer, int index)
+    {
+        Reference ref = new Reference(referrer, index);
+        prependPath(ref);
+    }
+
+    public void prependPath(Reference r)
+    {
+        if (_path == null) {
+            _path = new LinkedList<Reference>();
+        }
+        /* Also: let's not increase without bounds. Could choose either
+         * head or tail; tail is easier (no need to ever remove), as
+         * well as potentially more useful so let's use it:
+         */
+        if (_path.size() < MAX_REFS_TO_LIST) {
+            _path.addFirst(r);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    @Override
+    public String getLocalizedMessage() {
+        return _buildMessage();
+    }
+    
+    /**
+     * Method is overridden so that we can properly inject description
+     * of problem path, if such is defined.
+     */
+    @Override
+    public String getMessage() {
+        return _buildMessage();
+    }
+
+    protected String _buildMessage()
+    {
+        /* First: if we have no path info, let's just use parent's
+         * definition as is
+         */
+        String msg = super.getMessage();
+        if (_path == null) {
+            return msg;
+        }
+        StringBuilder sb = (msg == null) ? new StringBuilder() : new StringBuilder(msg);
+        /* 18-Feb-2009, tatu: initially there was a linefeed between
+         *    message and path reference; but unfortunately many systems
+         *   (loggers, junit) seem to assume linefeeds are only added to
+         *   separate stack trace.
+         */
+        sb.append(" (through reference chain: ");
+        sb = getPathReference(sb);
+        sb.append(')');
+        return sb.toString();
+    }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getName()+": "+getMessage();
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected void _appendPathDesc(StringBuilder sb)
+    {
+        if (_path == null) {
+            return;
+        }
+        Iterator<Reference> it = _path.iterator();
+        while (it.hasNext()) {
+            sb.append(it.next().toString());
+            if (it.hasNext()) {
+                sb.append("->");
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonNode.java b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
new file mode 100644
index 0000000..2f92353
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonNode.java
@@ -0,0 +1,845 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.databind.node.JsonNodeType;
+import com.fasterxml.jackson.databind.util.EmptyIterator;
+
+/**
+ * Base class for all JSON nodes, which form the basis of JSON
+ * Tree Model that Jackson implements.
+ * One way to think of these nodes is to consider them
+ * similar to DOM nodes in XML DOM trees.
+ *<p>
+ * As a general design rule, most accessors ("getters") are included
+ * in this base class, to allow for traversing structure without
+ * type casts. Most mutators, however, need to be accessed through
+ * specific sub-classes (such as <code>ObjectNode</code>
+ * and <code>ArrayNode</code>).
+ * This seems sensible because proper type
+ * information is generally available when building or modifying
+ * trees, but less often when reading a tree (newly built from
+ * parsed JSON content).
+ *<p>
+ * Actual concrete sub-classes can be found from package
+ * {@link com.fasterxml.jackson.databind.node}.
+ */
+public abstract class JsonNode
+    implements TreeNode, Iterable<JsonNode>
+{
+    /*
+    /**********************************************************
+    /* Construction, related
+    /**********************************************************
+     */
+    
+    protected JsonNode() { }
+
+    /**
+     * Method that can be called to get a node that is guaranteed
+     * not to allow changing of this node through mutators on
+     * this node or any of its children.
+     * This means it can either make a copy of this node (and all
+     * mutable children and grand children nodes), or node itself
+     * if it is immutable.
+     *<p>
+     * Note: return type is guaranteed to have same type as the
+     * node method is called on; which is why method is declared
+     * with local generic type.
+     * 
+     * @since 2.0
+     * 
+     * @return Node that is either a copy of this node (and all non-leaf
+     *    children); or, for immutable leaf nodes, node itself.
+     */
+    public abstract <T extends JsonNode> T deepCopy();
+
+    /*
+    /**********************************************************
+    /* TreeNode implementation
+    /**********************************************************
+     */
+
+//    public abstract JsonToken asToken();
+
+//    public abstract JsonParser.NumberType numberType();
+
+//    public abstract JsonParser traverse();
+    
+    @Override
+    public int size() { return 0; }
+
+    @Override
+    public final boolean isValueNode()
+    {
+        switch (getNodeType()) {
+            case ARRAY: case OBJECT: case MISSING:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    @Override
+    public final boolean isContainerNode() {
+        final JsonNodeType type = getNodeType();
+        return type == JsonNodeType.OBJECT || type == JsonNodeType.ARRAY;
+    }
+
+    @Override
+    public final boolean isMissingNode() {
+        return getNodeType() == JsonNodeType.MISSING;
+    }
+
+    @Override
+    public final boolean isArray() {
+        return getNodeType() == JsonNodeType.ARRAY;
+    }
+
+    @Override
+    public final boolean isObject() {
+        return getNodeType() == JsonNodeType.OBJECT;
+    }
+
+    /**
+     * Method for accessing value of the specified element of
+     * an array node. For other nodes, null is always returned.
+     *<p>
+     * For array nodes, index specifies
+     * exact location within array and allows for efficient iteration
+     * over child elements (underlying storage is guaranteed to
+     * be efficiently indexable, i.e. has random-access to elements).
+     * If index is less than 0, or equal-or-greater than
+     * <code>node.size()</code>, null is returned; no exception is
+     * thrown for any index.
+     *<p>
+     * NOTE: if the element value has been explicitly set as <code>null</code>
+     * (which is different from removal!),
+     * a {@link com.fasterxml.jackson.databind.node.NullNode} will be returned,
+     * not null.
+     *
+     * @return Node that represent value of the specified element,
+     *   if this node is an array and has specified element.
+     *   Null otherwise.
+     */
+    @Override
+    public abstract JsonNode get(int index);
+
+    /**
+     * Method for accessing value of the specified field of
+     * an object node. If this node is not an object (or it
+     * does not have a value for specified field name), or
+     * if there is no field with such name, null is returned.
+     *<p>
+     * NOTE: if the property value has been explicitly set as <code>null</code>
+     * (which is different from removal!),
+     * a {@link com.fasterxml.jackson.databind.node.NullNode} will be returned,
+     * not null.
+     *
+     * @return Node that represent value of the specified field,
+     *   if this node is an object and has value for the specified
+     *   field. Null otherwise.
+     */
+    @Override
+    public JsonNode get(String fieldName) { return null; }
+    /**
+     * This method is similar to {@link #get(String)}, except
+     * that instead of returning null if no such value exists (due
+     * to this node not being an object, or object not having value
+     * for the specified field),
+     * a "missing node" (node that returns true for
+     * {@link #isMissingNode}) will be returned. This allows for
+     * convenient and safe chained access via path calls.
+     */
+
+    @Override
+    public abstract JsonNode path(String fieldName);
+
+    /**
+     * This method is similar to {@link #get(int)}, except
+     * that instead of returning null if no such element exists (due
+     * to index being out of range, or this node not being an array),
+     * a "missing node" (node that returns true for
+     * {@link #isMissingNode}) will be returned. This allows for
+     * convenient and safe chained access via path calls.
+     */
+    @Override
+    public abstract JsonNode path(int index);
+
+    @Override
+    public Iterator<String> fieldNames() {
+        return EmptyIterator.instance();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, type introspection
+    /**********************************************************
+     */
+
+    // // First high-level division between values, containers and "missing"
+
+    /**
+     * Return the type of this node
+     *
+     * @return the node type as a {@link JsonNodeType} enum value
+     *
+     * @since 2.2
+     */
+    public abstract JsonNodeType getNodeType();
+
+    /**
+     * Method that can be used to check if the node is a wrapper
+     * for a POJO ("Plain Old Java Object" aka "bean".
+     * Returns true only for
+     * instances of <code>POJONode</code>.
+     *
+     * @return True if this node wraps a POJO
+     */
+    public final boolean isPojo() {
+        return getNodeType() == JsonNodeType.POJO;
+    }
+
+    /**
+     * @return True if this node represents a numeric JSON value
+     */
+    public final boolean isNumber() {
+        return getNodeType() == JsonNodeType.NUMBER;
+    }
+
+    /**
+     * 
+     * @return True if this node represents an integral (integer)
+     *   numeric JSON value
+     */
+    public boolean isIntegralNumber() { return false; }
+
+    /**
+     * @return True if this node represents a non-integral
+     *   numeric JSON value
+     */
+    public boolean isFloatingPointNumber() { return false; }
+
+    /**
+     * Method that can be used to check whether contained value
+     * is a number represented as Java <code>short</code>.
+     * Note, however, that even if this method returns false, it
+     * is possible that conversion would be possible from other numeric
+     * types -- to check if this is possible, use
+     * {@link #canConvertToInt()} instead.
+     * 
+     * @return True if the value contained by this node is stored as Java short
+     */
+    public boolean isShort() { return false; }
+
+    /**
+     * Method that can be used to check whether contained value
+     * is a number represented as Java <code>int</code>.
+     * Note, however, that even if this method returns false, it
+     * is possible that conversion would be possible from other numeric
+     * types -- to check if this is possible, use
+     * {@link #canConvertToInt()} instead.
+     * 
+     * @return True if the value contained by this node is stored as Java int
+     */
+    public boolean isInt() { return false; }
+
+    /**
+     * Method that can be used to check whether contained value
+     * is a number represented as Java <code>long</code>.
+     * Note, however, that even if this method returns false, it
+     * is possible that conversion would be possible from other numeric
+     * types -- to check if this is possible, use
+     * {@link #canConvertToInt()} instead.
+     * 
+     * @return True if the value contained by this node is stored as Java <code>long</code>
+     */
+    public boolean isLong() { return false; }
+
+    /**
+     * @since 2.2
+     */
+    public boolean isFloat() { return false; }
+
+    public boolean isDouble() { return false; }
+    public boolean isBigDecimal() { return false; }
+    public boolean isBigInteger() { return false; }
+
+    /**
+     * Method that checks whether this node represents basic JSON String
+     * value.
+     */
+    public final boolean isTextual() {
+        return getNodeType() == JsonNodeType.STRING;
+    }
+
+    /**
+     * Method that can be used to check if this node was created from
+     * JSON boolean value (literals "true" and "false").
+     */
+    public final boolean isBoolean() {
+        return getNodeType() == JsonNodeType.BOOLEAN;
+    }
+
+    /**
+     * Method that can be used to check if this node was created from
+     * JSON literal null value.
+     */
+    public final boolean isNull() {
+        return getNodeType() == JsonNodeType.NULL;
+    }
+
+    /**
+     * Method that can be used to check if this node represents
+     * binary data (Base64 encoded). Although this will be externally
+     * written as JSON String value, {@link #isTextual} will
+     * return false if this method returns true.
+     *
+     * @return True if this node represents base64 encoded binary data
+     */
+    public final boolean isBinary() {
+        return getNodeType() == JsonNodeType.BINARY;
+    }
+
+    /**
+     * Method that can be used to check whether this node is a numeric
+     * node ({@link #isNumber} would return true) AND its value fits
+     * within Java's 32-bit signed integer type, <code>int</code>.
+     * Note that floating-point numbers are convertible if the integral
+     * part fits without overflow (as per standard Java coercion rules)
+     * 
+     * @since 2.0
+     */
+    public boolean canConvertToInt() { return false; }
+
+    /**
+     * Method that can be used to check whether this node is a numeric
+     * node ({@link #isNumber} would return true) AND its value fits
+     * within Java's 64-bit signed integer type, <code>long</code>.
+     * Note that floating-point numbers are convertible if the integral
+     * part fits without overflow (as per standard Java coercion rules)
+     * 
+     * @since 2.0
+     */
+    public boolean canConvertToLong() { return false; }
+    
+    /*
+    /**********************************************************
+    /* Public API, straight value access
+    /**********************************************************
+     */
+
+    /**
+     * Method to use for accessing String values.
+     * Does <b>NOT</b> do any conversions for non-String value nodes;
+     * for non-String values (ones for which {@link #isTextual} returns
+     * false) null will be returned.
+     * For String values, null is never returned (but empty Strings may be)
+     *
+     * @return Textual value this node contains, iff it is a textual
+     *   JSON node (comes from JSON String value entry)
+     */
+    public String textValue() { return null; }
+
+    /**
+     * Method to use for accessing binary content of binary nodes (nodes
+     * for which {@link #isBinary} returns true); or for Text Nodes
+     * (ones for which {@link #textValue} returns non-null value),
+     * to read decoded base64 data.
+     * For other types of nodes, returns null.
+     *
+     * @return Binary data this node contains, iff it is a binary
+     *   node; null otherwise
+     */
+    public byte[] binaryValue() throws IOException {
+        return null;
+    }
+
+    /**
+     * Method to use for accessing JSON boolean values (value
+     * literals 'true' and 'false').
+     * For other types, always returns false.
+     *
+     * @return Textual value this node contains, iff it is a textual
+     *   json node (comes from JSON String value entry)
+     */
+    public boolean booleanValue() { return false; }
+
+    /**
+     * Returns numeric value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true); otherwise
+     * returns null
+     *
+     * @return Number value this node contains, if any (null for non-number
+     *   nodes).
+     */
+    public Number numberValue() { return null; }
+
+    /**
+     * Returns 16-bit short value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.
+     * For floating-point numbers, value is truncated using default
+     * Java coercion, similar to how cast from double to short operates.
+     *
+     * @return Short value this node contains, if any; 0 for non-number
+     *   nodes.
+     */
+    public short shortValue() { return 0; }
+
+    /**
+     * Returns integer value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.
+     * For floating-point numbers, value is truncated using default
+     * Java coercion, similar to how cast from double to int operates.
+     *
+     * @return Integer value this node contains, if any; 0 for non-number
+     *   nodes.
+     */
+    public int intValue() { return 0; }
+
+    /**
+     * Returns 64-bit long value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.
+     * For floating-point numbers, value is truncated using default
+     * Java coercion, similar to how cast from double to long operates.
+     *
+     * @return Long value this node contains, if any; 0 for non-number
+     *   nodes.
+     */
+    public long longValue() { return 0L; }
+    
+    /**
+     * Returns 32-bit floating value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.0.
+     * For integer values, conversion is done using coercion; this means
+     * that an overflow is possible for `long` values
+     *
+     * @return 32-bit float value this node contains, if any; 0.0 for non-number nodes.
+     *
+     * @since 2.2
+     */
+    public float floatValue() { return 0.0f; }
+
+    /**
+     * Returns 64-bit floating point (double) value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.0.
+     * For integer values, conversion is done using coercion; this may result
+     * in overflows with {@link BigInteger} values.
+     *
+     * @return 64-bit double value this node contains, if any; 0.0 for non-number nodes.
+     *
+     * @since 2.2
+     */
+    public double doubleValue() { return 0.0; }
+
+    public BigDecimal decimalValue() { return BigDecimal.ZERO; }
+    public BigInteger bigIntegerValue() { return BigInteger.ZERO; }
+    
+    /*
+    /**********************************************************
+    /* Public API, value access with conversion(s)/coercion(s)
+    /**********************************************************
+     */
+
+    /**
+     * Method that will return a valid String representation of
+     * the container value, if the node is a value node
+     * (method {@link #isValueNode} returns true),
+     * otherwise empty String.
+     */
+    public abstract String asText();
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * 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
+     * like Objects and Arrays),
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public int asInt() {
+        return asInt(0);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * 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
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public int asInt(int defaultValue) {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * 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
+     * like Objects and Arrays),
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public long asLong() {
+        return asLong(0L);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * 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
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public long asLong(long defaultValue) {
+        return defaultValue;
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * 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
+     * like Objects and Arrays),
+     * default value of <b>0.0</b> will be returned; no exceptions are thrown.
+     */
+    public double asDouble() {
+        return asDouble(0.0);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * 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
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public double asDouble(double defaultValue) {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 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
+     * like Objects and Arrays),
+     * default value of <b>false</b> will be returned; no exceptions are thrown.
+     */
+    public boolean asBoolean() {
+        return asBoolean(false);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 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
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public boolean asBoolean(boolean defaultValue) {
+        return defaultValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, value find / existence check methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that allows checking whether this node is JSON Object node
+     * and contains value for specified property. If this is the case
+     * (including properties with explicit null values), returns true;
+     * otherwise returns false.
+     *<p>
+     * This method is equivalent to:
+     *<pre>
+     *   node.get(fieldName) != null
+     *</pre>
+     * (since return value of get() is node, not value node contains)
+     *<p>
+     * NOTE: when explicit <code>null</code> values are added, this
+     * method will return <code>true</code> for such properties.
+     *
+     * @param fieldName Name of element to check
+     * 
+     * @return True if this node is a JSON Object node, and has a property
+     *   entry with specified name (with any value, including null value)
+     */
+    public boolean has(String fieldName) {
+        return get(fieldName) != null;
+    }
+
+    /**
+     * Method that allows checking whether this node is JSON Array node
+     * and contains a value for specified index
+     * If this is the case
+     * (including case of specified indexing having null as value), returns true;
+     * otherwise returns false.
+     *<p>
+     * Note: array element indexes are 0-based.
+     *<p>
+     * This method is equivalent to:
+     *<pre>
+     *   node.get(index) != null
+     *</pre>
+     *<p>
+     * NOTE: this method will return <code>true</code> for explicitly added
+     * null values.
+     *
+     * @param index Index to check
+     * 
+     * @return True if this node is a JSON Object node, and has a property
+     *   entry with specified name (with any value, including null value)
+     */
+    public boolean has(int index) {
+        return get(index) != null;
+    }
+
+    /**
+     * Method that is similar to {@link #has(String)}, but that will
+     * return <code>false</code> for explicitly added nulls.
+     *<p>
+     * This method is functionally equivalent to:
+     *<pre>
+     *   node.get(fieldName) != null && !node.get(fieldName).isNull()
+     *</pre>
+     * 
+     * @since 2.1
+     */
+    public boolean hasNonNull(String fieldName) {
+        JsonNode n = get(fieldName);
+        return (n != null) && !n.isNull();
+    }
+
+    /**
+     * Method that is similar to {@link #has(int)}, but that will
+     * return <code>false</code> for explicitly added nulls.
+     *<p>
+     * This method is equivalent to:
+     *<pre>
+     *   node.get(index) != null && !node.get(index).isNull()
+     *</pre>
+     * 
+     * @since 2.1
+     */
+    public boolean hasNonNull(int index) {
+        JsonNode n = get(index);
+        return (n != null) && !n.isNull();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, container access
+    /**********************************************************
+     */
+
+    /**
+     * Same as calling {@link #elements}; implemented so that
+     * convenience "for-each" loop can be used for looping over elements
+     * of JSON Array constructs.
+     */
+    @Override
+    public final Iterator<JsonNode> iterator() { return elements(); }
+
+    /**
+     * Method for accessing all value nodes of this Node, iff
+     * this node is a JSON Array or Object node. In case of Object node,
+     * field names (keys) are not included, only values.
+     * For other types of nodes, returns empty iterator.
+     */
+    public Iterator<JsonNode> elements() {
+        return EmptyIterator.instance();
+    }
+
+    /**
+     * @return Iterator that can be used to traverse all key/value pairs for
+     *   object nodes; empty iterator (no contents) for other types
+     */
+    public Iterator<Map.Entry<String, JsonNode>> fields() {
+        return EmptyIterator.instance();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, find methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method for finding a JSON Object field with specified name in this
+     * node or its child nodes, and returning value it has.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public abstract JsonNode findValue(String fieldName);
+
+    /**
+     * Method for finding JSON Object fields with specified name, and returning
+     * found ones as a List. Note that sub-tree search ends if a field is found,
+     * so possible children of result nodes are <b>not</b> included.
+     * If no matching fields are found in this node or its descendants, returns
+     * an empty List.
+     * 
+     * @param fieldName Name of field to look for
+     */
+    public final List<JsonNode> findValues(String fieldName)
+    {
+        List<JsonNode> result = findValues(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    /**
+     * Similar to {@link #findValues}, but will additionally convert
+     * values into Strings, calling {@link #asText}.
+     */
+    public final List<String> findValuesAsText(String fieldName)
+    {
+        List<String> result = findValuesAsText(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+    
+    /**
+     * Method similar to {@link #findValue}, but that will return a
+     * "missing node" instead of null if no field is found. Missing node
+     * is a specific kind of node for which {@link #isMissingNode}
+     * returns true; and all value access methods return empty or
+     * missing value.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found; or if not found, a
+     *    "missing node" (non-null instance that has no value)
+     */
+    public abstract JsonNode findPath(String fieldName);
+    
+    /**
+     * Method for finding a JSON Object that contains specified field,
+     * within this node or its descendants.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public abstract JsonNode findParent(String fieldName);
+
+    /**
+     * Method for finding a JSON Object that contains specified field,
+     * within this node or its descendants.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public final List<JsonNode> findParents(String fieldName)
+    {
+        List<JsonNode> result = findParents(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    public abstract List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar);
+    public abstract List<String> findValuesAsText(String fieldName, List<String> foundSoFar);
+    public abstract List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar);
+
+    /*
+    /**********************************************************
+    /* Public API, path handling
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called on Object nodes, to access a property
+     * that has Object value; or if no such property exists, to create,
+     * add and return such Object node.
+     * If the node method is called on is not Object node,
+     * or if property exists and has value that is not Object node,
+     * {@link UnsupportedOperationException} is thrown
+     */
+    public JsonNode with(String propertyName) {
+        throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+                +getClass().getName()+"), can not call with() on it");
+    }
+
+    /**
+     * Method that can be called on Object nodes, to access a property
+     * that has <code>Array</code> value; or if no such property exists, to create,
+     * add and return such Array node.
+     * If the node method is called on is not Object node,
+     * or if property exists and has value that is not Array node,
+     * {@link UnsupportedOperationException} is thrown
+     */
+    public JsonNode withArray(String propertyName) {
+        throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+                +getClass().getName()+"), can not call withArray() on it");
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+    
+    /**
+     *<p>
+     * Note: marked as abstract to ensure all implementation
+     * classes define it properly.
+     */
+    @Override
+    public abstract String toString();
+
+    /**
+     * Equality for node objects is defined as full (deep) value
+     * equality. This means that it is possible to compare complete
+     * JSON trees for equality by comparing equality of root nodes.
+     *<p>
+     * Note: marked as abstract to ensure all implementation
+     * classes define it properly and not rely on definition
+     * from {@link java.lang.Object}.
+     */
+    @Override
+    public abstract boolean equals(Object o);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java
new file mode 100644
index 0000000..073a6ea
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializable.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Interface that can be implemented by objects that know how to
+ * serialize themselves to JSON, using {@link JsonGenerator}
+ * (and {@link SerializerProvider} if necessary).
+ *<p>
+ * Note that implementing this interface binds implementing object
+ * closely to Jackson API, and that it is often not necessary to do
+ * so -- if class is a bean, it can be serialized without
+ * implementing this interface.
+ *<p>
+ * NOTE: Jackson 2.0 added another method (from former "JsonSerializableWithType"),
+ * which is required for proper handling of case where additional type information
+ * is needed.
+ */
+public interface JsonSerializable
+{
+    /**
+     * Serialization method called when no additional type information is
+     * to be included in serialization.
+     */
+    public void serialize(JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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)})
+     * 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
+     * (Array, Object or scalar like String/Number/Boolean).
+     *<p>
+     * Note that some types (most notably, "natural" types: String, Integer,
+     * Double and Boolean) never include type information.
+     */
+    public void serializeWithType(JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
new file mode 100644
index 0000000..f8e7fad
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/JsonSerializer.java
@@ -0,0 +1,243 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Abstract class that defines API used by {@link ObjectMapper} (and
+ * other chained {@link JsonSerializer}s too) to serialize Objects of
+ * arbitrary types into JSON, using provided {@link JsonGenerator}.
+ * {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} instead
+ * of this class, since it will implement many of optional
+ * methods of this class.
+ *<p>
+ * NOTE: various <code>serialize</code> methods are never (to be) called
+ * 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
+ * the output to produce when serializing null values.
+ *<p>
+ * If serializer is an aggregate one -- meaning it delegates handling of some
+ * of its contents by using other serializer(s) -- it typically also needs
+ * to implement {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer},
+ * which can locate secondary serializers needed. This is important to allow dynamic
+ * overrides of serializers; separate call interface is needed to separate
+ * resolution of secondary serializers (which may have cyclic link back
+ * to serializer itself, directly or indirectly).
+ *<p>
+ * In addition, to support per-property annotations (to configure aspects
+ * of serialization on per-property basis), serializers may want
+ * to implement 
+ * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer},
+ * which allows specialization of serializers: call to
+ * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual}
+ * is passed information on property, and can create a newly configured
+ * serializer for handling that particular property.
+ *<p>
+ * If both
+ * {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer} and
+ * {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}
+ * are implemented, resolution of serializers occurs before
+ * contextualization.
+ */
+public abstract class JsonSerializer<T>
+    implements JsonFormatVisitable // since 2.1
+{
+    /*
+    /**********************************************************
+    /* Fluent factory methods for constructing decorated versions
+    /**********************************************************
+     */
+
+    /**
+     * Method that will return serializer instance that produces
+     * "unwrapped" serialization, if applicable for type being
+     * serialized (which is the case for some serializers
+     * that produce JSON Objects as output).
+     * If no unwrapped serializer can be constructed, will simply
+     * return serializer as-is.
+     *<p>
+     * Default implementation just returns serializer as-is,
+     * indicating that no unwrapped variant exists
+     * 
+     * @param unwrapper Name transformation to use to convert between names
+     *   of unwrapper properties
+     */
+    public JsonSerializer<T> unwrappingSerializer(NameTransformer unwrapper) {
+        return this;
+    }
+
+    /**
+     * Method that can be called to try to replace serializer this serializer
+     * delegates calls to. If not supported (either this serializer does not
+     * delegate anything; or it does not want any changes), should either
+     * throw {@link UnsupportedOperationException} (if operation does not
+     * make sense or is not allowed); or return this serializer as is.
+     * 
+     * @since 2.1
+     */
+    public JsonSerializer<T> replaceDelegatee(JsonSerializer<?> delegatee) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles.
+     *
+     * @param value Value to serialize; can <b>not</b> be null.
+     * @param jgen Generator used to output resulting Json content
+     * @param provider Provider that can be used to get serializers for
+     *   serializing Objects value contains, if any.
+     */
+    public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method that can be called to ask implementation to serialize
+     * values of type this serializer handles, using specified type serializer
+     * for embedding necessary type information.
+     *<p>
+     * Default implementation will throw {@link UnsupportedOperationException}
+     * to indicate that proper type handling needs to be implemented.
+     *<p>
+     * For simple datatypes written as a single scalar value (JSON String, Number, Boolean),
+     * implementation would look like:
+     *<pre>
+     *  // note: method to call depends on whether this type is serialized as JSON scalar, object or Array!
+     *  typeSer.writeTypePrefixForScalar(value, jgen);
+     *  serialize(value, jgen, provider);
+     *  typeSer.writeTypeSuffixForScalar(value, jgen);
+     *</pre>
+     * and implementations for type serialized as JSON Arrays or Objects would differ slightly,
+     * as <code>START-ARRAY>/<code>END-ARRAY</code> and
+     * <code>START-OBJECT>/<code>END-OBJECT</code> pairs
+     * need to be properly handled with respect to serializing of contents.
+     *
+     * @param value Value to serialize; can <b>not</b> be null.
+     * @param jgen Generator used to output resulting Json content
+     * @param provider Provider that can be used to get serializers for
+     *   serializing Objects value contains, if any.
+     * @param typeSer Type serializer to use for including type information
+     */
+    public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        Class<?> clz = handledType();
+        if (clz == null) {
+            clz = value.getClass();
+        }
+        throw new UnsupportedOperationException("Type id handling not implemented for type "+clz.getName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Other accessors
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing type of Objects this serializer can handle.
+     * Note that this information is not guaranteed to be exact -- it
+     * may be a more generic (super-type) -- but it should not be
+     * incorrect (return a non-related type).
+     *<p>
+     * Default implementation will return null, which essentially means
+     * same as returning <code>Object.class</code> would; that is, that
+     * nothing is known about handled type.
+     *<p>
+     */
+    public Class<T> handledType() { return null; }
+
+    /**
+     * Method called to check whether given serializable value is
+     * considered "empty" value (for purposes of suppressing serialization
+     * of empty values).
+     *<p>
+     * Default implementation will consider only null values to be empty.
+     * 
+     * @since 2.0
+     */
+    public boolean isEmpty(T value) {
+        return (value == null);
+    }
+
+    /**
+     * Method that can be called to see whether this serializer instance
+     * will use Object Id to handle cyclic references.
+     */
+    public boolean usesObjectId() {
+        return false;
+    }
+
+    /**
+     * Accessor for checking whether this serializer is an
+     * "unwrapping" serializer; this is necessary to know since
+     * it may also require caller to suppress writing of the
+     * leading property name.
+     */
+    public boolean isUnwrappingSerializer() {
+        return false;
+    }
+    
+    /**
+     * Accessor that can be used to determine if this serializer uses
+     * another serializer for actual serialization, by delegating
+     * calls. If so, will return immediate delegate (which itself may
+     * delegate to further serializers); otherwise will return null.
+     * 
+     * @return Serializer this serializer delegates calls to, if null;
+     *   null otherwise.
+     * 
+     * @since 2.1
+     */
+    public JsonSerializer<?> getDelegatee() {
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Default JsonFormatVisitable implementation
+    /**********************************************************
+     */
+
+    /**
+     * Default implementation simply calls {@link JsonFormatVisitorWrapper#expectAnyFormat(JavaType)}.
+     * 
+     * @since 2.1
+     */
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType type)
+        throws JsonMappingException
+    {
+        if (visitor != null) visitor.expectAnyFormat(type);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper class(es)
+    /**********************************************************
+     */
+
+    /**
+     * This marker class is only to be used with annotations, to
+     * indicate that <b>no serializer is configured</b>.
+     *<p>
+     * Specifically, this class is to be used as the marker for
+     * annotation {@link com.fasterxml.jackson.databind.annotation.JsonSerialize}.
+     */
+    public abstract static class None
+        extends JsonSerializer<Object> { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java
new file mode 100644
index 0000000..90dd56d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/KeyDeserializer.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.databind;
+
+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
+ * if the Map key class is not <code>String</code> or <code>Object</code>.
+ */
+public abstract class KeyDeserializer
+{
+    /**
+     * 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;
+
+    /**
+     * This marker class is only to be used with annotations, to
+     * indicate that <b>no deserializer is configured</b>.
+     *<p>
+     * Specifically, this class is to be used as the marker for
+     * annotation {@link com.fasterxml.jackson.databind.annotation.JsonDeserialize}.
+     */
+    public abstract static class None
+        extends KeyDeserializer { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
new file mode 100644
index 0000000..7753c88
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/MapperFeature.java
@@ -0,0 +1,289 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.cfg.ConfigFeature;
+
+/**
+ * Enumeration that defines simple on/off features to set
+ * for {@link ObjectMapper}, and accessible (but not changeable)
+ * via {@link ObjectReader} and {@link ObjectWriter} (as well as
+ * through various convenience methods through context objects).
+ *<p>
+ * Note that in addition to being only mutable via {@link ObjectMapper},
+ * changes only take effect when done <b>before any serialization or
+ * deserialization</b> calls -- that is, caller must follow
+ * "configure-then-use" pattern.
+ */
+public enum MapperFeature implements ConfigFeature
+{
+    /*
+    /******************************************************
+    /*  Introspection features
+    /******************************************************
+     */
+    
+    /**
+     * Feature that determines whether annotation introspection
+     * is used for configuration; if enabled, configured
+     * {@link AnnotationIntrospector} will be used: if disabled,
+     * no annotations are considered.
+     *<p>
+     * Feature is enabled by default.
+     */
+    USE_ANNOTATIONS(true),
+
+    /**
+     * Feature that determines whether "creator" methods are
+     * automatically detected by consider public constructors,
+     * and static single argument methods with name "valueOf".
+     * If disabled, only methods explicitly annotated are considered
+     * creator methods (except for the no-arg default constructor which
+     * is always considered a factory method).
+     *<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_CREATORS(true),
+    
+    /**
+     * Feature that determines whether non-static fields are recognized as
+     * properties.
+     * If yes, then all public member fields
+     * are considered as properties. If disabled, only fields explicitly
+     * annotated are considered property fields.
+     *<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_FIELDS(true),
+    
+    /**
+     * Feature that determines whether regualr "getter" methods are
+     * automatically detected based on standard Bean naming convention
+     * or not. If yes, then all public zero-argument methods that
+     * start with prefix "get" 
+     * are considered as getters.
+     * If disabled, only methods explicitly  annotated are considered getters.
+     *<p>
+     * Note that since version 1.3, this does <b>NOT</b> include
+     * "is getters" (see {@link #AUTO_DETECT_IS_GETTERS} for details)
+     *<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_GETTERS(true),
+
+    /**
+     * Feature that determines whether "is getter" methods are
+     * automatically detected based on standard Bean naming convention
+     * or not. If yes, then all public zero-argument methods that
+     * start with prefix "is", and whose return type is boolean
+     * are considered as "is getters".
+     * If disabled, only methods explicitly annotated are considered getters.
+     *<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_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 getters (getter methods)
+     * can be auto-detected if there is no matching mutator (setter,
+     * constructor parameter or field) or not: if set to true,
+     * only getters that match a mutator are auto-discovered; if
+     * false, all auto-detectable getters can be discovered.
+     *<p>
+     * Feature is disabled by default.
+     */
+    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.
+     *<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 whether method and field access
+     * modifier settings can be overridden when accessing
+     * properties. If enabled, method
+     * {@link java.lang.reflect.AccessibleObject#setAccessible}
+     * may be called to enable access to otherwise unaccessible
+     * objects.
+     *<p>
+     * Feature is enabled by default.
+     */
+    CAN_OVERRIDE_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.
+     */
+    ALLOW_FINAL_FIELDS_AS_MUTATORS(true),
+    
+    /*
+    /******************************************************
+    /* Type-handling features
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether the type detection for
+     * serialization should be using actual dynamic runtime type,
+     * or declared static type.
+     * Note that deserialization always uses declared static types
+     * since no runtime types are available (as we are creating
+     * instances after using type information).
+     *<p>
+     * This global default value can be overridden at class, method
+     * or field level by using {@link JsonSerialize#typing} annotation
+     * property.
+     *<p>
+     * Feature is disabled by default which means that dynamic runtime types
+     * are used (instead of declared static types) for serialization.
+     */
+    USE_STATIC_TYPING(false),
+
+    /*
+    /******************************************************
+    /* View-related features
+    /******************************************************
+     */
+    
+    /**
+     * Feature that determines whether properties that have no view
+     * annotations are included in JSON serialization views (see
+     * {@link com.fasterxml.jackson.annotation.JsonView} for more
+     * details on JSON Views).
+     * If enabled, non-annotated properties will be included;
+     * when disabled, they will be excluded. So this feature
+     * changes between "opt-in" (feature disabled) and
+     * "opt-out" (feature enabled) modes.
+     *<p>
+     * Default value is enabled, meaning that non-annotated
+     * properties are included in all views if there is no
+     * {@link com.fasterxml.jackson.annotation.JsonView} annotation.
+     *<p>
+     * Feature is enabled by default.
+     */
+    DEFAULT_VIEW_INCLUSION(true),
+    
+    /*
+    /******************************************************
+    /* Generic output features
+    /******************************************************
+     */
+
+    /**
+     * Feature that defines default property serialization order used
+     * for POJO fields (note: does <b>not</b> apply to {@link java.util.Map}
+     * serialization!):
+     * if enabled, default ordering is alphabetic (similar to
+     * how {@link com.fasterxml.jackson.annotation.JsonPropertyOrder#alphabetic()}
+     * works); if disabled, order is unspecified (based on what JDK gives
+     * us, which may be declaration order, but is not guaranteed).
+     *<p>
+     * Note that this is just the default behavior, and can be overridden by
+     * explicit overrides in classes.
+     *<p>
+     * Feature is disabled by default.
+     */
+    SORT_PROPERTIES_ALPHABETICALLY(false),
+
+    /*
+    /******************************************************
+    /* Name-related features
+    /******************************************************
+     */
+
+    /**
+     * Feature that can be enabled to make property names be
+     * overridden by wrapper name (usually detected with annotations
+     * as defined by {@link AnnotationIntrospector#findWrapperName}.
+     * If enabled, all properties that have associated non-empty Wrapper
+     * name will use that wrapper name instead of property name.
+     * If disabled, wrapper name is only used for wrapping (if anything).
+     *<p>
+     * Feature is disabled by default.
+     * 
+     * @since 2.1
+     */
+    USE_WRAPPER_NAME_AS_PROPERTY_NAME(false)
+    ;
+
+    private final boolean _defaultState;
+    
+    private MapperFeature(boolean defaultState) {
+        _defaultState = defaultState;
+    }
+    
+    @Override
+    public boolean enabledByDefault() { return _defaultState; }
+
+    @Override
+    public int getMask() { return (1 << ordinal()); }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
new file mode 100644
index 0000000..04e8077
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/MappingIterator.java
@@ -0,0 +1,266 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Iterator exposed by {@link ObjectMapper} when binding sequence of
+ * objects. Extension is done to allow more convenient exposing of
+ * {@link IOException} (which basic {@link Iterator} does not expose)
+ */
+public class MappingIterator<T> implements Iterator<T>, Closeable
+{
+    protected final static MappingIterator<?> EMPTY_ITERATOR =
+        new MappingIterator<Object>(null, null, null, null, false, null);
+    
+    protected final JavaType _type;
+
+    protected final DeserializationContext _context;
+    
+    protected final JsonDeserializer<T> _deserializer;
+
+    protected JsonParser _parser;
+    
+    /**
+     * Flag that indicates whether input {@link JsonParser} should be closed
+     * when we are done or not; generally only called when caller did not
+     * pass JsonParser.
+     */
+    protected final boolean _closeParser;
+
+    /**
+     * Flag that is set when we have determined what {@link #hasNextValue()}
+     * should value; reset when {@link #nextValue} is called
+     */
+    protected boolean _hasNextChecked;
+    
+    /**
+     * If not null, "value to update" instead of creating a new instance
+     * for each call.
+     */
+    protected final T _updatedValue;
+
+    /**
+     * @deprecated Since 2.1, to be removed
+     */
+    @Deprecated
+    protected MappingIterator(JavaType type, JsonParser jp, DeserializationContext ctxt,
+            JsonDeserializer<?> deser)
+    {
+        this(type, jp, ctxt, deser, true, null);
+    }
+
+    /**
+     * @param managedParser Whether we "own" the {@link JsonParser} passed or not:
+     *   if true, it was created by {@link ObjectReader} and code here needs to
+     *   close it; if false, it was passed by calling code and should not be
+     *   closed by iterator.
+     */
+    @SuppressWarnings("unchecked")
+    protected MappingIterator(JavaType type, JsonParser jp, DeserializationContext ctxt,
+            JsonDeserializer<?> deser,
+            boolean managedParser, Object valueToUpdate)
+    {
+        _type = type;
+        _parser = jp;
+        _context = ctxt;
+        _deserializer = (JsonDeserializer<T>) deser;
+        _closeParser = managedParser;
+        if (valueToUpdate == null) {
+            _updatedValue = null;
+        } else {
+            _updatedValue = (T) valueToUpdate;
+        }
+
+        /* Ok: one more thing; we may have to skip START_ARRAY, assuming
+         * "wrapped" sequence; but this is ONLY done for 'managed' parsers
+         * and never if JsonParser was directly passed by caller (if it
+         * was, caller must have either positioned it over first token of
+         * the first element, or cleared the START_ARRAY token explicitly).
+         * Note, however, that we do not try to guess whether this could be
+         * an unwrapped sequence of arrays/Lists: we just assume it is wrapped;
+         * and if not, caller needs to hand us JsonParser instead, pointing to
+         * the first token of the first element.
+         */
+        if (managedParser && jp != null && jp.getCurrentToken() == JsonToken.START_ARRAY) {
+            jp.clearCurrentToken();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static <T> MappingIterator<T> emptyIterator() {
+        return (MappingIterator<T>) EMPTY_ITERATOR;
+    }
+    
+    /*
+    /**********************************************************
+    /* Basic iterator impl
+    /**********************************************************
+     */
+
+    @Override
+    public boolean hasNext()
+    {
+        try {
+            return hasNextValue();
+        } catch (JsonMappingException e) {
+            throw new RuntimeJsonMappingException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public T next()
+    {
+        try {
+            return nextValue();
+        } catch (JsonMappingException e) {
+            throw new RuntimeJsonMappingException(e.getMessage(), e);
+        } catch (IOException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+    
+    @Override
+    public void close() throws IOException{
+        if(_parser != null) {
+            _parser.close();
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API, iteration
+    /**********************************************************
+     */
+
+    /**
+     * Equivalent of {@link #next} but one that may throw checked
+     * exceptions from Jackson due to invalid input.
+     */
+    public boolean hasNextValue() throws IOException
+    {
+        if (_parser == null) {
+            return false;
+        }
+        if (!_hasNextChecked) {
+            JsonToken t = _parser.getCurrentToken();
+            _hasNextChecked = true;
+            if (t == null) { // un-initialized or cleared; find next
+                t = _parser.nextToken();
+                // If EOF, no more, or if we hit END_ARRAY (although we don't clear the token).
+                if (t == null || t == JsonToken.END_ARRAY) {
+                    JsonParser jp = _parser;
+                    _parser = null;
+                    if (_closeParser) {
+                        jp.close();
+                    }
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    
+    public T nextValue() throws IOException
+    {
+        // caller should always call 'hasNext[Value]' first; but let's ensure:
+        if (!_hasNextChecked) {
+            if (!hasNextValue()) {
+                throw new NoSuchElementException();
+            }
+        }
+        if (_parser == null) {
+            throw new NoSuchElementException();
+        }
+        _hasNextChecked = false;
+        T result;
+        
+        if (_updatedValue == null) {
+            result = _deserializer.deserialize(_parser, _context);
+        } else{
+            _deserializer.deserialize(_parser, _context, _updatedValue);
+            result = _updatedValue;
+        }
+        // Need to consume the token too
+        _parser.clearCurrentToken();
+        return result;
+    }
+
+    /**
+     * Convenience method for reading all entries accessible via
+     * this iterator
+     * 
+     * @return List of entries read
+     * 
+     * @since 2.2
+     */
+    public List<T> readAll() throws IOException {
+        return readAll(new ArrayList<T>());
+    }
+
+    /**
+     * Convenience method for reading all entries accessible via
+     * this iterator
+     * 
+     * @return List of entries read (same as passed-in argument)
+     * 
+     * @since 2.2
+     */
+    public List<T> readAll(List<T> resultList) throws IOException
+    {
+        while (hasNextValue()) {
+    		    resultList.add(nextValue());
+        }
+        return resultList;
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for getting underlying parser this iterator uses.
+     * 
+     * @since 2.2
+     */
+    public JsonParser getParser() {
+    	return _parser;
+    }
+
+    /**
+     * Accessor for accessing {@link FormatSchema} that the underlying parser
+     * (as per {@link #getParser}) is using, if any; only parser of schema-aware
+     * formats use schemas.
+     * 
+     * @since 2.2
+     */
+    public FormatSchema getParserSchema() {
+    	return _parser.getSchema();
+    }
+
+    /**
+     * Convenience method, functionally equivalent to:
+     *<code>
+     *   iterator.getParser().getCurrentLocation()
+     *</code>
+     * 
+     * @return Location of the input stream of the underlying parser
+     * 
+     * @since 2.2.1
+     */
+    public JsonLocation getCurrentLocation() {
+        return _parser.getCurrentLocation();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java b/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java
new file mode 100644
index 0000000..c4595e2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/MappingJsonFactory.java
@@ -0,0 +1,95 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta at iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.format.InputAccessor;
+import com.fasterxml.jackson.core.format.MatchStrength;
+
+/**
+ * Sub-class of {@link JsonFactory} that will create a proper
+ * {@link ObjectCodec} to allow seam-less conversions between
+ * JSON content and Java objects (POJOs).
+ * The only addition to regular {@link JsonFactory} currently
+ * is that {@link ObjectMapper} is constructed and passed as
+ * the codec to use.
+ */
+public class MappingJsonFactory
+    extends JsonFactory
+{
+    // generated for Jackson 2.1.0
+    private static final long serialVersionUID = -6744103724013275513L;
+
+    public MappingJsonFactory()
+    {
+        this(null);
+    }
+
+    public MappingJsonFactory(ObjectMapper mapper)
+    {
+        super(mapper);
+        if (mapper == null) {
+            setCodec(new ObjectMapper(this));
+        }
+    }
+
+    /**
+     * We'll override the method to return more specific type; co-variance
+     * helps here
+     */
+    @Override
+    public final ObjectMapper getCodec() { return (ObjectMapper) _objectCodec; }
+
+    // @since 2.1
+    @Override
+    public JsonFactory copy()
+    {
+        _checkInvalidCopy(MappingJsonFactory.class);
+        // note: as with base class, must NOT copy mapper reference
+        return new MappingJsonFactory(null);
+    }
+    
+    /*
+    /**********************************************************
+    /* Format detection functionality (since 1.8)
+    /**********************************************************
+     */
+    
+    /**
+     * Sub-classes need to override this method (as of 1.8)
+     */
+    @Override
+    public String getFormatName()
+    {
+        /* since non-JSON factories typically should not extend this class,
+         * let's just always return JSON as name.
+         */
+        return FORMAT_NAME_JSON;
+    }
+
+    /**
+     * Sub-classes need to override this method (as of 1.8)
+     */
+    @Override
+    public MatchStrength hasFormat(InputAccessor acc) throws IOException
+    {
+        if (getClass() == MappingJsonFactory.class) {
+            return hasJSONFormat(acc);
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/Module.java b/src/main/java/com/fasterxml/jackson/databind/Module.java
new file mode 100644
index 0000000..d55cce1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/Module.java
@@ -0,0 +1,285 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+import com.fasterxml.jackson.databind.deser.Deserializers;
+import com.fasterxml.jackson.databind.deser.KeyDeserializers;
+import com.fasterxml.jackson.databind.deser.ValueInstantiators;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.type.TypeModifier;
+
+/**
+ * Simple interface for extensions that can be registered with {@link ObjectMapper}
+ * to provide a well-defined set of extensions to default functionality; such as
+ * support for new data types.
+ */
+public abstract class Module
+    implements Versioned
+{
+    /*
+    /**********************************************************
+    /* Simple accessors
+    /**********************************************************
+     */
+    
+    /**
+     * Method that returns identifier for module; this can be used by Jackson
+     * for informational purposes, as well as in associating extensions with
+     * module that provides them.
+     */
+    public abstract String getModuleName();
+
+    /**
+     * Method that returns version of this module. Can be used by Jackson for
+     * informational purposes.
+     */
+    @Override
+    public abstract Version version();
+
+    /*
+    /**********************************************************
+    /* Life-cycle: registration
+    /**********************************************************
+     */
+    
+    /**
+     * Method called by {@link ObjectMapper} when module is registered.
+     * It is called to let module register functionality it provides,
+     * using callback methods passed-in context object exposes.
+     */
+    public abstract void setupModule(SetupContext context);
+    
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Interface Jackson exposes to modules for purpose of registering
+     * extended functionality.
+     * Usually implemented by {@link ObjectMapper}, but modules should
+     * NOT rely on this -- if they do require access to mapper instance,
+     * they need to call {@link SetupContext#getOwner} method.
+     */
+    public static interface SetupContext
+    {
+        /*
+        /**********************************************************
+        /* Simple accessors
+        /**********************************************************
+         */
+        
+        /**
+         * Method that returns version information about {@link ObjectMapper} 
+         * that implements this context. Modules can use this to choose
+         * different settings or initialization order; or even decide to fail
+         * set up completely if version is compatible with module.
+         */
+        public Version getMapperVersion();
+
+        /**
+         * Fallback access method that allows modules to refer to the
+         * {@link ObjectMapper} that provided this context.
+         * It should NOT be needed by most modules; and ideally should
+         * not be used -- however, there may be cases where this may
+         * be necessary due to various design constraints.
+         *<p>
+         * NOTE: use of this method is discouraged, as it allows access to
+         * things Modules typically should not modify. It is included, however,
+         * to allow access to new features in cases where Module API
+         * has not yet been extended, or there are oversights.
+         *<p>
+         * Return value is chosen to not leak dependency to {@link ObjectMapper};
+         * however, instance will always be of that type.
+         * This is why return value is declared generic, to allow caller to
+         * specify context to often avoid casting.
+         * 
+         * @since 2.0
+         */
+        public <C extends ObjectCodec> C getOwner();
+
+        /**
+         * Accessor for finding {@link TypeFactory} that is currently configured
+         * by the context.
+         *<p>
+         * NOTE: since it is possible that other modules might change or replace
+         * TypeFactory, use of this method adds order-dependency for registrations.
+         * 
+         * @since 2.0
+         */
+        public TypeFactory getTypeFactory();
+        
+        public boolean isEnabled(MapperFeature f);
+        
+        public boolean isEnabled(DeserializationFeature f);
+
+        public boolean isEnabled(SerializationFeature f);
+
+        public boolean isEnabled(JsonFactory.Feature f);
+        
+        public boolean isEnabled(JsonParser.Feature f);
+
+        public boolean isEnabled(JsonGenerator.Feature f);
+        
+        /*
+        /**********************************************************
+        /* Handler registration; serializers/deserializers
+        /**********************************************************
+         */
+        
+        /**
+         * Method that module can use to register additional deserializers to use for
+         * handling types.
+         * 
+         * @param d Object that can be called to find deserializer for types supported
+         *   by module (null returned for non-supported types)
+         */
+        public void addDeserializers(Deserializers d);
+
+        /**
+         * Method that module can use to register additional deserializers to use for
+         * handling Map key values (which are separate from value deserializers because
+         * they are always serialized from String values)
+         */
+        public void addKeyDeserializers(KeyDeserializers s);
+        
+        /**
+         * Method that module can use to register additional serializers to use for
+         * handling types.
+         * 
+         * @param s Object that can be called to find serializer for types supported
+         *   by module (null returned for non-supported types)
+         */
+        public void addSerializers(Serializers s);
+
+        /**
+         * Method that module can use to register additional serializers to use for
+         * handling Map key values (which are separate from value serializers because
+         * they must write <code>JsonToken.FIELD_NAME</code> instead of String value).
+         */
+        public void addKeySerializers(Serializers s);
+
+        /*
+        /**********************************************************
+        /* Handler registration; other
+        /**********************************************************
+         */
+        
+        /**
+         * Method that module can use to register additional modifier objects to
+         * customize configuration and construction of bean deserializers.
+         * 
+         * @param mod Modifier to register
+         */
+        public void addBeanDeserializerModifier(BeanDeserializerModifier mod);
+
+        /**
+         * Method that module can use to register additional modifier objects to
+         * customize configuration and construction of bean serializers.
+         * 
+         * @param mod Modifier to register
+         */
+        public void addBeanSerializerModifier(BeanSerializerModifier mod);
+
+        /**
+         * Method that module can use to register additional
+         * {@link AbstractTypeResolver} instance, to handle resolution of
+         * abstract to concrete types (either by defaulting, or by materializing).
+         * 
+         * @param resolver Resolver to add.
+         */
+        public void addAbstractTypeResolver(AbstractTypeResolver resolver);
+
+        /**
+         * Method that module can use to register additional
+         * {@link TypeModifier} instance, which can augment {@link com.fasterxml.jackson.databind.JavaType}
+         * instances constructed by {@link com.fasterxml.jackson.databind.type.TypeFactory}.
+         * 
+         * @param modifier to add
+         */
+        public void addTypeModifier(TypeModifier modifier);
+
+        /**
+         * Method that module can use to register additional {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s,
+         * by adding {@link ValueInstantiators} object that gets called when 
+         * instantatiator is needed by a deserializer.
+         * 
+         * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for
+         *    constructing POJO values during deserialization
+         */
+        public void addValueInstantiators(ValueInstantiators instantiators);
+
+        /**
+         * Method for replacing the default class introspector with a derived class that
+         * overrides specific behavior.
+         *
+         * @param ci Derived class of ClassIntrospector with overriden behavior
+         *
+         * @since 2.2
+         */
+        public void setClassIntrospector(ClassIntrospector ci);
+
+        /**
+         * Method for registering specified {@link AnnotationIntrospector} as the highest
+         * priority introspector (will be chained with existing introspector(s) which
+         * will be used as fallbacks for cases this introspector does not handle)
+         * 
+         * @param ai Annotation introspector to register.
+         */
+        public void insertAnnotationIntrospector(AnnotationIntrospector ai);
+
+        /**
+         * Method for registering specified {@link AnnotationIntrospector} as the lowest
+         * priority introspector, chained with existing introspector(s) and called
+         * as fallback for cases not otherwise handled.
+         * 
+         * @param ai Annotation introspector to register.
+         */
+        public void appendAnnotationIntrospector(AnnotationIntrospector ai);
+
+        /**
+         * Method for registering specified classes as subtypes (of supertype(s)
+         * they have)
+         */
+        public void registerSubtypes(Class<?>... subtypes);
+
+        /**
+         * Method for registering specified classes as subtypes (of supertype(s)
+         * they have), using specified type names.
+         */
+        public void registerSubtypes(NamedType... subtypes);
+        
+        /**
+         * Method used for defining mix-in annotations to use for augmenting
+         * specified class or interface.
+         * All annotations from
+         * <code>mixinSource</code> are taken to override annotations
+         * that <code>target</code> (or its supertypes) has.
+         *<p>
+         * Note: mix-ins are registered both for serialization and deserialization
+         * (which can be different internally).
+         *<p>
+         * Note: currently only one set of mix-in annotations can be defined for
+         * a single class; so if multiple modules register mix-ins, highest
+         * priority one (last one registered) will have priority over other modules.
+         *
+         * @param target Class (or interface) whose annotations to effectively override
+         * @param mixinSource Class (or interface) whose annotations are to
+         *   be "added" to target's annotations, overriding as necessary
+         */
+        public void setMixInAnnotations(Class<?> target, Class<?> mixinSource);
+
+        /**
+         * Add a deserialization problem handler
+         *
+         * @param handler The deserialization problem handler
+         */
+        public void addDeserializationProblemHandler(DeserializationProblemHandler handler);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
new file mode 100644
index 0000000..c58a963
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectMapper.java
@@ -0,0 +1,3010 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.net.URL;
+import java.text.DateFormat;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SegmentedStringWriter;
+import com.fasterxml.jackson.core.io.SerializedString;
+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.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.*;
+import com.fasterxml.jackson.databind.jsontype.impl.StdSubtypeResolver;
+import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
+import com.fasterxml.jackson.databind.node.*;
+import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.RootNameLookup;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * This mapper (or, data binder, or codec) provides functionality for
+ * converting between Java objects (instances of JDK provided core classes,
+ * beans), and matching JSON constructs.
+ * It will use instances of {@link JsonParser} and {@link JsonGenerator}
+ * for implementing actual reading/writing of JSON.
+ *<p>
+ * The main conversion API is defined in {@link ObjectCodec}, so that
+ * implementation details of this class need not be exposed to
+ * streaming parser and generator classes.
+ *<p>
+ * Note on caching: root-level deserializers are always cached, and accessed
+ * using full (generics-aware) type information. This is different from
+ * caching of referenced types, which is more limited and is done only
+ * for a subset of all deserializer types. The main reason for difference
+ * is that at root-level there is no incoming reference (and hence no
+ * referencing property, no referral information or annotations to
+ * produce differing deserializers), and that the performance impact
+ * greatest at root level (since it'll essentially cache the full
+ * graph of deserializers involved).
+ */
+public class ObjectMapper
+    extends ObjectCodec
+    implements Versioned,
+        java.io.Serializable // as of 2.1
+{
+    private static final long serialVersionUID = 1L;
+
+    /*
+    /**********************************************************
+    /* Helper classes, enums
+    /**********************************************************
+     */
+
+    /**
+     * Enumeration used with {@link ObjectMapper#enableDefaultTyping()}
+     * to specify what kind of types (classes) default typing should
+     * be used for. It will only be used if no explicit type information
+     * is found, but this enumeration further limits subset of those types.
+     */
+    public enum DefaultTyping {
+        /**
+         * This value means that only properties that have
+         * {@link java.lang.Object} as declared type (including
+         * generic types without explicit type) will use default
+         * typing.
+         */
+        JAVA_LANG_OBJECT,
+        
+        /**
+         * Value that means that default typing will be used for
+         * properties with declared type of {@link java.lang.Object}
+         * or an abstract type (abstract class or interface).
+         * Note that this does <b>not</b> include array types.
+         */
+        OBJECT_AND_NON_CONCRETE,
+
+        /**
+         * Value that means that default typing will be used for
+         * all types covered by {@link #OBJECT_AND_NON_CONCRETE}
+         * plus all array types for them.
+         */
+        NON_CONCRETE_AND_ARRAYS,
+        
+        /**
+         * Value that means that default typing will be used for
+         * all non-final types, with exception of small number of
+         * "natural" types (String, Boolean, Integer, Double), which
+         * can be correctly inferred from JSON; as well as for
+         * all arrays of non-final types.
+         */
+        NON_FINAL
+    }
+
+    /**
+     * Customized {@link TypeResolverBuilder} that provides type resolver builders
+     * used with so-called "default typing"
+     * (see {@link ObjectMapper#enableDefaultTyping()} for details).
+     *<p>
+     * Type resolver construction is based on configuration: implementation takes care
+     * of only providing builders in cases where type information should be applied.
+     * This is important since build calls may be sent for any and all types, and
+     * type information should NOT be applied to all of them.
+     */
+    public static class DefaultTypeResolverBuilder
+        extends StdTypeResolverBuilder
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Definition of what types is this default typer valid for.
+         */
+        protected final DefaultTyping _appliesFor;
+
+        public DefaultTypeResolverBuilder(DefaultTyping t) {
+            _appliesFor = t;
+        }
+
+        @Override
+        public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
+                JavaType baseType, Collection<NamedType> subtypes)
+        {
+            return useForType(baseType) ? super.buildTypeDeserializer(config, baseType, subtypes) : null;
+        }
+
+        @Override
+        public TypeSerializer buildTypeSerializer(SerializationConfig config,
+                JavaType baseType, Collection<NamedType> subtypes)
+        {
+            return useForType(baseType) ? super.buildTypeSerializer(config, baseType, subtypes) : null;            
+        }
+
+        /**
+         * Method called to check if the default type handler should be
+         * used for given type.
+         * Note: "natural types" (String, Boolean, Integer, Double) will never
+         * use typing; that is both due to them being concrete and final,
+         * and since actual serializers and deserializers will also ignore any
+         * attempts to enforce typing.
+         */
+        public boolean useForType(JavaType t)
+        {
+            switch (_appliesFor) {
+            case NON_CONCRETE_AND_ARRAYS:
+                while (t.isArrayType()) {
+                    t = t.getContentType();
+                }
+                // fall through
+            case OBJECT_AND_NON_CONCRETE:
+                return (t.getRawClass() == Object.class) || !t.isConcrete();
+            case NON_FINAL:
+                while (t.isArrayType()) {
+                    t = t.getContentType();
+                }
+                return !t.isFinal(); // includes Object.class
+            default:
+            //case JAVA_LANG_OBJECT:
+                return (t.getRawClass() == Object.class);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal constants, singletons
+    /**********************************************************
+     */
+    
+    // Quick little shortcut, to avoid having to use global TypeFactory instance...
+    private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
+
+    /* !!! 03-Apr-2009, tatu: Should try to avoid direct reference... but not
+     *   sure what'd be simple and elegant way. So until then:
+     */
+    protected final static ClassIntrospector DEFAULT_INTROSPECTOR = BasicClassIntrospector.instance;
+
+    // 16-May-2009, tatu: Ditto ^^^
+    protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();
+
+    protected final static VisibilityChecker<?> STD_VISIBILITY_CHECKER = VisibilityChecker.Std.defaultInstance();
+
+    protected final static PrettyPrinter _defaultPrettyPrinter = new DefaultPrettyPrinter();
+    
+    /**
+     * Base settings contain defaults used for all {@link ObjectMapper}
+     * instances.
+     */
+    protected final static BaseSettings DEFAULT_BASE = new BaseSettings(DEFAULT_INTROSPECTOR,
+            DEFAULT_ANNOTATION_INTROSPECTOR, STD_VISIBILITY_CHECKER, null, TypeFactory.defaultInstance(),
+            null, StdDateFormat.instance, null,
+            Locale.getDefault(),
+//            TimeZone.getDefault()
+            TimeZone.getTimeZone("GMT"),
+            Base64Variants.getDefaultVariant() // 2.1
+    );
+    
+    /*
+    /**********************************************************
+    /* Configuration settings, shared
+    /**********************************************************
+     */
+
+    /**
+     * Factory used to create {@link JsonParser} and {@link JsonGenerator}
+     * instances as necessary.
+     */
+    protected final JsonFactory _jsonFactory;
+
+    /**
+     * Specific factory used for creating {@link JavaType} instances;
+     * needed to allow modules to add more custom type handling
+     * (mostly to support types of non-Java JVM languages)
+     */
+    protected TypeFactory _typeFactory;
+
+    /**
+     * Provider for values to inject in deserialized POJOs.
+     */
+    protected InjectableValues _injectableValues;
+
+    /**
+     * Thing used for registering sub-types, resolving them to
+     * super/sub-types as needed.
+     */
+    protected SubtypeResolver _subtypeResolver;
+
+    /**
+     * Cache for root names used when root-wrapping is enabled.
+     */
+    protected final RootNameLookup _rootNames;
+    
+    /*
+    /**********************************************************
+    /* Configuration settings: mix-in annotations
+    /**********************************************************
+     */
+    
+    /**
+     * Mapping that defines how to apply mix-in annotations: key is
+     * the type to received additional annotations, and value is the
+     * type that has annotations to "mix in".
+     *<p>
+     * Annotations associated with the value classes will be used to
+     * override annotations of the key class, associated with the
+     * same field or method. They can be further masked by sub-classes:
+     * you can think of it as injecting annotations between the target
+     * class and its sub-classes (or interfaces)
+     */
+    protected final HashMap<ClassKey,Class<?>> _mixInAnnotations
+        = new HashMap<ClassKey,Class<?>>();
+    
+    /*
+    /**********************************************************
+    /* Configuration settings, serialization
+    /**********************************************************
+     */
+
+    /**
+     * Configuration object that defines basic global
+     * settings for the serialization process
+     */
+    protected SerializationConfig _serializationConfig;
+
+    /**
+     * Object that manages access to serializers used for serialization,
+     * including caching.
+     * It is configured with {@link #_serializerFactory} to allow
+     * for constructing custom serializers.
+     *<p>
+     * Note: while serializers are only exposed {@link SerializerProvider},
+     * mappers and readers need to access additional API defined by
+     * {@link DefaultSerializerProvider}
+     */
+    protected DefaultSerializerProvider _serializerProvider;
+
+    /**
+     * Serializer factory used for constructing serializers.
+     */
+    protected SerializerFactory _serializerFactory;
+
+    /*
+    /**********************************************************
+    /* Configuration settings, deserialization
+    /**********************************************************
+     */
+
+    /**
+     * Configuration object that defines basic global
+     * settings for the serialization process
+     */
+    protected DeserializationConfig _deserializationConfig;
+
+    /**
+     * Blueprint context object; stored here to allow custom
+     * sub-classes. Contains references to objects needed for
+     * deserialization construction (cache, factory).
+     */
+    protected DefaultDeserializationContext _deserializationContext;
+
+    /*
+    /**********************************************************
+    /* Caching
+    /**********************************************************
+     */
+
+    /* Note: handling of serializers and deserializers is not symmetric;
+     * and as a result, only root-level deserializers can be cached here.
+     * This is mostly because typing and resolution for deserializers is
+     * fully static; whereas it is quite dynamic for serialization.
+     */
+
+    /**
+     * We will use a separate main-level Map for keeping track
+     * of root-level deserializers. This is where most succesful
+     * cache lookups get resolved.
+     * Map will contain resolvers for all kinds of types, including
+     * container types: this is different from the component cache
+     * which will only cache bean deserializers.
+     *<p>
+     * Given that we don't expect much concurrency for additions
+     * (should very quickly converge to zero after startup), let's
+     * explicitly define a low concurrency setting.
+     *<p>
+     * Since version 1.5, these may are either "raw" deserializers (when
+     * no type information is needed for base type), or type-wrapped
+     * deserializers (if it is needed)
+     */
+    final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _rootDeserializers
+        = new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.6f, 2);
+
+    /*
+    /**********************************************************
+    /* Life-cycle: constructing instance
+    /**********************************************************
+     */
+
+    /**
+     * Default constructor, which will construct the default
+     * {@link JsonFactory} as necessary, use
+     * {@link SerializerProvider} as its
+     * {@link SerializerProvider}, and
+     * {@link BeanSerializerFactory} as its
+     * {@link SerializerFactory}.
+     * This means that it
+     * can serialize all standard JDK types, as well as regular
+     * Java Beans (based on method names and Jackson-specific annotations),
+     * but does not support JAXB annotations.
+     */
+    public ObjectMapper()
+    {
+        this(null, null, null);
+    }
+
+    /**
+     * Constructs instance that uses specified {@link JsonFactory}
+     * for constructing necessary {@link JsonParser}s and/or
+     * {@link JsonGenerator}s.
+     */
+    public ObjectMapper(JsonFactory jf)
+    {
+        this(jf, null, null);
+    }
+
+    /**
+     * Copy-constructor, mostly used to support {@link #copy}.
+     * 
+     * @since 2.1
+     */
+    protected ObjectMapper(ObjectMapper src)
+    {
+        _jsonFactory = src._jsonFactory.copy();
+        _jsonFactory.setCodec(this);
+        _subtypeResolver = src._subtypeResolver;
+        _rootNames = new RootNameLookup();
+        _typeFactory = src._typeFactory;
+        _serializationConfig = src._serializationConfig;
+        HashMap<ClassKey,Class<?>> mixins = new HashMap<ClassKey,Class<?>>(src._mixInAnnotations);
+        _serializationConfig = new SerializationConfig(src._serializationConfig, mixins);
+        _deserializationConfig = new DeserializationConfig(src._deserializationConfig, mixins);
+        _serializerProvider = src._serializerProvider;
+        _deserializationContext = src._deserializationContext;
+
+        // Default serializer factory is stateless, can just assign
+        _serializerFactory = src._serializerFactory;
+    }
+    
+    /**
+     * Constructs instance that uses specified {@link JsonFactory}
+     * for constructing necessary {@link JsonParser}s and/or
+     * {@link JsonGenerator}s, and uses given providers for accessing
+     * serializers and deserializers.
+     * 
+     * @param jf JsonFactory to use: if null, a new {@link MappingJsonFactory} will be constructed
+     * @param sp SerializerProvider to use: if null, a {@link SerializerProvider} will be constructed
+     * @param dc Blueprint deserialization context instance to use for creating
+     *    actual context objects; if null, will construct standard
+     *    {@link DeserializationContext}
+     */
+    public ObjectMapper(JsonFactory jf,
+            DefaultSerializerProvider sp, DefaultDeserializationContext dc)
+    {
+        /* 02-Mar-2009, tatu: Important: we MUST default to using
+         *   the mapping factory, otherwise tree serialization will
+         *   have problems with POJONodes.
+         * 03-Jan-2010, tatu: and obviously we also must pass 'this',
+         *    to create actual linking.
+         */
+        if (jf == null) {
+            _jsonFactory = new MappingJsonFactory(this);
+        } else {
+            _jsonFactory = jf;
+            if (jf.getCodec() == null) { // as per [JACKSON-741]
+                _jsonFactory.setCodec(this);
+            }
+        }
+        _subtypeResolver = new StdSubtypeResolver();
+        _rootNames = new RootNameLookup();
+        // and default type factory is shared one
+        _typeFactory = TypeFactory.defaultInstance();
+        _serializationConfig = new SerializationConfig(DEFAULT_BASE,
+                    _subtypeResolver, _mixInAnnotations);
+        _deserializationConfig = new DeserializationConfig(DEFAULT_BASE,
+                    _subtypeResolver, _mixInAnnotations);
+        _serializerProvider = (sp == null) ? new DefaultSerializerProvider.Impl() : sp;
+        _deserializationContext = (dc == null) ?
+                new DefaultDeserializationContext.Impl(BeanDeserializerFactory.instance) : dc;
+
+        // Default serializer factory is stateless, can just assign
+        _serializerFactory = BeanSerializerFactory.instance;
+    }
+
+    /**
+     * Method for creating a new {@link ObjectMapper} instance that
+     * has same initial configuration as this instance. Note that this
+     * also requires making a copy of the underlying {@link JsonFactory}
+     * instance.
+     *<p>
+     * Method is typically
+     * used when multiple, differently configured mappers are needed.
+     * Although configuration is shared, cached serializers and deserializers
+     * are NOT shared, which means that the new instance may be re-configured
+     * before use; meaning that it behaves the same way as if an instance
+     * was constructed from scratch.
+     * 
+     * @since 2.1
+     */
+    public ObjectMapper copy()
+    {
+        _checkInvalidCopy(ObjectMapper.class);
+        return new ObjectMapper(this);
+    }
+
+    /**
+     * @since 2.1
+     * @param exp
+     */
+    protected void _checkInvalidCopy(Class<?> exp)
+    {
+        if (getClass() != exp) {
+            throw new IllegalStateException("Failed copy(): "+getClass().getName()
+                    +" (version: "+version()+") does not override copy(); it has to");
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Versioned impl
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will return version information stored in and read from jar
+     * that contains this class.
+     */
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+    
+    /*
+    /**********************************************************
+    /* Module registration, discovery
+    /**********************************************************
+     */
+
+    /**
+     * Method for registering a module that can extend functionality
+     * provided by this mapper; for example, by adding providers for
+     * custom serializers and deserializers.
+     * 
+     * @param module Module to register
+     */
+    public ObjectMapper registerModule(Module module)
+    {
+        /* Let's ensure we have access to name and version information, 
+         * even if we do not have immediate use for either. This way we know
+         * that they will be available from beginning
+         */
+        String name = module.getModuleName();
+        if (name == null) {
+            throw new IllegalArgumentException("Module without defined name");
+        }
+        Version version = module.version();
+        if (version == null) {
+            throw new IllegalArgumentException("Module without defined version");
+        }
+
+        final ObjectMapper mapper = this;
+        
+        // And then call registration
+        module.setupModule(new Module.SetupContext()
+        {
+            // // // Accessors
+
+            @Override
+            public Version getMapperVersion() {
+                return version();
+            }
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public <C extends ObjectCodec> C getOwner() {
+                // why do we need the cast here?!?
+                return (C) mapper;
+            }
+
+            @Override
+            public TypeFactory getTypeFactory() {
+                return _typeFactory;
+            }
+            
+            @Override
+            public boolean isEnabled(MapperFeature f) {
+                return mapper.isEnabled(f);
+            }
+
+            @Override
+            public boolean isEnabled(DeserializationFeature f) {
+                return mapper.isEnabled(f);
+            }
+            
+            @Override
+            public boolean isEnabled(SerializationFeature f) {
+                return mapper.isEnabled(f);
+            }
+
+            @Override
+            public boolean isEnabled(JsonFactory.Feature f) {
+                return mapper.isEnabled(f);
+            }
+
+            @Override
+            public boolean isEnabled(JsonParser.Feature f) {
+                return mapper.isEnabled(f);
+            }
+            
+            @Override
+            public boolean isEnabled(JsonGenerator.Feature f) {
+                return mapper.isEnabled(f);
+            }
+            
+            // // // Methods for registering handlers: deserializers
+            
+            @Override
+            public void addDeserializers(Deserializers d) {
+                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalDeserializers(d);
+                mapper._deserializationContext = mapper._deserializationContext.with(df);
+            }
+
+            @Override
+            public void addKeyDeserializers(KeyDeserializers d) {
+                DeserializerFactory df = mapper._deserializationContext._factory.withAdditionalKeyDeserializers(d);
+                mapper._deserializationContext = mapper._deserializationContext.with(df);
+            }
+
+            @Override
+            public void addBeanDeserializerModifier(BeanDeserializerModifier modifier) {
+                DeserializerFactory df = mapper._deserializationContext._factory.withDeserializerModifier(modifier);
+                mapper._deserializationContext = mapper._deserializationContext.with(df);
+            }
+            
+            // // // Methods for registering handlers: serializers
+            
+            @Override
+            public void addSerializers(Serializers s) {
+                mapper._serializerFactory = mapper._serializerFactory.withAdditionalSerializers(s);
+            }
+
+            @Override
+            public void addKeySerializers(Serializers s) {
+                mapper._serializerFactory = mapper._serializerFactory.withAdditionalKeySerializers(s);
+            }
+            
+            @Override
+            public void addBeanSerializerModifier(BeanSerializerModifier modifier) {
+                mapper._serializerFactory = mapper._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);
+            }
+
+            @Override
+            public void addTypeModifier(TypeModifier modifier) {
+                TypeFactory f = mapper._typeFactory;
+                f = f.withModifier(modifier);
+                mapper.setTypeFactory(f);
+            }
+
+            @Override
+            public void addValueInstantiators(ValueInstantiators instantiators) {
+                DeserializerFactory df = mapper._deserializationContext._factory.withValueInstantiators(instantiators);
+                mapper._deserializationContext = mapper._deserializationContext.with(df);
+            }
+
+            @Override
+            public void setClassIntrospector(ClassIntrospector ci) {
+                mapper._deserializationConfig = mapper._deserializationConfig.with(ci);
+                mapper._serializationConfig = mapper._serializationConfig.with(ci);
+            }
+
+            @Override
+            public void insertAnnotationIntrospector(AnnotationIntrospector ai) {
+                mapper._deserializationConfig = mapper._deserializationConfig.withInsertedAnnotationIntrospector(ai);
+                mapper._serializationConfig = mapper._serializationConfig.withInsertedAnnotationIntrospector(ai);
+            }
+            
+            @Override
+            public void appendAnnotationIntrospector(AnnotationIntrospector ai) {
+                mapper._deserializationConfig = mapper._deserializationConfig.withAppendedAnnotationIntrospector(ai);
+                mapper._serializationConfig = mapper._serializationConfig.withAppendedAnnotationIntrospector(ai);
+            }
+
+            @Override
+            public void registerSubtypes(Class<?>... subtypes) {
+                mapper.registerSubtypes(subtypes);
+            }
+
+            @Override
+            public void registerSubtypes(NamedType... subtypes) {
+                mapper.registerSubtypes(subtypes);
+            }
+            
+            @Override
+            public void setMixInAnnotations(Class<?> target, Class<?> mixinSource) {
+                mapper.addMixInAnnotations(target, mixinSource);
+            }
+            
+            @Override
+            public void addDeserializationProblemHandler(DeserializationProblemHandler handler) {
+                mapper.addHandler(handler);
+            }
+        });
+        return this;
+    }
+
+    /**
+     * Convenience method for registering specified modules in order;
+     * functionally equivalent to:
+     *<pre>
+     *   for (Module module : modules) {
+     *      registerModule(module);
+     *   }
+     *</pre>
+     * 
+     * @since 2.2
+     */
+    public ObjectMapper registerModules(Module... modules)
+    {
+        for (Module module : modules) {
+            registerModule(module);
+        }
+        return this;
+    }
+
+    /**
+     * Convenience method for registering specified modules in order;
+     * functionally equivalent to:
+     *<pre>
+     *   for (Module module : modules) {
+     *      registerModule(module);
+     *   }
+     *</pre>
+     * 
+     * @since 2.2
+     */
+    public ObjectMapper registerModules(Iterable<Module> modules)
+    {
+        for (Module module : modules) {
+            registerModule(module);
+        }
+        return this;
+    }
+    
+    /**
+     * Method for locating available methods, using JDK {@link ServiceLoader}
+     * facility, along with module-provided SPI.
+     *<p>
+     * Note that method does not do any caching, so calls should be considered
+     * potentially expensive.
+     * 
+     * @since 2.2
+     */
+    public static List<Module> findModules() {
+        return findModules(null);
+    }
+
+    /**
+     * Method for locating available methods, using JDK {@link ServiceLoader}
+     * facility, along with module-provided SPI.
+     *<p>
+     * Note that method does not do any caching, so calls should be considered
+     * potentially expensive.
+     * 
+     * @since 2.2
+     */
+    public static List<Module> findModules(ClassLoader classLoader)
+    {
+        ArrayList<Module> modules = new ArrayList<Module>();
+        ServiceLoader<Module> loader = (classLoader == null) ?
+                ServiceLoader.load(Module.class) : ServiceLoader.load(Module.class, classLoader);
+        for (Module module : loader) {
+            modules.add(module);
+        }
+        return modules;
+    }
+
+    /**
+     * Convenience method that is functionally equivalent to:
+     *<code>
+     *   mapper.registerModules(mapper.findModules());
+     *<code>
+     *<p>
+     * As with {@link #findModules()}, no caching is done for modules, so care
+     * needs to be taken to either create and share a single mapper instance;
+     * or to cache introspected set of modules.
+     *
+     * @since 2.2
+     */
+    public ObjectMapper findAndRegisterModules() {
+        return registerModules(findModules());
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration: main config object access
+    /**********************************************************
+     */
+
+    /**
+     * Method that returns the shared default {@link SerializationConfig}
+     * object that defines configuration settings for serialization.
+     *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of config object.
+     */
+    public SerializationConfig getSerializationConfig() {
+        return _serializationConfig;
+    }
+
+    /**
+     * Method that returns
+     * the shared default {@link DeserializationConfig} object
+     * that defines configuration settings for deserialization.
+     *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of config object.
+     */
+    public DeserializationConfig getDeserializationConfig() {
+        return _deserializationConfig;
+    }
+    
+    /**
+     * Method for getting current {@link DeserializationContext}.
+      *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of context object.
+    */
+    public DeserializationContext getDeserializationContext() {
+        return _deserializationContext;
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration: ser/deser factory, provider access
+    /**********************************************************
+     */
+    
+    /**
+     * Method for setting specific {@link SerializerFactory} to use
+     * for constructing (bean) serializers.
+     */
+    public ObjectMapper setSerializerFactory(SerializerFactory f) {
+        _serializerFactory = f;
+        return this;
+    }
+
+    /**
+     * Method for getting current {@link SerializerFactory}.
+      *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of factory object.
+     */
+    public SerializerFactory getSerializerFactory() {
+        return _serializerFactory;
+    }
+
+    /**
+     * Method for setting specific {@link SerializerProvider} to use
+     * for handling caching of {@link JsonSerializer} instances.
+     */
+    public ObjectMapper setSerializerProvider(DefaultSerializerProvider p) {
+        _serializerProvider = p;
+        return this;
+    }
+
+    public SerializerProvider getSerializerProvider() {
+        return _serializerProvider;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration: mix-in annotations
+    /**********************************************************
+     */
+    
+    /**
+     * Method to use for defining mix-in annotations to use for augmenting
+     * annotations that processable (serializable / deserializable)
+     * classes have.
+     * Mixing in is done when introspecting class annotations and properties.
+     * Map passed contains keys that are target classes (ones to augment
+     * with new annotation overrides), and values that are source classes
+     * (have annotations to use for augmentation).
+     * Annotations from source classes (and their supertypes)
+     * will <b>override</b>
+     * annotations that target classes (and their super-types) have.
+     */
+    public final void setMixInAnnotations(Map<Class<?>, Class<?>> sourceMixins)
+    {
+        _mixInAnnotations.clear();
+        if (sourceMixins != null && sourceMixins.size() > 0) {
+            for (Map.Entry<Class<?>,Class<?>> en : sourceMixins.entrySet()) {
+                _mixInAnnotations.put(new ClassKey(en.getKey()), en.getValue());
+            }
+        }
+    }
+
+    /**
+     * Method to use for adding mix-in annotations to use for augmenting
+     * specified class or interface. All annotations from
+     * <code>mixinSource</code> are taken to override annotations
+     * that <code>target</code> (or its supertypes) has.
+     *
+     * @param target Class (or interface) whose annotations to effectively override
+     * @param mixinSource Class (or interface) whose annotations are to
+     *   be "added" to target's annotations, overriding as necessary
+     */
+    public final void addMixInAnnotations(Class<?> target, Class<?> mixinSource)
+    {
+        _mixInAnnotations.put(new ClassKey(target), mixinSource);
+    }
+
+    public final Class<?> findMixInClassFor(Class<?> cls) {
+        return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls));
+    }
+
+    public final int mixInCount() {
+        return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size();
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, introspection
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing currently configured visibility checker;
+     * object used for determining whether given property element
+     * (method, field, constructor) can be auto-detected or not.
+     */
+    public VisibilityChecker<?> getVisibilityChecker() {
+        return _serializationConfig.getDefaultVisibilityChecker();
+    }
+
+    /**
+     * Method for setting currently configured visibility checker;
+     * 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.
+     */    
+    public void setVisibilityChecker(VisibilityChecker<?> vc) {
+        _deserializationConfig = _deserializationConfig.with(vc);
+        _serializationConfig = _serializationConfig.with(vc);
+    }
+
+    /**
+     * Convenience method that allows changing configuration for
+     * underlying {@link VisibilityChecker}s, to change details of what kinds of
+     * properties are auto-detected.
+     * Basically short cut for doing:
+     *<pre>
+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().withVisibility(forMethod, visibility)
+     *  );
+     *</pre>
+     * one common use case would be to do:
+     *<pre>
+     *  mapper.setVisibility(JsonMethod.FIELD, Visibility.ANY);
+     *</pre>
+     * which would make all member fields serializable without further annotations,
+     * instead of just public fields (default setting).
+     * 
+     * @param forMethod Type of property descriptor affected (field, getter/isGetter,
+     *     setter, creator)
+     * @param visibility Minimum visibility to require for the property descriptors of type
+     * 
+     * @return Modified mapper instance (that is, "this"), to allow chaining
+     *    of configuration calls
+     */
+    public ObjectMapper setVisibility(PropertyAccessor forMethod, JsonAutoDetect.Visibility visibility)
+    {
+        _deserializationConfig = _deserializationConfig.withVisibility(forMethod, visibility);
+        _serializationConfig = _serializationConfig.withVisibility(forMethod, visibility);
+        return this;
+    }
+    
+    /**
+     * Method for accessing subtype resolver in use.
+     */
+    public SubtypeResolver getSubtypeResolver() {
+        return _subtypeResolver;
+    }
+
+    /**
+     * Method for setting custom subtype resolver to use.
+     */
+    public ObjectMapper setSubtypeResolver(SubtypeResolver str) {
+        _subtypeResolver = str;
+        _deserializationConfig = _deserializationConfig.with(str);
+        _serializationConfig = _serializationConfig.with(str);
+        return this;
+    }
+
+    /**
+     * Method for changing {@link AnnotationIntrospector} used by this
+     * mapper instance for both serialization and deserialization
+     */
+    public ObjectMapper setAnnotationIntrospector(AnnotationIntrospector ai) {
+        _serializationConfig = _serializationConfig.with(ai);
+        _deserializationConfig = _deserializationConfig.with(ai);
+        return this;
+    }
+
+    /**
+     * Method for changing {@link AnnotationIntrospector} instances used
+     * by this mapper instance for serialization and deserialization,
+     * specifying them separately so that different introspection can be
+     * used for different aspects
+     * 
+     * @since 2.1
+     * 
+     * @param serializerAI {@link AnnotationIntrospector} to use for configuring
+     *    serialization
+     * @param deserializerAI {@link AnnotationIntrospector} to use for configuring
+     *    deserialization
+     */
+    public ObjectMapper setAnnotationIntrospectors(AnnotationIntrospector serializerAI,
+            AnnotationIntrospector deserializerAI) {
+        _serializationConfig = _serializationConfig.with(serializerAI);
+        _deserializationConfig = _deserializationConfig.with(deserializerAI);
+        return this;
+    }
+    
+    /**
+     * Method for setting custom property naming strategy to use.
+     */
+    public ObjectMapper setPropertyNamingStrategy(PropertyNamingStrategy s) {
+        _serializationConfig = _serializationConfig.with(s);
+        _deserializationConfig = _deserializationConfig.with(s);
+        return this;
+    }
+
+    /**
+     * Method for setting defalt POJO property inclusion strategy for serialization.
+     */
+    public ObjectMapper setSerializationInclusion(JsonInclude.Include incl) {
+        _serializationConfig = _serializationConfig.withSerializationInclusion(incl);
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Type information configuration (1.5+)
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method that is equivalent to calling
+     *<pre>
+     *  enableObjectTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
+     *</pre>
+     */
+    public ObjectMapper enableDefaultTyping() {
+        return enableDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
+    }
+
+    /**
+     * Convenience method that is equivalent to calling
+     *<pre>
+     *  enableObjectTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
+     *</pre>
+     */
+    public ObjectMapper enableDefaultTyping(DefaultTyping dti) {
+        return enableDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
+    }
+
+    /**
+     * Method for enabling automatic inclusion of type information, needed
+     * for proper deserialization of polymorphic types (unless types
+     * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}).
+     * 
+     * @param applicability Defines kinds of types for which additional type information
+     *    is added; see {@link DefaultTyping} for more information.
+     */
+    public ObjectMapper enableDefaultTyping(DefaultTyping applicability, JsonTypeInfo.As includeAs)
+    {
+        TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(applicability);
+        // we'll always use full class name, when using defaulting
+        typer = typer.init(JsonTypeInfo.Id.CLASS, null);
+        typer = typer.inclusion(includeAs);
+        return setDefaultTyping(typer);
+    }
+
+    /**
+     * Method for enabling automatic inclusion of type information -- needed
+     * for proper deserialization of polymorphic types (unless types
+     * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) --
+     * 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)
+     */
+    public ObjectMapper enableDefaultTypingAsProperty(DefaultTyping applicability, String propertyName)
+    {
+        TypeResolverBuilder<?> typer = new DefaultTypeResolverBuilder(applicability);
+        // we'll always use full class name, when using defaulting
+        typer = typer.init(JsonTypeInfo.Id.CLASS, null);
+        typer = typer.inclusion(JsonTypeInfo.As.PROPERTY);
+        typer = typer.typeProperty(propertyName);
+        return setDefaultTyping(typer);
+    }
+    
+    /**
+     * Method for disabling automatic inclusion of type information; if so, only
+     * explicitly annotated types (ones with
+     * {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) will have
+     * additional embedded type information.
+     */
+    public ObjectMapper disableDefaultTyping() {
+        return setDefaultTyping(null);
+    }
+
+    /**
+     * 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.
+     * 
+     * @param typer Type information inclusion handler
+     */
+    public ObjectMapper setDefaultTyping(TypeResolverBuilder<?> typer) {
+        _deserializationConfig = _deserializationConfig.with(typer);
+        _serializationConfig = _serializationConfig.with(typer);
+        return this;
+    }
+
+    /**
+     * Method for registering specified class as a subtype, so that
+     * typename-based resolution can link supertypes to subtypes
+     * (as an alternative to using annotations).
+     * Type for given class is determined from appropriate annotation;
+     * or if missing, default name (unqualified class name)
+     */
+    public void registerSubtypes(Class<?>... classes) {
+        getSubtypeResolver().registerSubtypes(classes);
+    }
+
+    /**
+     * Method for registering specified class as a subtype, so that
+     * typename-based resolution can link supertypes to subtypes
+     * (as an alternative to using annotations).
+     * Name may be provided as part of argument, but if not will
+     * be based on annotations or use default name (unqualified
+     * class name).
+     */
+    public void registerSubtypes(NamedType... types) {
+        getSubtypeResolver().registerSubtypes(types);
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration, basic type handling
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for getting currently configured {@link TypeFactory} instance.
+     */
+    public TypeFactory getTypeFactory() {
+        return _typeFactory;
+    }
+
+    /**
+     * Method that can be used to override {@link TypeFactory} instance
+     * used by this mapper.
+     *<p>
+     * Note: will also set {@link TypeFactory} that deserialization and
+     * serialization config objects use.
+     */
+    public ObjectMapper setTypeFactory(TypeFactory f)
+    {
+        _typeFactory = f;
+        _deserializationConfig = _deserializationConfig.with(f);
+        _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
+     * context.
+     */
+    public JavaType constructType(Type t) {
+        return _typeFactory.constructType(t);
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, deserialization
+    /**********************************************************
+     */
+    
+    /**
+     * Method for specifying {@link JsonNodeFactory} to use for
+     * constructing root level tree nodes (via method
+     * {@link #createObjectNode}
+     */
+    public ObjectMapper setNodeFactory(JsonNodeFactory f) {
+        _deserializationConfig = _deserializationConfig.with(f);
+        return this;
+    }
+
+    /**
+     * Method for adding specified {@link DeserializationProblemHandler}
+     * to be used for handling specific problems during deserialization.
+     */
+    public ObjectMapper addHandler(DeserializationProblemHandler h) {
+        _deserializationConfig = _deserializationConfig.withHandler(h);
+        return this;
+    }
+
+    /**
+     * Method for removing all registered {@link DeserializationProblemHandler}s
+     * instances from this mapper.
+     */
+    public ObjectMapper clearProblemHandlers() {
+        _deserializationConfig = _deserializationConfig.withNoProblemHandlers();
+        return this;
+    }
+    
+    
+    /*
+    /**********************************************************
+    /* Configuration, serialization
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method that is equivalent to:
+     *<pre>
+     *  mapper.setFilters(mapper.getSerializationConfig().withFilters(filterProvider));
+     *</pre>
+     *<p>
+     * Note that usually it is better to use method {@link #writer(FilterProvider)};
+     * however, sometimes
+     * this method is more convenient. For example, some frameworks only allow configuring
+     * of ObjectMapper instances and not ObjectWriters.
+     */
+    public void setFilters(FilterProvider filterProvider) {
+        _serializationConfig = _serializationConfig.withFilters(filterProvider);
+    }
+
+    /**
+     * Method that will configure default {@link Base64Variant} that
+     * <code>byte[]</code> serializers and deserializers will use.
+     * 
+     * @param v Base64 variant to use
+     * 
+     * @return This mapper, for convenience to allow chaining
+     * 
+     * @since 2.1
+     */
+    public ObjectMapper setBase64Variant(Base64Variant v) {
+        _serializationConfig = _serializationConfig.with(v);
+        _deserializationConfig = _deserializationConfig.with(v);
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, other
+    /**********************************************************
+     */
+
+    /**
+     * 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.
+     *
+     * @return {@link JsonFactory} that this mapper uses when it needs to
+     *   construct Json parser and generators
+     */
+    @Override
+    public JsonFactory getFactory() { return _jsonFactory; }
+    
+    /**
+     * @deprecated Since 2.1: Use {@link #getFactory} instead
+     */
+    @Deprecated
+    @Override
+    public JsonFactory getJsonFactory() { return _jsonFactory; }
+
+    /**
+     * Method for configuring the default {@link DateFormat} to use when serializing time
+     * values as Strings, and deserializing from JSON Strings.
+     * This is preferably to directly modifying {@link SerializationConfig} and
+     * {@link DeserializationConfig} instances.
+     * If you need per-request configuration, use {@link #writer(DateFormat)} to
+     * create properly configured {@link ObjectWriter} and use that; this because
+     * {@link ObjectWriter}s are thread-safe whereas ObjectMapper itself is only
+     * thread-safe when configuring methods (such as this one) are NOT called.
+     */
+    public ObjectMapper setDateFormat(DateFormat dateFormat)
+    {
+        _deserializationConfig = _deserializationConfig.with(dateFormat);
+        _serializationConfig = _serializationConfig.with(dateFormat);
+        return this;
+    }
+
+    /**
+     * Method for configuring {@link HandlerInstantiator} to use for creating
+     * instances of handlers (such as serializers, deserializers, type and type
+     * id resolvers), given a class.
+     *
+     * @param hi Instantiator to use; if null, use the default implementation
+     */
+    public Object setHandlerInstantiator(HandlerInstantiator hi)
+    {
+        _deserializationConfig = _deserializationConfig.with(hi);
+        _serializationConfig = _serializationConfig.with(hi);
+        return this;
+    }
+    
+    /**
+     * Method for configuring {@link InjectableValues} which used to find
+     * values to inject.
+     */
+    public ObjectMapper setInjectableValues(InjectableValues injectableValues) {
+        _injectableValues = injectableValues;
+        return this;
+    }
+    
+    /**
+     * Method for overriding default locale to use for formatting.
+     * Default value used is {@link Locale#getDefault()}.
+     */
+    public ObjectMapper setLocale(Locale l) {
+        _deserializationConfig = _deserializationConfig.with(l);
+        _serializationConfig = _serializationConfig.with(l);
+        return this;
+    }
+
+    /**
+     * Method for overriding default TimeZone to use for formatting.
+     * Default value used is {@link TimeZone#getDefault()}.
+     */
+    public ObjectMapper setTimeZone(TimeZone tz) {
+        _deserializationConfig = _deserializationConfig.with(tz);
+        _serializationConfig = _serializationConfig.with(tz);
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, simple features
+    /**********************************************************
+     */
+
+    /**
+     * Method for changing state of an on/off mapper feature for
+     * this mapper instance.
+     */
+    public ObjectMapper configure(MapperFeature f, boolean state) {
+        _serializationConfig = state ?
+                _serializationConfig.with(f) : _serializationConfig.without(f);
+        _deserializationConfig = state ?
+                _deserializationConfig.with(f) : _deserializationConfig.without(f);
+        return this;
+    }
+    
+    /**
+     * Method for changing state of an on/off serialization feature for
+     * this object mapper.
+     */
+    public ObjectMapper configure(SerializationFeature f, boolean state) {
+        _serializationConfig = state ?
+                _serializationConfig.with(f) : _serializationConfig.without(f);
+        return this;
+    }
+
+    /**
+     * Method for changing state of an on/off deserialization feature for
+     * this object mapper.
+     */
+    public ObjectMapper configure(DeserializationFeature f, boolean state) {
+        _deserializationConfig = state ?
+                _deserializationConfig.with(f) : _deserializationConfig.without(f);
+        return this;
+    }
+
+    /**
+     * Method for changing state of an on/off {@link JsonParser} feature for
+     * {@link JsonFactory} instance this object mapper uses.
+     *<p>
+     * This is method is basically a shortcut method for calling
+     * {@link JsonFactory#enable} on the shared
+     * {@link JsonFactory} this mapper uses (which is accessible
+     * using {@link #getJsonFactory}).
+     */
+    public ObjectMapper configure(JsonParser.Feature f, boolean state) {
+        _jsonFactory.configure(f, state);
+        return this;
+    }
+
+    /**
+     * Method for changing state of an on/off {@link JsonGenerator} feature for
+     * {@link JsonFactory} instance this object mapper uses.
+     *<p>
+     * This is method is basically a shortcut method for calling
+     * {@link JsonFactory#enable} on the shared
+     * {@link JsonFactory} this mapper uses (which is accessible
+     * using {@link #getJsonFactory}).
+     */
+    public ObjectMapper configure(JsonGenerator.Feature f, boolean state) {
+        _jsonFactory.configure(f, state);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link MapperConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper enable(MapperFeature... f) {
+        _deserializationConfig = _deserializationConfig.with(f);
+        _serializationConfig = _serializationConfig.with(f);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper disable(MapperFeature... f) {
+        _deserializationConfig = _deserializationConfig.without(f);
+        _serializationConfig = _serializationConfig.without(f);
+        return this;
+    }
+    
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper enable(DeserializationFeature feature) {
+        _deserializationConfig = _deserializationConfig.with(feature);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper enable(DeserializationFeature first,
+            DeserializationFeature... f) {
+        _deserializationConfig = _deserializationConfig.with(first, f);
+        return this;
+    }
+    
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper disable(DeserializationFeature feature) {
+        _deserializationConfig = _deserializationConfig.without(feature);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper disable(DeserializationFeature first,
+            DeserializationFeature... f) {
+        _deserializationConfig = _deserializationConfig.without(first, f);
+        return this;
+    }
+    
+    /**
+     * Method for enabling specified {@link DeserializationConfig} feature.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper enable(SerializationFeature f) {
+        _serializationConfig = _serializationConfig.with(f);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper enable(SerializationFeature first,
+            SerializationFeature... f) {
+        _serializationConfig = _serializationConfig.with(first, f);
+        return this;
+    }
+    
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper disable(SerializationFeature f) {
+        _serializationConfig = _serializationConfig.without(f);
+        return this;
+    }
+
+    /**
+     * Method for enabling specified {@link DeserializationConfig} features.
+     * Modifies and returns this instance; no new object is created.
+     */
+    public ObjectMapper disable(SerializationFeature first,
+            SerializationFeature... f) {
+        _serializationConfig = _serializationConfig.without(first, f);
+        return this;
+    }
+
+    /**
+     * Method for checking whether given Mapper
+     * feature is enabled.
+     */
+    public boolean isEnabled(MapperFeature f) {
+        // ok to use either one, should be kept in sync
+        return _serializationConfig.isEnabled(f);
+    }
+
+    /**
+     * Method for checking whether given serialization-specific
+     * feature is enabled.
+     */
+    public boolean isEnabled(SerializationFeature f) {
+        return _serializationConfig.isEnabled(f);
+    }
+    
+    /**
+     * Method for checking whether given deserialization-specific
+     * feature is enabled.
+     */
+    public boolean isEnabled(DeserializationFeature f) {
+        return _deserializationConfig.isEnabled(f);
+    }
+
+    /**
+     * Convenience method, equivalent to:
+     *<pre>
+     *  getJsonFactory().isEnabled(f);
+     *</pre>
+     */
+    public boolean isEnabled(JsonFactory.Feature f) {
+        return _jsonFactory.isEnabled(f);
+    }
+
+    /**
+     * Convenience method, equivalent to:
+     *<pre>
+     *  getJsonFactory().isEnabled(f);
+     *</pre>
+     */
+    public boolean isEnabled(JsonParser.Feature f) {
+        return _jsonFactory.isEnabled(f);
+    }
+    
+    /**
+     * Convenience method, equivalent to:
+     *<pre>
+     *  getJsonFactory().isEnabled(f);
+     *</pre>
+     */
+    public boolean isEnabled(JsonGenerator.Feature f) {
+        return _jsonFactory.isEnabled(f);
+    }
+    
+    /**
+     * Method that can be used to get hold of {@link JsonNodeFactory}
+     * that this mapper will use when directly constructing
+     * root {@link JsonNode} instances for Trees.
+     *<p>
+     * Note: this is just a shortcut for calling
+     *<pre>
+     *   getDeserializationConfig().getNodeFactory()
+     *</pre>
+     */
+    public JsonNodeFactory getNodeFactory() {
+        return _deserializationConfig.getNodeFactory();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API (from ObjectCodec): deserialization
+    /* (mapping from JSON to Java types);
+    /* main methods
+    /**********************************************************
+     */
+
+    /**
+     * Method to deserialize JSON content into a non-container
+     * type (it can be an array type, however): typically a bean, array
+     * or a wrapper type (like {@link java.lang.Boolean}).
+     *<p>
+     * 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.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueType));
+    } 
+
+    /**
+     * Method to deserialize JSON content into a Java type, reference
+     * to which is passed as argument. Type is passed using so-called
+     * "super type token" (see )
+     * and specifically needs to be used if the root type is a 
+     * parameterized (generic) container type.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readValue(getDeserializationConfig(), jp, _typeFactory.constructType(valueTypeRef));
+    }
+
+    /**
+     * Method to deserialize JSON content into a Java type, reference
+     * to which is passed as argument. Type is passed using 
+     * Jackson specific type; instance of which can be constructed using
+     * {@link TypeFactory}.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public final <T> T readValue(JsonParser jp, ResolvedType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readValue(getDeserializationConfig(), jp, (JavaType) valueType);
+    }
+
+    /**
+     * Type-safe overloaded method, basically alias for {@link #readValue(JsonParser, ResolvedType)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readValue(getDeserializationConfig(), jp, valueType);
+    }
+    
+    /**
+     * Method to deserialize JSON content as tree expressed
+     * using set of {@link JsonNode} instances. Returns
+     * root of the resulting tree (where root can consist
+     * of just a single node if the current event is a
+     * value event, not container).
+     */
+    @Override
+    public <T extends TreeNode> T readTree(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        /* 02-Mar-2009, tatu: One twist; deserialization provider
+         *   will map JSON null straight into Java null. But what
+         *   we want to return is the "null node" instead.
+         */
+        /* 05-Aug-2011, tatu: Also, must check for EOF here before
+         *   calling readValue(), since that'll choke on it otherwise
+         */
+        DeserializationConfig cfg = getDeserializationConfig();
+        JsonToken t = jp.getCurrentToken();
+        if (t == null) {
+            t = jp.nextToken();
+            if (t == null) {
+                return null;
+            }
+        }
+        JsonNode n = (JsonNode) _readValue(cfg, jp, JSON_NODE_TYPE);
+        if (n == null) {
+            n = getNodeFactory().nullNode();
+        }
+        @SuppressWarnings("unchecked")
+        T result = (T) n;
+        return result;
+    }
+
+    /**
+     * Method for reading sequence of Objects from parser stream.
+     * Sequence can be either root-level "unwrapped" sequence (without surrounding
+     * JSON array), or a sequence contained in a JSON Array.
+     * In either case {@link JsonParser} must point to the first token of
+     * the first element, OR not point to any token (in which case it is advanced
+     * to the next token). This means, specifically, that for wrapped sequences,
+     * parser MUST NOT point to the surrounding <code>START_ARRAY</code> but rather
+     * to the token following it.
+     *<p>
+     * Note that {@link ObjectReader} has more complete set of variants.
+     */
+    @Override
+    public <T> MappingIterator<T> readValues(JsonParser jp, ResolvedType valueType)
+        throws IOException, JsonProcessingException
+    {
+        return readValues(jp, (JavaType) valueType);
+    }
+
+    /**
+     * Type-safe overloaded method, basically alias for {@link #readValues(JsonParser, ResolvedType)}.
+     */
+    public <T> MappingIterator<T> readValues(JsonParser jp, JavaType valueType)
+            throws IOException, JsonProcessingException
+    {
+        DeserializationConfig config = getDeserializationConfig();
+        DeserializationContext ctxt = createDeserializationContext(jp, config);
+        JsonDeserializer<?> deser = _findRootDeserializer(ctxt, valueType);
+        // false -> do NOT close JsonParser (since caller passed it)
+        return new MappingIterator<T>(valueType, jp, ctxt, deser,
+                false, null);
+    }
+
+    /**
+     * Type-safe overloaded method, basically alias for {@link #readValues(JsonParser, ResolvedType)}.
+     */
+    @Override
+    public <T> MappingIterator<T> readValues(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonProcessingException
+    {
+        return readValues(jp, _typeFactory.constructType(valueType));
+    }
+
+    /**
+     * Method for reading sequence of Objects from parser stream.
+     */
+    @Override
+    public <T> MappingIterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException
+    {
+        return readValues(jp, _typeFactory.constructType(valueTypeRef));
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API not included in ObjectCodec: deserialization
+    /* (mapping from JSON to Java types)
+    /**********************************************************
+     */
+
+    /**
+     * Method to deserialize JSON content as tree expressed
+     * using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist
+     * of just a single node if the current event is a
+     * value event, not container).
+     *
+     * @param in Input stream used to read JSON content
+     *   for building the JSON tree.
+     */
+    public JsonNode readTree(InputStream in)
+        throws IOException, JsonProcessingException
+    {
+        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(in), JSON_NODE_TYPE);
+        return (n == null) ? NullNode.instance : n;
+    }
+
+    /**
+     * Method to deserialize JSON content as tree expressed
+     * using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist
+     * of just a single node if the current event is a
+     * value event, not container).
+     *
+     * @param r Reader used to read JSON content
+     *   for building the JSON tree.
+     */
+    public JsonNode readTree(Reader r)
+        throws IOException, JsonProcessingException
+    {
+        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(r), JSON_NODE_TYPE);
+        return (n == null) ? NullNode.instance : n;
+    }
+
+    /**
+     * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist of just a single node if the current
+     * event is a value event, not container).
+     *
+     * @param content JSON content to parse to build the JSON tree.
+     */
+    public JsonNode readTree(String content)
+        throws IOException, JsonProcessingException
+    {
+        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE);
+        return (n == null) ? NullNode.instance : n;
+    }
+
+    /**
+     * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist of just a single node if the current
+     * event is a value event, not container).
+     *
+     * @param content JSON content to parse to build the JSON tree.
+     */
+    public JsonNode readTree(byte[] content)
+        throws IOException, JsonProcessingException
+    {
+        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(content), JSON_NODE_TYPE);
+        return (n == null) ? NullNode.instance : n;
+    }
+    
+    /**
+     * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist of just a single node if the current
+     * event is a value event, not container).
+     *
+     * @param file File of which contents to parse as JSON for building a tree instance
+     */
+    public JsonNode readTree(File file)
+        throws IOException, JsonProcessingException
+    {
+        JsonNode n = (JsonNode) _readMapAndClose(_jsonFactory.createParser(file), JSON_NODE_TYPE);
+        return (n == null) ? NullNode.instance : n;
+    }
+
+    /**
+     * Method to deserialize JSON content as tree expressed using set of {@link JsonNode} instances.
+     * Returns root of the resulting tree (where root can consist of just a single node if the current
+     * event is a value event, not container).
+     *
+     * @param source URL to use for fetching contents to parse as JSON for building a tree instance
+     */
+    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 API (from ObjectCodec): serialization
+    /* (mapping from Java types to Json)
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using provided {@link JsonGenerator}.
+     */
+    @Override
+    public void writeValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        SerializationConfig config = getSerializationConfig();
+        // 10-Aug-2012, tatu: as per [Issue#12], must handle indentation:
+        if (config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
+            jgen.useDefaultPrettyPrinter();
+        }
+        if (config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
+            _writeCloseableValue(jgen, value, config);
+        } else {
+            _serializerProvider(config).serializeValue(jgen, value);
+            if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
+                jgen.flush();
+            }
+        }
+    }
+
+    /**
+     * Method to serialize given JSON Tree, using generator
+     * provided.
+     */
+    public void writeTree(JsonGenerator jgen, JsonNode rootNode)
+        throws IOException, JsonProcessingException
+    {
+        SerializationConfig config = getSerializationConfig();
+        _serializerProvider(config).serializeValue(jgen, rootNode);
+        if (config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
+            jgen.flush();
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Public API (from ObjectCodec): Tree Model support
+    /**********************************************************
+     */
+
+    /**
+     *<p>
+     * Note: return type is co-variant, as basic ObjectCodec
+     * abstraction can not refer to concrete node types (as it's
+     * part of core package, whereas impls are part of mapper
+     * package)
+     */
+    @Override    
+    public ObjectNode createObjectNode() {
+        return _deserializationConfig.getNodeFactory().objectNode();
+    }
+
+    /**
+     *<p>
+     * Note: return type is co-variant, as basic ObjectCodec
+     * abstraction can not refer to concrete node types (as it's
+     * part of core package, whereas impls are part of mapper
+     * package)
+     */
+    @Override
+    public ArrayNode createArrayNode() {
+        return _deserializationConfig.getNodeFactory().arrayNode();
+    }
+
+    /**
+     * Method for constructing a {@link JsonParser} out of JSON tree
+     * representation.
+     * 
+     * @param n Root node of the tree that resulting parser will read from
+     */
+    @Override
+    public JsonParser treeAsTokens(TreeNode n)
+    {
+        return new TreeTraversingParser((JsonNode) n, this);
+    }
+
+    /**
+     * Convenience conversion method that will bind data given JSON tree
+     * contains into specific value (usually bean) type.
+     *<p>
+     * Equivalent to:
+     *<pre>
+     *   objectMapper.convertValue(n, valueClass);
+     *</pre>
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T treeToValue(TreeNode n, Class<T> valueType)
+        throws JsonProcessingException
+    {
+        try {
+            // [Issue-11]: Simple cast when we just want to cast to, say, ObjectNode
+            // ... one caveat; while everything is Object.class, let's not take shortcut
+            if (valueType != Object.class && valueType.isAssignableFrom(n.getClass())) {
+                return (T) n;
+            }
+            return readValue(treeAsTokens(n), valueType);
+        } catch (JsonProcessingException e) {
+            throw e;
+        } catch (IOException e) { // should not occur, no real i/o...
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Reverse of {@link #treeToValue}; given a value (usually bean), will
+     * construct equivalent JSON Tree representation. Functionally same
+     * as if serializing value into JSON and parsing JSON as tree, but
+     * more efficient.
+     * 
+     * @param <T> Actual node type; usually either basic {@link JsonNode} or
+     *  {@link com.fasterxml.jackson.databind.node.ObjectNode}
+     * @param fromValue Bean value to convert
+     * @return Root node of the resulting JSON tree
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends JsonNode> T valueToTree(Object fromValue)
+        throws IllegalArgumentException
+    {
+        if (fromValue == null) return null;
+        TokenBuffer buf = new TokenBuffer(this);
+        JsonNode result;
+        try {
+            writeValue(buf, fromValue);
+            JsonParser jp = buf.asParser();
+            result = readTree(jp);
+            jp.close();
+        } catch (IOException e) { // should not occur, no real i/o...
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+        return (T) result;
+    } 
+    
+    /*
+    /**********************************************************
+    /* Extended Public API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to check whether mapper thinks
+     * it could serialize an instance of given Class.
+     * Check is done
+     * by checking whether a serializer can be found for the type.
+     *
+     * @return True if mapper can find a serializer for instances of
+     *  given class (potentially serializable), false otherwise (not
+     *  serializable)
+     */
+    public boolean canSerialize(Class<?> type) {
+        return _serializerProvider(getSerializationConfig()).hasSerializerFor(type);
+    }
+
+    /**
+     * Method that can be called to check whether mapper thinks
+     * it could deserialize an Object of given type.
+     * Check is done
+     * by checking whether a deserializer can be found for the type.
+     *
+     * @return True if mapper can find a serializer for instances of
+     *  given class (potentially serializable), false otherwise (not
+     *  serializable)
+     */
+    public boolean canDeserialize(JavaType type)
+    {
+        return createDeserializationContext(null,
+                getDeserializationConfig()).hasValueDeserializerFor(type);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended Public API, deserialization,
+    /* convenience methods
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(File src, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(File src, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(File src, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(URL src, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(URL src, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(URL src, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(String content, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(String content, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(content), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(String content, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(content), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(Reader src, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(Reader src, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(Reader src, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(InputStream src, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(InputStream src, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(InputStream src, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src, Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//      _setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType));
+    } 
+    
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src, int offset, int len, 
+                               Class<T> valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+     // !!! TODO
+//    	_setupClassLoaderForDeserialization(valueType);
+        return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueType));
+    } 
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(byte[] src, TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueTypeRef));
+    } 
+    
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> T readValue(byte[] src, int offset, int len,
+                           TypeReference valueTypeRef)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), _typeFactory.constructType(valueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src), valueType);
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src, int offset, int len,
+                           JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        return (T) _readMapAndClose(_jsonFactory.createParser(src, offset, len), valueType);
+    } 
+    
+    /*
+    /**********************************************************
+    /* Extended Public API: serialization
+    /* (mapping from Java types to JSON)
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, written to File provided.
+     */
+    public void writeValue(File resultFile, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(resultFile, JsonEncoding.UTF8), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using output stream provided (using encoding
+     * {@link JsonEncoding#UTF8}).
+     *<p>
+     * Note: method does not close the underlying stream explicitly
+     * here; however, {@link JsonFactory} this mapper uses may choose
+     * to close the stream depending on its settings (by default,
+     * it will try to close it when {@link JsonGenerator} we construct
+     * is closed).
+     */
+    public void writeValue(OutputStream out, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using Writer provided.
+     *<p>
+     * Note: method does not close the underlying stream explicitly
+     * here; however, {@link JsonFactory} this mapper uses may choose
+     * to close the stream depending on its settings (by default,
+     * it will try to close it when {@link JsonGenerator} we construct
+     * is closed).
+     */
+    public void writeValue(Writer w, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(w), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * a String. Functionally equivalent to calling
+     * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter}
+     * and constructing String, but more efficient.
+     *<p>
+     * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it.
+     */
+    public String writeValueAsString(Object value)
+        throws JsonProcessingException
+    {        
+        // alas, we have to pull the recycler directly here...
+        SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
+        try {
+            _configAndWriteValue(_jsonFactory.createGenerator(sw), value);
+        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+            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
+     * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream}
+     * and getting bytes, but more efficient.
+     * Encoding used will be UTF-8.
+     *<p>
+     * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it.
+     */
+    public byte[] writeValueAsBytes(Object value)
+        throws JsonProcessingException
+    {
+        ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler());
+        try {
+            _configAndWriteValue(_jsonFactory.createGenerator(bb, JsonEncoding.UTF8), value);
+        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+            throw e;
+        } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
+            throw JsonMappingException.fromUnexpectedIOE(e);
+        }
+        byte[] result = bb.toByteArray();
+        bb.release();
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Extended Public API: constructing ObjectWriters
+    /* for more advanced configuration
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for constructing {@link ObjectWriter}
+     * with default settings.
+     */
+    public ObjectWriter writer() {
+        return new ObjectWriter(this, getSerializationConfig());
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectWriter} with
+     * specified feature enabled (compared to settings that this
+     * mapper instance has).
+     */
+    public ObjectWriter writer(SerializationFeature feature) {
+        return new ObjectWriter(this, getSerializationConfig().with(feature));
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectWriter} with
+     * specified features enabled (compared to settings that this
+     * mapper instance has).
+     */
+    public ObjectWriter writer(SerializationFeature first,
+            SerializationFeature... other) {
+        return new ObjectWriter(this, getSerializationConfig().with(first, other));
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified {@link DateFormat}; or, if
+     * null passed, using timestamp (64-bit number.
+     */
+    public ObjectWriter writer(DateFormat df) {
+        return new ObjectWriter(this,
+                getSerializationConfig().with(df));
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified JSON View (filter).
+     */
+    public ObjectWriter writerWithView(Class<?> serializationView) {
+        return new ObjectWriter(this, getSerializationConfig().withView(serializationView));
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified root type, instead of actual
+     * runtime type of value. Type must be a super-type of runtime
+     * type.
+     */
+    public ObjectWriter writerWithType(Class<?> rootType) {
+        return new ObjectWriter(this, getSerializationConfig(),
+                // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+                ((rootType == null) ? null :_typeFactory.constructType(rootType)),
+                /*PrettyPrinter*/null);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified root type, instead of actual
+     * runtime type of value. Type must be a super-type of runtime type.
+     */
+    public ObjectWriter writerWithType(TypeReference<?> rootType) {
+        return new ObjectWriter(this, getSerializationConfig(),
+                // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+                ((rootType == null) ? null : _typeFactory.constructType(rootType)),
+                /*PrettyPrinter*/null);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified root type, instead of actual
+     * runtime type of value. Type must be a super-type of runtime type.
+     */
+    public ObjectWriter writerWithType(JavaType rootType) {
+        return new ObjectWriter(this, getSerializationConfig(), rootType, /*PrettyPrinter*/null);
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified pretty printer for indentation
+     * (or if null, no pretty printer)
+     */
+    public ObjectWriter writer(PrettyPrinter pp) {
+        if (pp == null) { // need to use a marker to indicate explicit disabling of pp
+            pp = ObjectWriter.NULL_PRETTY_PRINTER;
+        }
+        return new ObjectWriter(this, getSerializationConfig(), /*root type*/ null, pp);
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using the default pretty printer for indentation
+     */
+    public ObjectWriter writerWithDefaultPrettyPrinter() {
+        return new ObjectWriter(this, getSerializationConfig(),
+                /*root type*/ null, _defaultPrettyPrinter());
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * serialize objects using specified filter provider.
+     */
+    public ObjectWriter writer(FilterProvider filterProvider) {
+        return new ObjectWriter(this,
+                getSerializationConfig().withFilters(filterProvider));
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * pass specific schema object to {@link JsonGenerator} used for
+     * writing content.
+     * 
+     * @param schema Schema to pass to generator
+     */
+    public ObjectWriter writer(FormatSchema schema) {
+        _verifySchemaType(schema);
+        return new ObjectWriter(this, getSerializationConfig(), schema);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectWriter} that will
+     * use specified Base64 encoding variant for Base64-encoded binary data.
+     * 
+     * @since 2.1
+     */
+    public ObjectWriter writer(Base64Variant defaultBase64) {
+        return new ObjectWriter(this, getSerializationConfig().with(defaultBase64));
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended Public API: constructing ObjectReaders
+    /* for more advanced configuration
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for constructing {@link ObjectReader} with
+     * default settings. Note that the resulting instance is NOT usable as is,
+     * without defining expected value type.
+     */
+    public ObjectReader reader() {
+        return new ObjectReader(this, getDeserializationConfig())
+            .with(_injectableValues);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} with
+     * specified feature enabled (compared to settings that this
+     * mapper instance has).
+     * Note that the resulting instance is NOT usable as is,
+     * without defining expected value type.
+     */
+    public ObjectReader reader(DeserializationFeature feature) {
+        return new ObjectReader(this, getDeserializationConfig().with(feature));
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} with
+     * specified features enabled (compared to settings that this
+     * mapper instance has).
+     * Note that the resulting instance is NOT usable as is,
+     * without defining expected value type.
+     */
+    public ObjectReader reader(DeserializationFeature first,
+            DeserializationFeature... other) {
+        return new ObjectReader(this, getDeserializationConfig().with(first, other));
+    }
+    
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * update given Object (usually Bean, but can be a Collection or Map
+     * as well, but NOT an array) with JSON data. Deserialization occurs
+     * normally except that the root-level value in JSON is not used for
+     * instantiating a new object; instead give updateable object is used
+     * as root.
+     * Runtime type of value object is used for locating deserializer,
+     * unless overridden by other factory methods of {@link ObjectReader}
+     */
+    public ObjectReader readerForUpdating(Object valueToUpdate)
+    {
+        JavaType t = _typeFactory.constructType(valueToUpdate.getClass());
+        return new ObjectReader(this, getDeserializationConfig(), t, valueToUpdate,
+                null, _injectableValues);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * read or update instances of specified type
+     */
+    public ObjectReader reader(JavaType type)
+    {
+        return new ObjectReader(this, getDeserializationConfig(), type, null,
+                null, _injectableValues);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * read or update instances of specified type
+     */
+    public ObjectReader reader(Class<?> type)
+    {
+        return reader(_typeFactory.constructType(type));
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * read or update instances of specified type
+     */
+    public ObjectReader reader(TypeReference<?> type)
+    {
+        return reader(_typeFactory.constructType(type));
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * use specified {@link JsonNodeFactory} for constructing JSON trees.
+     */
+    public ObjectReader reader(JsonNodeFactory f)
+    {
+        return new ObjectReader(this, getDeserializationConfig()).with(f);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * pass specific schema object to {@link JsonParser} used for
+     * reading content.
+     * 
+     * @param schema Schema to pass to parser
+     */
+    public ObjectReader reader(FormatSchema schema) {
+        _verifySchemaType(schema);
+        return new ObjectReader(this, getDeserializationConfig(), null, null,
+                schema, _injectableValues);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * use specified injectable values.
+     * 
+     * @param injectableValues Injectable values to use
+     */
+    public ObjectReader reader(InjectableValues injectableValues) {
+        return new ObjectReader(this, getDeserializationConfig(), null, null,
+                null, injectableValues);
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * deserialize objects using specified JSON View (filter).
+     */
+    public ObjectReader readerWithView(Class<?> view) {
+        return new ObjectReader(this, getDeserializationConfig().withView(view));
+    }
+
+    /**
+     * Factory method for constructing {@link ObjectReader} that will
+     * use specified Base64 encoding variant for Base64-encoded binary data.
+     * 
+     * @since 2.1
+     */
+    public ObjectReader reader(Base64Variant defaultBase64) {
+        return new ObjectReader(this, getDeserializationConfig().with(defaultBase64));
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended Public API: convenience type conversion
+    /**********************************************************
+     */
+   
+    /**
+     * Convenience method for doing two-step conversion from given value, into
+     * instance of given value type. This is functionality equivalent to first
+     * serializing given value into JSON, then binding JSON data into value
+     * of given type, but may be executed without fully serializing into
+     * JSON. Same converters (serializers, deserializers) will be used as for
+     * data binding, meaning same object mapper configuration works.
+     *      
+     * @throws IllegalArgumentException If conversion fails due to incompatible type;
+     *    if so, root cause will contain underlying checked exception data binding
+     *    functionality threw
+     */
+    @SuppressWarnings("unchecked")
+    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));
+    } 
+
+    @SuppressWarnings("unchecked")
+    public <T> T convertValue(Object fromValue, TypeReference<?> toValueTypeRef)
+        throws IllegalArgumentException
+    {
+        return (T) convertValue(fromValue, _typeFactory.constructType(toValueTypeRef));
+    } 
+
+    @SuppressWarnings("unchecked")
+    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);
+    } 
+
+    /**
+     * Actual conversion implementation: instead of using existing read
+     * and write methods, much of code is inlined. Reason for this is
+     * that we must avoid wrapping/unwrapping both for efficiency and
+     * for correctness. If wrapping/unwrapping is actually desired,
+     * caller must use explicit <code>writeValue</code> and
+     * <code>readValue</code> methods.
+     */
+    protected Object _convert(Object fromValue, JavaType toValueType)
+        throws IllegalArgumentException
+    {        
+        // also, as per [Issue-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:
+         * (see [JACKSON-175])
+         */
+        TokenBuffer buf = new TokenBuffer(this);
+        try {
+            // inlined 'writeValue' with minor changes:
+            // first: disable wrapping when writing
+            SerializationConfig config = getSerializationConfig().without(SerializationFeature.WRAP_ROOT_VALUE);
+            // no need to check for closing of TokenBuffer
+            _serializerProvider(config).serializeValue(buf, fromValue);
+
+            // then matching read, inlined 'readValue' with minor mods:
+            final JsonParser jp = buf.asParser();
+            Object result;
+            // ok to pass in existing feature flags; unwrapping handled by mapper
+            final DeserializationConfig deserConfig = getDeserializationConfig();
+            JsonToken t = _initForReading(jp);
+            if (t == JsonToken.VALUE_NULL) {
+                DeserializationContext ctxt = createDeserializationContext(jp, deserConfig);
+                result = _findRootDeserializer(ctxt, toValueType).getNullValue();
+            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+                result = null;
+            } else { // pointing to event other than null
+                DeserializationContext ctxt = createDeserializationContext(jp, deserConfig);
+                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, toValueType);
+                // note: no handling of unwarpping
+                result = deser.deserialize(jp, ctxt);
+            }
+            jp.close();
+            return result;
+        } catch (IOException e) { // should not occur, no real i/o...
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Extended Public API: JSON Schema generation
+    /**********************************************************
+     */
+
+    /**
+     * Generate <a href="http://json-schema.org/">Json-schema</a>
+     * instance for specified class.
+     *
+     * @param t The class to generate schema for
+     * @return Constructed JSON schema.
+     */
+    @SuppressWarnings("deprecation")
+    public com.fasterxml.jackson.databind.jsonschema.JsonSchema generateJsonSchema(Class<?> t)
+            throws JsonMappingException {
+        return _serializerProvider(getSerializationConfig()).generateJsonSchema(t);
+    }
+
+    /**
+     * Method for visiting type hierarchy for given type, using specified visitor.
+     *<p>
+     * This method can be used for things like
+     * generating <a href="http://json-schema.org/">Json Schema</a>
+     * instance for specified type.
+     *
+     * @param type Type to generate schema for (possibly with generic signature)
+     * 
+     * @since 2.1
+     */
+    public void acceptJsonFormatVisitor(Class<?> type, JsonFormatVisitorWrapper visitor)
+        throws JsonMappingException
+    {
+        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
+     *<p>
+     * This method can be used for things like
+     * generating <a href="http://json-schema.org/">Json Schema</a>
+     * instance for specified type.
+     *
+     * @param type Type to generate schema for (possibly with generic signature)
+     * 
+     * @since 2.1
+     */
+    public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor)
+        throws JsonMappingException
+    {
+        if (type == null) {
+            throw new IllegalArgumentException("type must be provided");
+        }
+        _serializerProvider(getSerializationConfig()).acceptJsonFormatVisitor(type, visitor);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods for serialization, overridable
+    /**********************************************************
+     */
+
+    /**
+     * Overridable helper method used for constructing
+     * {@link SerializerProvider} to use for serialization.
+     */
+    protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {
+        return _serializerProvider.createInstance(config, _serializerFactory);
+    }
+    
+    /**
+     * Helper method that should return default pretty-printer to
+     * use for generators constructed by this mapper, when instructed
+     * to use default pretty printer.
+     */
+    protected PrettyPrinter _defaultPrettyPrinter() {
+        return _defaultPrettyPrinter;
+    }
+    
+    /**
+     * Method called to configure the generator as necessary and then
+     * call write functionality
+     */
+    protected final void _configAndWriteValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        SerializationConfig cfg = getSerializationConfig();
+        // [JACKSON-96]: allow enabling pretty printing for ObjectMapper directly
+        if (cfg.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
+            jgen.useDefaultPrettyPrinter();
+        }
+        // [JACKSON-282]: consider Closeable
+        if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
+            _configAndWriteCloseable(jgen, value, cfg);
+            return;
+        }
+        boolean closed = false;
+        try {
+            _serializerProvider(cfg).serializeValue(jgen, value);
+            closed = true;
+            jgen.close();
+        } finally {
+            /* won't try to close twice; also, must catch exception (so it 
+             * will not mask exception that is pending)
+             */
+            if (!closed) {
+                try {
+                    jgen.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+
+    protected final void _configAndWriteValue(JsonGenerator jgen, Object value, Class<?> viewClass)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        SerializationConfig cfg = getSerializationConfig().withView(viewClass);
+        if (cfg.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
+            jgen.useDefaultPrettyPrinter();
+        }
+        // [JACKSON-282]: consider Closeable
+        if (cfg.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
+            _configAndWriteCloseable(jgen, value, cfg);
+            return;
+        }
+        boolean closed = false;
+        try {
+            _serializerProvider(cfg).serializeValue(jgen, value);
+            closed = true;
+            jgen.close();
+        } finally {
+            if (!closed) {
+                try {
+                    jgen.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+
+    /**
+     * Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
+     * method is to be called right after serialization has been called
+     */
+    private final void _configAndWriteCloseable(JsonGenerator jgen, Object value, SerializationConfig cfg)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        Closeable toClose = (Closeable) value;
+        try {
+            _serializerProvider(cfg).serializeValue(jgen, value);
+            JsonGenerator tmpJgen = jgen;
+            jgen = null;
+            tmpJgen.close();
+            Closeable tmpToClose = toClose;
+            toClose = null;
+            tmpToClose.close();
+        } finally {
+            /* Need to close both generator and value, as long as they haven't yet
+             * been closed
+             */
+            if (jgen != null) {
+                try {
+                    jgen.close();
+                } catch (IOException ioe) { }
+            }
+            if (toClose != null) {
+                try {
+                    toClose.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+    
+    /**
+     * Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
+     * method is to be called right after serialization has been called
+     */
+    private final void _writeCloseableValue(JsonGenerator jgen, Object value, SerializationConfig cfg)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        Closeable toClose = (Closeable) value;
+        try {
+            _serializerProvider(cfg).serializeValue(jgen, value);
+            if (cfg.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
+                jgen.flush();
+            }
+            Closeable tmpToClose = toClose;
+            toClose = null;
+            tmpToClose.close();
+        } finally {
+            if (toClose != null) {
+                try {
+                    toClose.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods for deserialization, overridable
+    /**********************************************************
+     */
+
+    /**
+     * 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 jp,
+            DeserializationConfig cfg)
+    {
+        return _deserializationContext.createInstance(cfg,
+                jp, _injectableValues);
+    }
+    
+    /**
+     * Actual implementation of value reading+binding operation.
+     */
+    protected Object _readValue(DeserializationConfig cfg, JsonParser jp, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        /* First: may need to read the next token, to initialize
+         * state (either before first read from parser, or after
+         * previous token has been cleared)
+         */
+        Object result;
+        JsonToken t = _initForReading(jp);
+        if (t == JsonToken.VALUE_NULL) {
+            // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use:
+            DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+            result = _findRootDeserializer(ctxt, valueType).getNullValue();
+        } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+            result = null;
+        } else { // pointing to event other than null
+            DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+            JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
+            // ok, let's get the value
+            if (cfg.useRootWrapping()) {
+                result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
+            } else {
+                result = deser.deserialize(jp, ctxt);
+            }
+        }
+        // Need to consume the token too
+        jp.clearCurrentToken();
+        return result;
+    }
+    
+    protected Object _readMapAndClose(JsonParser jp, JavaType valueType)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        try {
+            Object result;
+            JsonToken t = _initForReading(jp);
+            if (t == JsonToken.VALUE_NULL) {
+                // [JACKSON-643]: Ask JsonDeserializer what 'null value' to use:
+                DeserializationContext ctxt = createDeserializationContext(jp,
+                        getDeserializationConfig());
+                result = _findRootDeserializer(ctxt, valueType).getNullValue();
+            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+                result = null;
+            } else {
+                DeserializationConfig cfg = getDeserializationConfig();
+                DeserializationContext ctxt = createDeserializationContext(jp, cfg);
+                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, valueType);
+                if (cfg.useRootWrapping()) {
+                    result = _unwrapAndDeserialize(jp, ctxt, cfg, valueType, deser);
+                } else {
+                    result = deser.deserialize(jp, ctxt);
+                }
+            }
+            // Need to consume the token too
+            jp.clearCurrentToken();
+            return result;
+        } finally {
+            try {
+                jp.close();
+            } catch (IOException ioe) { }
+        }
+    }
+    
+    /**
+     * 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 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)
+     */
+    protected JsonToken _initForReading(JsonParser jp)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        /* 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 = jp.getCurrentToken();
+        if (t == null) {
+            // and then we must get something...
+            t = jp.nextToken();
+            if (t == null) {
+                /* [JACKSON-546] Throw mapping exception, since it's failure to map,
+                 *   not an actual parsing problem
+                 */
+                throw JsonMappingException.from(jp, "No content to map due to end-of-input");
+            }
+        }
+        return t;
+    }
+
+    protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt, 
+            DeserializationConfig config,
+            JavaType rootType, JsonDeserializer<Object> deser)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        String expName = config.getRootName();
+        if (expName == null) {
+            SerializedString sstr = _rootNames.findRootName(rootType, config);
+            expName = sstr.getValue();
+        }
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        if (jp.nextToken() != JsonToken.FIELD_NAME) {
+            throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        String actualName = jp.getCurrentName();
+        if (!expName.equals(actualName)) {
+            throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+                    +expName+"') for type "+rootType);
+        }
+        // ok, then move to value itself....
+        jp.nextToken();
+        Object result = deser.deserialize(jp, ctxt);
+        // and last, verify that we now get matching END_OBJECT
+        if (jp.nextToken() != JsonToken.END_OBJECT) {
+            throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, other
+    /**********************************************************
+     */
+
+    /**
+     * Method called to locate deserializer for the passed root-level value.
+     */
+    protected JsonDeserializer<Object> _findRootDeserializer(DeserializationContext ctxt,
+            JavaType valueType)
+        throws JsonMappingException
+    {
+        // First: have we already seen it?
+        JsonDeserializer<Object> deser = _rootDeserializers.get(valueType);
+        if (deser != null) {
+            return deser;
+        }
+        // Nope: need to ask provider to resolve it
+        deser = ctxt.findRootValueDeserializer(valueType);
+        if (deser == null) { // can this happen?
+            throw new JsonMappingException("Can not find a deserializer for type "+valueType);
+        }
+        _rootDeserializers.put(valueType, deser);
+        return deser;
+    }
+
+    /**
+     * @since 2.2
+     */
+    protected void _verifySchemaType(FormatSchema schema)
+    {
+        if (schema != null) {
+            if (!_jsonFactory.canUseSchema(schema)) {
+                    throw new IllegalArgumentException("Can not 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
new file mode 100644
index 0000000..757b973
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectReader.java
@@ -0,0 +1,1528 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.core.type.ResolvedType;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+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.RootNameLookup;
+
+/**
+ * Builder object that can be used for per-serialization configuration of
+ * deserialization parameters, such as root type to use or object
+ * to update (instead of constructing new instance).
+ *<p>
+ * Uses "fluent" (or, kind of, builder) pattern so that instances are immutable
+ * (and thus fully thread-safe with no external synchronization);
+ * new instances are constructed for different configurations.
+ * 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.
+ */
+public class ObjectReader
+    extends ObjectCodec
+    implements Versioned, java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = -4251443320039569153L;
+
+    private final static JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);
+
+    /*
+    /**********************************************************
+    /* Immutable configuration from ObjectMapper
+    /**********************************************************
+     */
+
+    /**
+     * General serialization configuration settings; while immutable,
+     * can use copy-constructor to create modified instances as necessary.
+     */
+    protected final DeserializationConfig _config;
+
+    /**
+     * Blueprint instance of deserialization context; used for creating
+     * actual instance when needed.
+     */
+    protected final DefaultDeserializationContext _context;
+
+    /**
+     * Factory used for constructing {@link JsonGenerator}s
+     */
+    protected final JsonFactory _jsonFactory;
+    
+    /**
+     * Flag that indicates whether root values are expected to be unwrapped or not
+     */
+    protected final boolean _unwrapRoot;
+    
+    /*
+    /**********************************************************
+    /* Configuration that can be changed during building
+    /**********************************************************
+     */   
+
+    /**
+     * Declared type of value to instantiate during deserialization.
+     * Defines which deserializer to use; as well as base type of instance
+     * to construct if an updatable value is not configured to be used
+     * (subject to changes by embedded type information, for polymorphic
+     * types). If {@link #_valueToUpdate} is non-null, only used for
+     * locating deserializer.
+     */
+    protected final JavaType _valueType;
+
+    /**
+     * We may pre-fetch deserializer as soon as {@link #_valueType}
+     * is known, and if so, reuse it afterwards.
+     * This allows avoiding further deserializer lookups and increases
+     * performance a bit on cases where readers are reused.
+     * 
+     * @since 2.1
+     */
+    protected final JsonDeserializer<Object> _rootDeserializer;
+    
+    /**
+     * Instance to update with data binding; if any. If null,
+     * a new instance is created, if non-null, properties of
+     * 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.
+     */
+    protected final Object _valueToUpdate;
+
+    /**
+     * When using data format that uses a schema, schema is passed
+     * to parser.
+     */
+    protected final FormatSchema _schema;
+
+    /**
+     * Values that can be injected during deserialization, if any.
+     */
+    protected final InjectableValues _injectableValues;
+
+    /**
+     * Optional detector used for auto-detecting data format that byte-based
+     * input uses.
+     *<p>
+     * 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),
+     * a {@link JsonParseException} will be thrown).
+     * 
+     * @since 2.1
+     */
+    protected final DataFormatReaders _dataFormatReaders;
+    
+    /*
+    /**********************************************************
+    /* Caching
+    /**********************************************************
+     */
+    
+    /**
+     * Root-level cached deserializers
+     */
+    final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _rootDeserializers;
+
+    /**
+     * Cache for root names used when root-wrapping is enabled.
+     */
+    protected final RootNameLookup _rootNames;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, construction
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by {@link ObjectMapper} for initial instantiation
+     */
+    protected ObjectReader(ObjectMapper mapper, DeserializationConfig config)
+    {
+        this(mapper, config, null, null, null, null);
+    }
+
+    /**
+     * Constructor called when a root deserializer should be fetched based
+     * on other configuration.
+     */
+    protected ObjectReader(ObjectMapper mapper, DeserializationConfig config,
+            JavaType valueType, Object valueToUpdate,
+            FormatSchema schema, InjectableValues injectableValues)
+    {
+        _config = config;
+        _context = mapper._deserializationContext;
+        _rootDeserializers = mapper._rootDeserializers;
+        _jsonFactory = mapper._jsonFactory;
+        _rootNames = mapper._rootNames;
+        _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();
+
+        _rootDeserializer = _prefetchRootDeserializer(config, valueType);
+        _dataFormatReaders = null;        
+    }
+    
+    /**
+     * Copy constructor used for building variations.
+     */
+    protected ObjectReader(ObjectReader base, DeserializationConfig config,
+            JavaType valueType, JsonDeserializer<Object> rootDeser, Object valueToUpdate,
+            FormatSchema schema, InjectableValues injectableValues,
+            DataFormatReaders dataFormatReaders)
+    {
+        _config = config;
+        _context = base._context;
+
+        _rootDeserializers = base._rootDeserializers;
+        _jsonFactory = base._jsonFactory;
+        _rootNames = base._rootNames;
+
+        _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();
+        _dataFormatReaders = dataFormatReaders;
+    }
+
+    /**
+     * Copy constructor used when modifying simple feature flags
+     */
+    protected ObjectReader(ObjectReader base, DeserializationConfig config)
+    {
+        _config = config;
+        _context = base._context;
+
+        _rootDeserializers = base._rootDeserializers;
+        _jsonFactory = base._jsonFactory;
+        _rootNames = base._rootNames;
+
+        _valueType = base._valueType;
+        _rootDeserializer = base._rootDeserializer;
+        _valueToUpdate = base._valueToUpdate;
+        _schema = base._schema;
+        _injectableValues = base._injectableValues;
+        _unwrapRoot = config.useRootWrapping();
+        _dataFormatReaders = base._dataFormatReaders;
+    }
+
+    protected ObjectReader(ObjectReader base, JsonFactory f)
+    {
+        _config = base._config;
+        _context = base._context;
+
+        _rootDeserializers = base._rootDeserializers;
+        _jsonFactory = f;
+        _rootNames = base._rootNames;
+
+        _valueType = base._valueType;
+        _rootDeserializer = base._rootDeserializer;
+        _valueToUpdate = base._valueToUpdate;
+        _schema = base._schema;
+        _injectableValues = base._injectableValues;
+        _unwrapRoot = base._unwrapRoot;
+        _dataFormatReaders = base._dataFormatReaders;
+    }
+    
+    /**
+     * Method that will return version information stored in and read from jar
+     * that contains this class.
+     */
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+
+    /*
+    /**********************************************************
+    /* Life-cycle, fluent factory methods
+    /**********************************************************
+     */
+
+    public ObjectReader with(DeserializationConfig config) {
+        return _with(config);
+    }    
+    
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified feature enabled.
+     */
+    public ObjectReader with(DeserializationFeature feature) {
+        return _with(_config.with(feature));
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectReader with(DeserializationFeature first,
+            DeserializationFeature... other)
+    {
+        return _with(_config.with(first, other));
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectReader withFeatures(DeserializationFeature... features) {
+        return _with(_config.withFeatures(features));
+    }    
+    
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified feature disabled.
+     */
+    public ObjectReader without(DeserializationFeature feature) {
+        return _with(_config.without(feature)); 
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified features disabled.
+     */
+    public ObjectReader without(DeserializationFeature first,
+            DeserializationFeature... other)
+    {
+        return _with(_config.without(first, other));
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * with specified features disabled.
+     */
+    public ObjectReader withoutFeatures(DeserializationFeature... features) {
+        return _with(_config.withoutFeatures(features));
+    }    
+    
+    /**
+     * Method for constructing a new instance with configuration that uses
+     * passed {@link InjectableValues} to provide injectable values.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader with(InjectableValues injectableValues)
+    {
+        if (_injectableValues == injectableValues) {
+            return this;
+        }
+        return new ObjectReader(this, _config,
+                _valueType, _rootDeserializer, _valueToUpdate,
+                _schema, injectableValues, _dataFormatReaders);
+    }
+
+    /**
+     * Method for constructing a new reader instance with configuration that uses
+     * passed {@link JsonNodeFactory} for constructing {@link JsonNode}
+     * instances.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader with(JsonNodeFactory f) {
+        return _with(_config.with(f));
+    }
+
+    /**
+     * Method for constructing a new reader instance with configuration that uses
+     * passed {@link JsonFactory} for constructing underlying Readers.
+     *<p>
+     * NOTE: only factories that <b>DO NOT REQUIRE SPECIAL MAPPERS</b>
+     * (that is, ones that return <code>false</code> for
+     * {@link JsonFactory#requiresCustomCodec()}) can be used: trying
+     * to use one that requires custom codec will throw exception
+     * 
+     * @since 2.1
+     */
+    public ObjectReader with(JsonFactory f) {
+        if (f == _jsonFactory) {
+            return this;
+        }
+        ObjectReader r = new ObjectReader(this, f);
+        // Also, try re-linking, if possible...
+        if (f.getCodec() == null) {
+            f.setCodec(r);
+        }
+        return r;
+    }
+    
+    /**
+     * Method for constructing a new instance with configuration that
+     * specifies what root name to expect for "root name unwrapping".
+     * See {@link DeserializationConfig#withRootName(String)} for
+     * details.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withRootName(String rootName) {
+        return _with(_config.withRootName(rootName));
+    }
+
+    /**
+     * Method for constructing a new instance with configuration that
+     * passes specified {@link FormatSchema} to {@link JsonParser} that
+     * is constructed for parsing content.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader with(FormatSchema schema)
+    {
+        if (_schema == schema) {
+            return this;
+        }
+        _verifySchemaType(schema);
+        return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
+                schema, _injectableValues, _dataFormatReaders);
+    }
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * to data bind into specified type.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withType(JavaType valueType)
+    {
+        if (valueType != null && valueType.equals(_valueType)) {
+            return this;
+        }
+        JsonDeserializer<Object> rootDeser = _prefetchRootDeserializer(_config, valueType);
+        // type is stored here, no need to make a copy of config
+        DataFormatReaders det = _dataFormatReaders;
+        if (det != null) {
+            det = det.withType(valueType);
+        }
+        return new ObjectReader(this, _config, valueType, rootDeser,
+                _valueToUpdate, _schema, _injectableValues, det);
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * to data bind into specified type.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withType(Class<?> valueType) {
+        return withType(_config.constructType(valueType));
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * to data bind into specified type.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withType(java.lang.reflect.Type valueType) {
+        return withType(_config.getTypeFactory().constructType(valueType));
+    }    
+
+    /**
+     * Method for constructing a new reader instance that is configured
+     * to data bind into specified type.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withType(TypeReference<?> valueTypeRef) {
+        return withType(_config.getTypeFactory().constructType(valueTypeRef.getType()));
+    }    
+
+    /**
+     * Method for constructing a new instance with configuration that
+     * updates passed Object (as root value), instead of constructing 
+     * a new value.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withValueToUpdate(Object value)
+    {
+        if (value == _valueToUpdate) return this;
+        if (value == null) {
+            throw new IllegalArgumentException("cat not update null value");
+        }
+        JavaType t;
+        
+        /* no real benefit from pre-fetching, as updating readers are much
+         * less likely to be reused, and value type may also be forced
+         * with a later chained call...
+         */
+        if (_valueType == null) {
+            t = _config.constructType(value.getClass());
+        } else {
+            t = _valueType;
+        }
+        return new ObjectReader(this, _config, t, _rootDeserializer, value,
+                _schema, _injectableValues, _dataFormatReaders);
+    }
+
+    /**
+     * Method for constructing a new instance with configuration that
+     * uses specified View for filtering.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectReader withView(Class<?> activeView) {
+        return _with(_config.withView(activeView));
+    }
+
+    public ObjectReader with(Locale l) {
+        return _with(_config.with(l));
+    }
+
+    public ObjectReader with(TimeZone tz) {
+        return _with(_config.with(tz));
+    }
+
+    public ObjectReader withHandler(DeserializationProblemHandler h) {
+        return _with(_config.withHandler(h));
+    }
+
+    public ObjectReader with(Base64Variant defaultBase64) {
+        return _with(_config.with(defaultBase64));
+    }
+
+    /**
+     * Fluent factory method for constructing a reader that will try to
+     * auto-detect underlying data format, using specified list of
+     * {@link JsonFactory} instances, and default {@link DataFormatReaders} settings
+     * (for customized {@link DataFormatReaders}, you can construct instance yourself).
+     * 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
+     * (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
+     * require "custom codec" (like XML factory) will not work. Instead, use
+     * method that takes {@link ObjectReader} instances instead of factories.
+     * 
+     * @param readers Data formats accepted, in decreasing order of priority (that is,
+     *   matches checked in listed order, first match wins)
+     * 
+     * @return Newly configured writer instance
+     * 
+     * @since 2.1
+     */
+    public ObjectReader withFormatDetection(ObjectReader... readers)
+    {
+        return withFormatDetection(new DataFormatReaders(readers));
+    }
+
+    /**
+     * Fluent factory method for constructing a reader that will try to
+     * auto-detect underlying data format, using specified
+     * {@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
+     * (enough) detected as one of specified types, an exception will be thrown.
+     * 
+     * @param readers DataFormatReaders to use for detecting underlying format.
+     * 
+     * @return Newly configured writer instance
+     * 
+     * @since 2.1
+     */
+    public ObjectReader withFormatDetection(DataFormatReaders readers)
+    {
+        return new ObjectReader(this, _config, _valueType, _rootDeserializer, _valueToUpdate,
+                _schema, _injectableValues, readers);
+    }
+    
+    /*
+    /**********************************************************
+    /* Simple accessors
+    /**********************************************************
+     */
+    
+    public boolean isEnabled(DeserializationFeature f) {
+        return _config.isEnabled(f);
+    }
+
+    public boolean isEnabled(MapperFeature f) {
+        return _config.isEnabled(f);
+    }
+
+    public boolean isEnabled(JsonParser.Feature f) {
+        return _jsonFactory.isEnabled(f);
+    }
+
+    /**
+     * @since 2.2
+     */
+    public DeserializationConfig getConfig() {
+        return _config;
+    }
+    
+    /**
+     * @since 2.1
+     */
+    @Override
+    public JsonFactory getFactory() {
+        return _jsonFactory;
+    }
+    
+    /**
+     * @deprecated Since 2.1: Use {@link #getFactory} instead
+     */
+    @Deprecated
+    @Override
+    public JsonFactory getJsonFactory() {
+        return _jsonFactory;
+    }
+
+    public TypeFactory getTypeFactory() {
+        return _config.getTypeFactory();
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization methods; basic ones to support ObjectCodec first
+    /* (ones that take JsonParser)
+    /**********************************************************
+     */
+
+    /**
+     * Method that binds content read using given parser, using
+     * configuration of this reader, including expected result type.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        return (T) _bind(jp, _valueToUpdate);
+    }
+
+    /**
+     * Convenience method that binds content read using given parser, using
+     * configuration of this reader, except that expected value type
+     * is specified with the call (instead of currently configured root type).
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T readValue(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonProcessingException
+    {
+        return (T) withType(valueType).readValue(jp);
+    }
+
+    /**
+     * Convenience method that binds content read using given parser, using
+     * configuration of this reader, except that expected value type
+     * is specified with the call (instead of currently configured root type).
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException
+    {
+        return (T) withType(valueTypeRef).readValue(jp);
+    }
+
+    /**
+     * Convenience method that binds content read using given parser, using
+     * configuration of this reader, except that expected value type
+     * is specified with the call (instead of currently configured root type).
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp, ResolvedType valueType) throws IOException, JsonProcessingException {
+        return (T) withType((JavaType)valueType).readValue(jp);
+    }
+
+    /**
+     * Type-safe overloaded method, basically alias for {@link #readValue(JsonParser, ResolvedType)}.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonParser jp, JavaType valueType) throws IOException, JsonProcessingException {
+        return (T) withType(valueType).readValue(jp);
+    }
+    
+    /**
+     * Convenience method that binds content read using given parser, using
+     * configuration of this reader, except that content is bound as
+     * JSON tree instead of configured root value type.
+     *<p>
+     * Note: if an object was specified with {@link #withValueToUpdate}, it
+     * will be ignored.
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends TreeNode> T readTree(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        return (T) _bindAsTree(jp);
+    }
+
+    /**
+     * Convenience method that is equivalent to:
+     *<pre>
+     *   withType(valueType).readValues(jp);
+     *</pre>
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @Override
+    public <T> Iterator<T> readValues(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonProcessingException {
+        return withType(valueType).readValues(jp);
+    }
+
+    /**
+     * Convenience method that is equivalent to:
+     *<pre>
+     *   withType(valueTypeRef).readValues(jp);
+     *</pre>
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @Override
+    public <T> Iterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException {
+        return withType(valueTypeRef).readValues(jp);
+    }
+    
+    /**
+     * Convenience method that is equivalent to:
+     *<pre>
+     *   withType(valueType).readValues(jp);
+     *</pre>
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    @Override
+    public <T> Iterator<T> readValues(JsonParser jp, ResolvedType valueType)
+        throws IOException, JsonProcessingException {
+        return readValues(jp, (JavaType) valueType);
+    }
+
+    /**
+     * Convenience method that is equivalent to:
+     *<pre>
+     *   withType(valueType).readValues(jp);
+     *</pre>
+     *<p>
+     * NOTE: this method never tries to auto-detect format, since actual
+     * (data-format specific) parser is given.
+     */
+    public <T> Iterator<T> readValues(JsonParser jp, JavaType valueType)
+        throws IOException, JsonProcessingException {
+        return withType(valueType).readValues(jp);
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization methods; others similar to what ObjectMapper has
+    /**********************************************************
+     */
+    
+    /**
+     * Method that binds content read from given input source,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(InputStream src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return (T) _detectBindAndClose(_dataFormatReaders.findFormat(src), false);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Method that binds content read from given input source,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(Reader src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(src);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+    
+    /**
+     * Method that binds content read from given JSON string,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(String src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(src);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Method that binds content read from given byte array,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return (T) _detectBindAndClose(src, 0, src.length);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Method that binds content read from given byte array,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(byte[] src, int offset, int length)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return (T) _detectBindAndClose(src, offset, length);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src, offset, length), _valueToUpdate);
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(File src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Method that binds content read from given input source,
+     * using configuration of this reader.
+     * Value return is either newly constructed, or root value that
+     * was specified with {@link #withValueToUpdate(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(URL src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return (T) _detectBindAndClose(_dataFormatReaders.findFormat(_inputStream(src)), true);
+        }
+        return (T) _bindAndClose(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Convenience method for converting results from given JSON tree into given
+     * value type. Basically short-cut for:
+     *<pre>
+     *   objectReader.readValue(src.traverse())
+     *</pre>
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValue(JsonNode src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(src);
+        }
+        return (T) _bindAndClose(treeAsTokens(src), _valueToUpdate);
+    }
+    
+    /**
+     * Method that reads content from given input source,
+     * using configuration of this reader, and binds it as JSON Tree.
+     *<p>
+     * Note that if an object was specified with a call to
+     * {@link #withValueToUpdate(Object)}
+     * it will just be ignored; result is always a newly constructed
+     * {@link JsonNode} instance.
+     */
+    public JsonNode readTree(InputStream in)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return _detectBindAndCloseAsTree(in);
+        }
+        return _bindAndCloseAsTree(_jsonFactory.createParser(in));
+    }
+    
+    /**
+     * Method that reads content from given input source,
+     * using configuration of this reader, and binds it as JSON Tree.
+     *<p>
+     * Note that if an object was specified with a call to
+     * {@link #withValueToUpdate(Object)}
+     * it will just be ignored; result is always a newly constructed
+     * {@link JsonNode} instance.
+     */
+    public JsonNode readTree(Reader r)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(r);
+        }
+        return _bindAndCloseAsTree(_jsonFactory.createParser(r));
+    }
+
+    /**
+     * Method that reads content from given JSON input String,
+     * using configuration of this reader, and binds it as JSON Tree.
+     *<p>
+     * Note that if an object was specified with a call to
+     * {@link #withValueToUpdate(Object)}
+     * it will just be ignored; result is always a newly constructed
+     * {@link JsonNode} instance.
+     */
+    public JsonNode readTree(String json)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(json);
+        }
+        return _bindAndCloseAsTree(_jsonFactory.createParser(json));
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization methods; reading sequence of values
+    /**********************************************************
+     */
+    
+    /**
+     * Method for reading sequence of Objects from parser stream.
+     *<p>
+     * Sequence can be either root-level "unwrapped" sequence (without surrounding
+     * JSON array), or a sequence contained in a JSON Array.
+     * In either case {@link JsonParser} must point to the first token of
+     * the first element, OR not point to any token (in which case it is advanced
+     * to the next token). This means, specifically, that for wrapped sequences,
+     * parser MUST NOT point to the surrounding <code>START_ARRAY</code> but rather
+     * to the token following it.
+     */
+    public <T> MappingIterator<T> readValues(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        DeserializationContext ctxt = createDeserializationContext(jp, _config);
+        // false -> do not close as caller gave parser instance
+        return new MappingIterator<T>(_valueType, jp, ctxt,
+                _findRootDeserializer(ctxt, _valueType),
+                false, _valueToUpdate);
+    }
+    
+    /**
+     * Method for reading sequence of Objects from parser stream.
+     *<p>
+     * Sequence can be either wrapped or unwrapped root-level sequence:
+     * wrapped means that the elements are enclosed in JSON Array;
+     * and unwrapped that elements are directly accessed at main level.
+     * Assumption is that iff the first token of the document is
+     * <code>START_ARRAY</code>, we have a wrapped sequence; otherwise
+     * unwrapped. For wrapped sequences, leading <code>START_ARRAY</code>
+     * is skipped, so that for both cases, underlying {@link JsonParser}
+     * will point to what is expected to be the first token of the first
+     * element.
+     *<p>
+     * Note that the wrapped vs unwrapped logic means that it is NOT
+     * possible to use this method for reading an unwrapped sequence
+     * of elements written as JSON Arrays: to read such sequences, one
+     * has to use {@link #readValues(JsonParser)}, making sure parser
+     * points to the first token of the first element (i.e. the second
+     * <code>START_ARRAY</code> which is part of the first element).
+     */
+    public <T> MappingIterator<T> readValues(InputStream src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return _detectBindAndReadValues(_dataFormatReaders.findFormat(src), false);
+        }
+        return _bindAndReadValues(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+    
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     */
+    public <T> MappingIterator<T> readValues(Reader src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(src);
+        }
+        JsonParser jp = _jsonFactory.createParser(src);
+        if (_schema != null) {
+            jp.setSchema(_schema);
+        }
+        jp.nextToken();
+        DeserializationContext ctxt = createDeserializationContext(jp, _config);
+        return new MappingIterator<T>(_valueType, jp, ctxt,
+                _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate);
+    }
+    
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     * 
+     * @param json String that contains JSON content to parse
+     */
+    public <T> MappingIterator<T> readValues(String json)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            _reportUndetectableSource(json);
+        }
+        JsonParser jp = _jsonFactory.createParser(json);
+        if (_schema != null) {
+            jp.setSchema(_schema);
+        }
+        jp.nextToken();
+        DeserializationContext ctxt = createDeserializationContext(jp, _config);
+        return new MappingIterator<T>(_valueType, jp, ctxt,
+                _findRootDeserializer(ctxt, _valueType), true, _valueToUpdate);
+    }
+
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     */
+    public <T> MappingIterator<T> readValues(byte[] src, int offset, int length)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return _detectBindAndReadValues(_dataFormatReaders.findFormat(src, offset, length), false);
+        }
+        return _bindAndReadValues(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     */
+    public final <T> MappingIterator<T> readValues(byte[] src)
+            throws IOException, JsonProcessingException {
+        return readValues(src, 0, src.length);
+    }
+    
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     */
+    public <T> MappingIterator<T> readValues(File src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return _detectBindAndReadValues(
+                    _dataFormatReaders.findFormat(_inputStream(src)), false);
+        }
+        return _bindAndReadValues(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /**
+     * Overloaded version of {@link #readValue(InputStream)}.
+     * 
+     * @param src URL to read to access JSON content to parse.
+     */
+    public <T> MappingIterator<T> readValues(URL src)
+        throws IOException, JsonProcessingException
+    {
+        if (_dataFormatReaders != null) {
+            return _detectBindAndReadValues(
+                    _dataFormatReaders.findFormat(_inputStream(src)), true);
+        }
+        return _bindAndReadValues(_jsonFactory.createParser(src), _valueToUpdate);
+    }
+
+    /*
+    /**********************************************************
+    /* Implementation of rest of ObjectCodec methods
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonNode createArrayNode() {
+        return _config.getNodeFactory().arrayNode();
+    }
+
+    @Override
+    public JsonNode createObjectNode() {
+        return _config.getNodeFactory().objectNode();
+    }
+
+    @Override
+    public JsonParser treeAsTokens(TreeNode n) {
+        return new TreeTraversingParser((JsonNode) n, this);
+    }
+
+    @Override
+    public <T> T treeToValue(TreeNode n, Class<T> valueType)
+        throws JsonProcessingException
+    {
+        try {
+            return readValue(treeAsTokens(n), valueType);
+        } catch (JsonProcessingException e) {
+            throw e;
+        } catch (IOException e) { // should not occur, no real i/o...
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public void writeValue(JsonGenerator jgen, Object value) throws IOException, JsonProcessingException
+    {
+        throw new UnsupportedOperationException("Not implemented for ObjectReader");
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, data-binding
+    /**********************************************************
+     */
+    
+    /**
+     * Actual implementation of value reading+binding operation.
+     */
+    protected Object _bind(JsonParser jp, Object valueToUpdate)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        /* First: may need to read the next token, to initialize state (either
+         * before first read from parser, or after previous token has been cleared)
+         */
+        Object result;
+        JsonToken t = _initForReading(jp);
+        if (t == JsonToken.VALUE_NULL) {
+            if (valueToUpdate == null) {
+                DeserializationContext ctxt = createDeserializationContext(jp, _config);
+                result = _findRootDeserializer(ctxt, _valueType).getNullValue();
+            } else {
+                result = valueToUpdate;
+            }
+        } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+            result = valueToUpdate;
+        } else { // pointing to event other than null
+            DeserializationContext ctxt = createDeserializationContext(jp, _config);
+            JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, _valueType);
+            if (_unwrapRoot) {
+                result = _unwrapAndDeserialize(jp, ctxt, _valueType, deser);
+            } else {
+                if (valueToUpdate == null) {
+                    result = deser.deserialize(jp, ctxt);
+                } else {
+                    deser.deserialize(jp, ctxt, valueToUpdate);
+                    result = valueToUpdate;
+                }
+            }
+        }
+        // Need to consume the token too
+        jp.clearCurrentToken();
+        return result;
+    }
+    
+    protected Object _bindAndClose(JsonParser jp, Object valueToUpdate)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        if (_schema != null) {
+            jp.setSchema(_schema);
+        }
+        try {
+            Object result;
+            JsonToken t = _initForReading(jp);
+            if (t == JsonToken.VALUE_NULL) {
+                if (valueToUpdate == null) {
+                    DeserializationContext ctxt = createDeserializationContext(jp, _config);
+                    result = _findRootDeserializer(ctxt, _valueType).getNullValue();
+                } else {
+                    result = valueToUpdate;
+                }
+            } else if (t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+                result = valueToUpdate;
+            } else {
+                DeserializationContext ctxt = createDeserializationContext(jp, _config);
+                JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, _valueType);
+                if (_unwrapRoot) {
+                    result = _unwrapAndDeserialize(jp, ctxt, _valueType, deser);
+                } else {
+                    if (valueToUpdate == null) {
+                        result = deser.deserialize(jp, ctxt);
+                    } else {
+                        deser.deserialize(jp, ctxt, valueToUpdate);
+                        result = valueToUpdate;                    
+                    }
+                }
+            }
+            return result;
+        } finally {
+            try {
+                jp.close();
+            } catch (IOException ioe) { }
+        }
+    }
+
+    protected JsonNode _bindAsTree(JsonParser jp)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        JsonNode result;
+        JsonToken t = _initForReading(jp);
+        if (t == JsonToken.VALUE_NULL || t == JsonToken.END_ARRAY || t == JsonToken.END_OBJECT) {
+            result = NullNode.instance;
+        } else {
+            DeserializationContext ctxt = createDeserializationContext(jp, _config);
+            JsonDeserializer<Object> deser = _findRootDeserializer(ctxt, JSON_NODE_TYPE);
+            if (_unwrapRoot) {
+                result = (JsonNode) _unwrapAndDeserialize(jp, ctxt, JSON_NODE_TYPE, deser);
+            } else {
+                result = (JsonNode) deser.deserialize(jp, ctxt);
+            }
+        }
+        // Need to consume the token too
+        jp.clearCurrentToken();
+        return result;
+    }
+    
+    protected JsonNode _bindAndCloseAsTree(JsonParser jp)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        if (_schema != null) {
+            jp.setSchema(_schema);
+        }
+        try {
+            return _bindAsTree(jp);
+        } finally {
+            try {
+                jp.close();
+            } catch (IOException ioe) { }
+        }
+    }
+    
+    /**
+     * @since 2.1
+     */
+    protected <T> MappingIterator<T> _bindAndReadValues(JsonParser p,
+            Object valueToUpdate)
+        throws IOException, JsonProcessingException
+    {
+        if (_schema != null) {
+            p.setSchema(_schema);
+        }
+        p.nextToken();
+        DeserializationContext ctxt = createDeserializationContext(p, _config);
+        return new MappingIterator<T>(_valueType, p, ctxt, 
+                _findRootDeserializer(ctxt, _valueType),
+                true, _valueToUpdate);
+    }
+    
+    protected static JsonToken _initForReading(JsonParser jp)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        /* 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 = jp.getCurrentToken();
+        if (t == null) { // and then we must get something...
+            t = jp.nextToken();
+            if (t == null) {
+                /* [JACKSON-546] Throw mapping exception, since it's failure to map,
+                 *   not an actual parsing problem
+                 */
+                throw JsonMappingException.from(jp, "No content to map due to end-of-input");
+            }
+        }
+        return t;
+    }
+
+    /**
+     * Method called to locate deserializer for the passed root-level value.
+     */
+    protected JsonDeserializer<Object> _findRootDeserializer(DeserializationContext ctxt,
+            JavaType valueType)
+        throws JsonMappingException
+    {
+        if (_rootDeserializer != null) {
+            return _rootDeserializer;
+        }
+
+        // Sanity check: must have actual type...
+        if (valueType == null) {
+            throw new JsonMappingException("No value type configured for ObjectReader");
+        }
+        
+        // First: have we already seen it?
+        JsonDeserializer<Object> deser = _rootDeserializers.get(valueType);
+        if (deser != null) {
+            return deser;
+        }
+        // Nope: need to ask provider to resolve it
+        deser = ctxt.findRootValueDeserializer(valueType);
+        if (deser == null) { // can this happen?
+            throw new JsonMappingException("Can not find a deserializer for type "+valueType);
+        }
+        _rootDeserializers.put(valueType, deser);
+        return deser;
+    }
+
+    /**
+     * Method called to locate deserializer ahead of time, if permitted
+     * by configuration. Method also is NOT to throw an exception if
+     * access fails.
+     */
+    protected JsonDeserializer<Object> _prefetchRootDeserializer(
+            DeserializationConfig config, JavaType valueType)
+    {
+        if (valueType == null || !_config.isEnabled(DeserializationFeature.EAGER_DESERIALIZER_FETCH)) {
+            return null;
+        }
+        // already cached?
+        JsonDeserializer<Object> deser = _rootDeserializers.get(valueType);
+        if (deser == null) {
+            try {
+                // If not, need to resolve; for which we need a temporary context as well:
+                DeserializationContext ctxt = createDeserializationContext(null, _config);
+                deser = ctxt.findRootValueDeserializer(valueType);
+                if (deser != null) {
+                    _rootDeserializers.put(valueType, deser);
+                }
+                return deser;
+                
+            } catch (JsonProcessingException e) {
+                // need to swallow?
+            }
+        }
+        return deser;
+    }
+    
+    protected Object _unwrapAndDeserialize(JsonParser jp, DeserializationContext ctxt,
+            JavaType rootType, JsonDeserializer<Object> deser)
+        throws IOException, JsonParseException, JsonMappingException
+    {
+        String expName = _config.getRootName();
+        if (expName == null) {
+            SerializedString sstr = _rootNames.findRootName(rootType, _config);
+            expName = sstr.getValue();
+        }
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw JsonMappingException.from(jp, "Current token not START_OBJECT (needed to unwrap root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        if (jp.nextToken() != JsonToken.FIELD_NAME) {
+            throw JsonMappingException.from(jp, "Current token not FIELD_NAME (to contain expected root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        String actualName = jp.getCurrentName();
+        if (!expName.equals(actualName)) {
+            throw JsonMappingException.from(jp, "Root name '"+actualName+"' does not match expected ('"
+                    +expName+"') for type "+rootType);
+        }
+        // ok, then move to value itself....
+        jp.nextToken();
+        Object result;
+        if (_valueToUpdate == null) {
+            result = deser.deserialize(jp, ctxt);
+        } else {
+            deser.deserialize(jp, ctxt, _valueToUpdate);
+            result = _valueToUpdate;                    
+        }
+        // and last, verify that we now get matching END_OBJECT
+        if (jp.nextToken() != JsonToken.END_OBJECT) {
+            throw JsonMappingException.from(jp, "Current token not END_OBJECT (to match wrapper object with root name '"
+                    +expName+"'), but "+jp.getCurrentToken());
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, format auto-detection (since 2.1)
+    /**********************************************************
+     */
+    
+    protected Object _detectBindAndClose(byte[] src, int offset, int length) throws IOException
+    {
+        DataFormatReaders.Match match = _dataFormatReaders.findFormat(src, offset, length);
+        if (!match.hasMatch()) {
+            _reportUnkownFormat(_dataFormatReaders, match);
+        }
+        JsonParser jp = match.createParserWithMatch();
+        return match.getReader()._bindAndClose(jp, _valueToUpdate);
+    }
+
+    protected Object _detectBindAndClose(DataFormatReaders.Match match, boolean forceClosing)
+        throws IOException
+    {
+        if (!match.hasMatch()) {
+            _reportUnkownFormat(_dataFormatReaders, match);
+        }
+        JsonParser p = match.createParserWithMatch();
+        // One more thing: we Own the input stream now; and while it's 
+        // not super clean way to do it, we must ensure closure so:
+        if (forceClosing) {
+            p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+        }
+        // important: use matching ObjectReader (may not be 'this')
+        return match.getReader()._bindAndClose(p, _valueToUpdate);
+    }
+
+    protected <T> MappingIterator<T> _detectBindAndReadValues(DataFormatReaders.Match match, boolean forceClosing)
+        throws IOException, JsonProcessingException
+    {
+        if (!match.hasMatch()) {
+            _reportUnkownFormat(_dataFormatReaders, match);
+        }
+        JsonParser p = match.createParserWithMatch();
+        // One more thing: we Own the input stream now; and while it's 
+        // not super clean way to do it, we must ensure closure so:
+        if (forceClosing) {
+            p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+        }
+        // important: use matching ObjectReader (may not be 'this')
+        return match.getReader()._bindAndReadValues(p, _valueToUpdate);
+    }
+    
+    protected JsonNode _detectBindAndCloseAsTree(InputStream in) throws IOException
+    {
+        DataFormatReaders.Match match = _dataFormatReaders.findFormat(in);
+        if (!match.hasMatch()) {
+            _reportUnkownFormat(_dataFormatReaders, match);
+        }
+        JsonParser p = match.createParserWithMatch();
+        p.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+        return match.getReader()._bindAndCloseAsTree(p);
+    }
+    
+    /**
+     * Method called to indicate that format detection failed to detect format
+     * of given input
+     */
+    protected void _reportUnkownFormat(DataFormatReaders detector, DataFormatReaders.Match match) throws JsonProcessingException
+    {
+        throw new JsonParseException("Can not detect format from input, does not look like any of detectable formats "
+                +detector.toString(),
+                JsonLocation.NA);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, other
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.2
+     */
+    protected void _verifySchemaType(FormatSchema schema)
+    {
+        if (schema != null) {
+            if (!_jsonFactory.canUseSchema(schema)) {
+                    throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName()
+                            +" for format "+_jsonFactory.getFormatName());
+            }
+        }
+    }
+
+    /**
+     * 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 jp,
+            DeserializationConfig cfg) {
+        // 04-Jan-2010, tatu: we do actually need the provider too... (for polymorphic deser)
+        return _context.createInstance(cfg, jp, _injectableValues);
+    }
+    
+    protected ObjectReader _with(DeserializationConfig newConfig) {
+        if (newConfig == _config) {
+            return this;
+        }
+        if (_dataFormatReaders != null) {
+            return new ObjectReader(this, newConfig)
+                .withFormatDetection(_dataFormatReaders.with(newConfig));
+        }
+        return new ObjectReader(this, newConfig);
+    }
+
+    protected void _reportUndetectableSource(Object src) throws JsonProcessingException
+    {
+        throw new JsonParseException("Can not use source of type "
+                +src.getClass().getName()+" with format auto-detection: must be byte- not char-based",
+                JsonLocation.NA);
+    }
+
+    protected InputStream _inputStream(URL src) throws IOException {
+        return src.openStream();
+    }
+
+    protected InputStream _inputStream(File f) throws IOException {
+        return new FileInputStream(f);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
new file mode 100644
index 0000000..530f577
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ObjectWriter.java
@@ -0,0 +1,813 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+import java.text.DateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SegmentedStringWriter;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.core.util.Instantiatable;
+import com.fasterxml.jackson.core.util.MinimalPrettyPrinter;
+
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
+import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.SerializerFactory;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Builder object that can be used for per-serialization configuration of
+ * serialization parameters, such as JSON View and root type to use.
+ * (and thus fully thread-safe with no external synchronization);
+ * new instances are constructed for different configurations.
+ * Instances are initially constructed by {@link ObjectMapper} and can be
+ * reused in completely thread-safe manner with no explicit synchronization
+ */
+public class ObjectWriter
+    implements Versioned,
+        java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = -7024829992408267532L;
+
+    /**
+     * We need to keep track of explicit disabling of pretty printing;
+     * easiest to do by a token value.
+     */
+    protected final static PrettyPrinter NULL_PRETTY_PRINTER = new MinimalPrettyPrinter();
+    
+    /*
+    /**********************************************************
+    /* Immutable configuration from ObjectMapper
+    /**********************************************************
+     */
+
+    /**
+     * General serialization configuration settings
+     */
+    protected final SerializationConfig _config;
+   
+    protected final DefaultSerializerProvider _serializerProvider;
+
+    protected final SerializerFactory _serializerFactory;
+
+    /**
+     * Factory used for constructing {@link JsonGenerator}s
+     */
+    protected final JsonFactory _jsonFactory;
+    
+    /*
+    /**********************************************************
+    /* Configuration that can be changed during building
+    /**********************************************************
+     */
+
+    /**
+     * Specified root serialization type to use; can be same
+     * as runtime type, but usually one of its super types
+     */
+    protected final JavaType _rootType;
+
+    /**
+     * We may pre-fetch serializer if {@link #_rootType}
+     * is known, and if so, reuse it afterwards.
+     * This allows avoiding further serializer lookups and increases
+     * performance a bit on cases where readers are reused.
+     * 
+     * @since 2.1
+     */
+    protected final JsonSerializer<Object> _rootSerializer;
+    
+    /**
+     * To allow for dynamic enabling/disabling of pretty printing,
+     * pretty printer can be optionally configured for writer
+     * as well
+     */
+    protected final PrettyPrinter _prettyPrinter;
+    
+    /**
+     * When using data format that uses a schema, schema is passed
+     * to generator.
+     */
+    protected final FormatSchema _schema;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, constructors
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by {@link ObjectMapper} for initial instantiation
+     */
+    protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
+            JavaType rootType, PrettyPrinter pp)
+    {
+        _config = config;
+
+        _serializerProvider = mapper._serializerProvider;
+        _serializerFactory = mapper._serializerFactory;
+        _jsonFactory = mapper._jsonFactory;
+
+        if (rootType != null) {
+            rootType = rootType.withStaticTyping();
+        }
+        _rootType = rootType;
+        _prettyPrinter = pp;
+        _schema = null;
+
+        _rootSerializer = _prefetchRootSerializer(config, rootType);
+    }
+
+    /**
+     * Alternative constructor for initial instantiation by {@link ObjectMapper}
+     */
+    protected ObjectWriter(ObjectMapper mapper, SerializationConfig config)
+    {
+        _config = config;
+
+        _serializerProvider = mapper._serializerProvider;
+        _serializerFactory = mapper._serializerFactory;
+        _jsonFactory = mapper._jsonFactory;
+
+        _rootType = null;
+        _rootSerializer = null;
+        _prettyPrinter = null;
+        _schema = null;
+    }
+
+    /**
+     * Alternative constructor for initial instantiation by {@link ObjectMapper}
+     */
+    protected ObjectWriter(ObjectMapper mapper, SerializationConfig config,
+            FormatSchema s)
+    {
+        _config = config;
+
+        _serializerProvider = mapper._serializerProvider;
+        _serializerFactory = mapper._serializerFactory;
+        _jsonFactory = mapper._jsonFactory;
+
+        _rootType = null;
+        _rootSerializer = null;
+        _prettyPrinter = null;
+        _schema = s;
+    }
+    
+    /**
+     * Copy constructor used for building variations.
+     */
+    protected ObjectWriter(ObjectWriter base, SerializationConfig config,
+            JavaType rootType, JsonSerializer<Object> rootSer,
+            PrettyPrinter pp, FormatSchema s)
+    {
+        _config = config;
+
+        _serializerProvider = base._serializerProvider;
+        _serializerFactory = base._serializerFactory;
+        _jsonFactory = base._jsonFactory;
+
+        _rootType = rootType;
+        _rootSerializer = rootSer;
+        _prettyPrinter = pp;
+        _schema = s;
+    }
+
+    /**
+     * Copy constructor used for building variations.
+     */
+    protected ObjectWriter(ObjectWriter base, SerializationConfig config)
+    {
+        _config = config;
+
+        _serializerProvider = base._serializerProvider;
+        _serializerFactory = base._serializerFactory;
+        _jsonFactory = base._jsonFactory;
+        _schema = base._schema;
+
+        _rootType = base._rootType;
+        _rootSerializer = base._rootSerializer;
+        _prettyPrinter = base._prettyPrinter;
+    }
+    
+    /**
+     * Method that will return version information stored in and read from jar
+     * that contains this class.
+     */
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, fluent factories
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified feature enabled.
+     */
+    public ObjectWriter with(SerializationFeature feature) 
+    {
+        SerializationConfig newConfig = _config.with(feature);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }
+
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectWriter with(SerializationFeature first,
+            SerializationFeature... other)
+    {
+        SerializationConfig newConfig = _config.with(first, other);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }    
+
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectWriter withFeatures(SerializationFeature... features)
+    {
+        SerializationConfig newConfig = _config.withFeatures(features);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }    
+    
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified feature enabled.
+     */
+    public ObjectWriter without(SerializationFeature feature) 
+    {
+        SerializationConfig newConfig = _config.without(feature);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }    
+
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectWriter without(SerializationFeature first,
+            SerializationFeature... other)
+    {
+        SerializationConfig newConfig = _config.without(first, other);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }    
+
+    /**
+     * Method for constructing a new instance that is configured
+     * with specified features enabled.
+     */
+    public ObjectWriter withoutFeatures(SerializationFeature... features)
+    {
+        SerializationConfig newConfig = _config.withoutFeatures(features);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }    
+    
+    /**
+     * Fluent factory method that will construct a new writer instance that will
+     * use specified date format for serializing dates; or if null passed, one
+     * that will serialize dates as numeric timestamps.
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectWriter with(DateFormat df)
+    {
+        SerializationConfig newConfig = _config.with(df);
+        return (newConfig == _config) ? this : new ObjectWriter(this, newConfig);
+    }
+
+    /**
+     * Method that will construct a new instance that will use the default
+     * pretty printer for serialization.
+     */
+    public ObjectWriter withDefaultPrettyPrinter()
+    {
+        return with(new DefaultPrettyPrinter());
+    }
+
+    /**
+     * Method that will construct a new instance that uses specified
+     * provider for resolving filter instances by id.
+     */
+    public ObjectWriter with(FilterProvider filterProvider)
+    {
+        if (filterProvider == _config.getFilterProvider()) { // no change?
+            return this;
+        }
+        return new ObjectWriter(this, _config.withFilters(filterProvider));
+    }
+
+    /**
+     * Method that will construct a new instance that will use specified pretty
+     * printer (or, if null, will not do any pretty-printing)
+     */
+    public ObjectWriter with(PrettyPrinter pp)
+    {
+        if (pp == _prettyPrinter) {
+            return this;
+        }
+        // since null would mean "don't care", need to use placeholder to indicate "disable"
+        if (pp == null) {
+            pp = NULL_PRETTY_PRINTER;
+        }
+        return new ObjectWriter(this, _config, _rootType, _rootSerializer, pp, _schema);
+    }
+
+    /**
+     * Method for constructing a new instance with configuration that
+     * specifies what root name to use for "root element wrapping".
+     * See {@link SerializationConfig#withRootName(String)} for details.
+     *<p>
+     * Note that method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectWriter withRootName(String rootName)
+    {
+        SerializationConfig newConfig = _config.withRootName(rootName);
+        return (newConfig == _config) ? this :  new ObjectWriter(this, newConfig);
+    }
+
+    /**
+     * Method that will construct a new instance that uses specific format schema
+     * for serialization.
+     *<p>
+     * Note that method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    
+    public ObjectWriter withSchema(FormatSchema schema)
+    {
+        if (_schema == schema) {
+            return this;
+        }
+        _verifySchemaType(schema);
+        return new ObjectWriter(this, _config, _rootType, _rootSerializer, _prettyPrinter, schema);
+    }
+
+    /**
+     * Method that will construct a new instance that uses specific type
+     * as the root type for serialization, instead of runtime dynamic
+     * type of the root object itself.
+     *<p>
+     * Note that method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectWriter withType(JavaType rootType)
+    {
+        // 15-Mar-2013, tatu: Important! Indicate that static typing is needed:
+        rootType = rootType.withStaticTyping();
+        JsonSerializer<Object> rootSer = _prefetchRootSerializer(_config, rootType);
+        return new ObjectWriter(this, _config, rootType, rootSer, _prettyPrinter, _schema);
+    }    
+
+    /**
+     * Method that will construct a new instance that uses specific type
+     * as the root type for serialization, instead of runtime dynamic
+     * type of the root object itself.
+     */
+    public ObjectWriter withType(Class<?> rootType) {
+        return withType(_config.constructType(rootType));
+    }
+
+    public ObjectWriter withType(TypeReference<?> rootType) {
+        return withType(_config.getTypeFactory().constructType(rootType.getType()));
+    }
+
+    /**
+     * Method that will construct a new instance that uses specified
+     * serialization view for serialization (with null basically disables
+     * view processing)
+     *<p>
+     * Note that the method does NOT change state of this reader, but
+     * rather construct and returns a newly configured instance.
+     */
+    public ObjectWriter withView(Class<?> view) {
+        SerializationConfig newConfig = _config.withView(view);
+        return (newConfig == _config) ? this :  new ObjectWriter(this, newConfig);
+    }    
+
+    public ObjectWriter with(Locale l) {
+        SerializationConfig newConfig = _config.with(l);
+        return (newConfig == _config) ? this :  new ObjectWriter(this, newConfig);
+    }
+
+    public ObjectWriter with(TimeZone tz) {
+        SerializationConfig newConfig = _config.with(tz);
+        return (newConfig == _config) ? this :  new ObjectWriter(this, newConfig);
+    }
+
+    /**
+     * Method that will construct a new instance that uses specified default
+     * {@link Base64Variant} for base64 encoding
+     * 
+     * @since 2.1
+     */
+    public ObjectWriter with(Base64Variant b64variant) {
+        SerializationConfig newConfig = _config.with(b64variant);
+        return (newConfig == _config) ? this :  new ObjectWriter(this, newConfig);
+    }
+    
+    /*
+    /**********************************************************
+    /* Simple accessors
+    /**********************************************************
+     */
+
+    public boolean isEnabled(SerializationFeature f) {
+        return _config.isEnabled(f);
+    }
+
+    public boolean isEnabled(MapperFeature f) {
+        return _config.isEnabled(f);
+    }
+
+    public boolean isEnabled(JsonParser.Feature f) {
+        return _jsonFactory.isEnabled(f);
+    }
+
+    /**
+     * @since 2.2
+     */
+    public SerializationConfig getConfig() {
+        return _config;
+    }
+
+    /**
+     * @deprecated Since 2.2, use {@link #getFactory} instead.
+     */
+    @Deprecated
+    public JsonFactory getJsonFactory() {
+        return _jsonFactory;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonFactory getFactory() {
+        return _jsonFactory;
+    }
+    
+    public TypeFactory getTypeFactory() {
+        return _config.getTypeFactory();
+    }
+
+    /**
+     * Diagnostics method that can be called to check whether this writer
+     * has pre-fetched serializer to use: pre-fetching improves performance
+     * when writer instances are reused as it avoids a per-call serializer
+     * lookup.
+     * 
+     * @since 2.2
+     */
+    public boolean hasPrefetchedSerializer() {
+        return _rootSerializer != null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization methods; ones from ObjectCodec first
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using provided {@link JsonGenerator}.
+     */
+    public void writeValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        // 10-Aug-2012, tatu: As per [Issue#12], may need to force PrettyPrinter settings, so:
+        _configureJsonGenerator(jgen);
+        if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE)
+                && (value instanceof Closeable)) {
+            _writeCloseableValue(jgen, value, _config);
+        } else {
+            if (_rootType == null) {
+                _serializerProvider(_config).serializeValue(jgen, value);
+            } else {
+                _serializerProvider(_config).serializeValue(jgen, value, _rootType, _rootSerializer);
+            }
+            if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
+                jgen.flush();
+            }
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization methods, others
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, written to File provided.
+     */
+    public void writeValue(File resultFile, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(resultFile, JsonEncoding.UTF8), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using output stream provided (using encoding
+     * {@link JsonEncoding#UTF8}).
+     *<p>
+     * Note: method does not close the underlying stream explicitly
+     * here; however, {@link JsonFactory} this mapper uses may choose
+     * to close the stream depending on its settings (by default,
+     * it will try to close it when {@link JsonGenerator} we construct
+     * is closed).
+     */
+    public void writeValue(OutputStream out, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(out, JsonEncoding.UTF8), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * JSON output, using Writer provided.
+     *<p>
+     * Note: method does not close the underlying stream explicitly
+     * here; however, {@link JsonFactory} this mapper uses may choose
+     * to close the stream depending on its settings (by default,
+     * it will try to close it when {@link JsonGenerator} we construct
+     * is closed).
+     */
+    public void writeValue(Writer w, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configAndWriteValue(_jsonFactory.createGenerator(w), value);
+    }
+
+    /**
+     * Method that can be used to serialize any Java value as
+     * a String. Functionally equivalent to calling
+     * {@link #writeValue(Writer,Object)} with {@link java.io.StringWriter}
+     * and constructing String, but more efficient.
+     *<p>
+     * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it.
+     */
+    public String writeValueAsString(Object value)
+        throws JsonProcessingException
+    {        
+        // alas, we have to pull the recycler directly here...
+        SegmentedStringWriter sw = new SegmentedStringWriter(_jsonFactory._getBufferRecycler());
+        try {
+            _configAndWriteValue(_jsonFactory.createGenerator(sw), value);
+        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+            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
+     * {@link #writeValue(Writer,Object)} with {@link java.io.ByteArrayOutputStream}
+     * and getting bytes, but more efficient.
+     * Encoding used will be UTF-8.
+     *<p>
+     * Note: prior to version 2.1, throws clause included {@link IOException}; 2.1 removed it.
+     */
+    public byte[] writeValueAsBytes(Object value)
+        throws JsonProcessingException
+    {
+        ByteArrayBuilder bb = new ByteArrayBuilder(_jsonFactory._getBufferRecycler());
+        try {
+            _configAndWriteValue(_jsonFactory.createGenerator(bb, JsonEncoding.UTF8), value);
+        } catch (JsonProcessingException e) { // to support [JACKSON-758]
+            throw e;
+        } catch (IOException e) { // shouldn't really happen, but is declared as possibility so:
+            throw JsonMappingException.fromUnexpectedIOE(e);
+        }
+        byte[] result = bb.toByteArray();
+        bb.release();
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Other public methods
+    /**********************************************************
+     */
+
+    /**
+     * Method for visiting type hierarchy for given type, using specified visitor.
+     * Visitation uses <code>Serializer</code> hierarchy and related properties
+     *<p>
+     * This method can be used for things like
+     * generating <a href="http://json-schema.org/">Json Schema</a>
+     * instance for specified type.
+     *
+     * @param type Type to generate schema for (possibly with generic signature)
+     * 
+     * @since 2.2
+     */
+    public void acceptJsonFormatVisitor(JavaType type, JsonFormatVisitorWrapper visitor)
+        throws JsonMappingException
+    {
+        if (type == null) {
+            throw new IllegalArgumentException("type must be provided");
+        }
+        _serializerProvider(_config).acceptJsonFormatVisitor(type, visitor);
+    }
+    
+    public boolean canSerialize(Class<?> type) {
+        return _serializerProvider(_config).hasSerializerFor(type);
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable helper methods
+    /**********************************************************
+     */
+    
+    /**
+     * Overridable helper method used for constructing
+     * {@link SerializerProvider} to use for serialization.
+     */
+    protected DefaultSerializerProvider _serializerProvider(SerializationConfig config) {
+        return _serializerProvider.createInstance(config, _serializerFactory);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.2
+     */
+    protected void _verifySchemaType(FormatSchema schema)
+    {
+        if (schema != null) {
+            if (!_jsonFactory.canUseSchema(schema)) {
+                    throw new IllegalArgumentException("Can not use FormatSchema of type "+schema.getClass().getName()
+                            +" for format "+_jsonFactory.getFormatName());
+            }
+        }
+    }
+
+    /**
+     * Method called to configure the generator as necessary and then
+     * call write functionality
+     */
+    protected final void _configAndWriteValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        _configureJsonGenerator(jgen);
+        // [JACKSON-282]: consider Closeable
+        if (_config.isEnabled(SerializationFeature.CLOSE_CLOSEABLE) && (value instanceof Closeable)) {
+            _writeCloseable(jgen, value, _config);
+            return;
+        }
+        boolean closed = false;
+        try {
+            if (_rootType == null) {
+                _serializerProvider(_config).serializeValue(jgen, value);
+            } else {
+                _serializerProvider(_config).serializeValue(jgen, value, _rootType, _rootSerializer);
+            }
+            closed = true;
+            jgen.close();
+        } finally {
+            /* won't try to close twice; also, must catch exception (so it 
+             * will not mask exception that is pending)
+             */
+            if (!closed) {
+                try {
+                    jgen.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+
+    /**
+     * Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
+     * method is to be called right after serialization has been called
+     */
+    private final void _writeCloseable(JsonGenerator jgen, Object value, SerializationConfig cfg)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        Closeable toClose = (Closeable) value;
+        try {
+            if (_rootType == null) {
+                _serializerProvider(cfg).serializeValue(jgen, value);
+            } else {
+                _serializerProvider(cfg).serializeValue(jgen, value, _rootType, _rootSerializer);
+            }
+            JsonGenerator tmpJgen = jgen;
+            jgen = null;
+            tmpJgen.close();
+            Closeable tmpToClose = toClose;
+            toClose = null;
+            tmpToClose.close();
+        } finally {
+            /* Need to close both generator and value, as long as they haven't yet
+             * been closed
+             */
+            if (jgen != null) {
+                try {
+                    jgen.close();
+                } catch (IOException ioe) { }
+            }
+            if (toClose != null) {
+                try {
+                    toClose.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+    
+    /**
+     * Helper method used when value to serialize is {@link Closeable} and its <code>close()</code>
+     * method is to be called right after serialization has been called
+     */
+    private final void _writeCloseableValue(JsonGenerator jgen, Object value, SerializationConfig cfg)
+        throws IOException, JsonGenerationException, JsonMappingException
+    {
+        Closeable toClose = (Closeable) value;
+        try {
+            if (_rootType == null) {
+                _serializerProvider(cfg).serializeValue(jgen, value);
+            } else {
+                _serializerProvider(cfg).serializeValue(jgen, value, _rootType, _rootSerializer);
+            }
+            if (_config.isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)) {
+                jgen.flush();
+            }
+            Closeable tmpToClose = toClose;
+            toClose = null;
+            tmpToClose.close();
+        } finally {
+            if (toClose != null) {
+                try {
+                    toClose.close();
+                } catch (IOException ioe) { }
+            }
+        }
+    }
+
+    /**
+     * Method called to locate (root) serializer ahead of time, if permitted
+     * by configuration. Method also is NOT to throw an exception if
+     * access fails.
+     */
+    protected JsonSerializer<Object> _prefetchRootSerializer(
+            SerializationConfig config, JavaType valueType)
+    {
+        if (valueType == null || !_config.isEnabled(SerializationFeature.EAGER_SERIALIZER_FETCH)) {
+            return null;
+        }
+        try {
+            return _serializerProvider(config).findTypedValueSerializer(valueType, true, null);
+        } catch (JsonProcessingException e) {
+            // need to swallow?
+            return null;
+        }
+    }
+    
+    /**
+     * Helper method called to set or override settings of passed-in
+     * {@link JsonGenerator}
+     * 
+     * @since 2.1
+     */
+    private void _configureJsonGenerator(JsonGenerator jgen)
+    {
+        if (_prettyPrinter != null) {
+            PrettyPrinter pp = _prettyPrinter;
+            if (pp == NULL_PRETTY_PRINTER) {
+                jgen.setPrettyPrinter(null);
+            } else {
+                /* [JACKSON-851]: Better take care of stateful PrettyPrinters...
+                 *   like the DefaultPrettyPrinter.
+                 */
+                if (pp instanceof Instantiatable<?>) {
+                    pp = (PrettyPrinter) ((Instantiatable<?>) pp).createInstance();
+                }
+                jgen.setPrettyPrinter(pp);
+            }
+        } else if (_config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
+            jgen.useDefaultPrettyPrinter();
+        }
+        // [JACKSON-520]: add support for pass-through schema:
+        if (_schema != null) {
+            jgen.setSchema(_schema);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyName.java b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java
new file mode 100644
index 0000000..e033b20
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/PropertyName.java
@@ -0,0 +1,173 @@
+package com.fasterxml.jackson.databind;
+
+/**
+ * Simple value class used for containing names of properties as defined
+ * by annotations (and possibly other configuration sources).
+ * 
+ * @since 2.1
+ */
+public class PropertyName
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 7930806520033045126L;
+
+    private final static String _USE_DEFAULT = "";
+    private final static String _NO_NAME = "#disabled";
+
+    /**
+     * Special placeholder value that indicates that name to use should be
+     * based on the standard heuristics. This can be different from returning
+     * null, as null means "no information available, whereas this value
+     * indicates explicit defaulting.
+     */
+    public final static PropertyName USE_DEFAULT = new PropertyName(_USE_DEFAULT, null);
+
+    /**
+     * Special placeholder value that indicates that there is no name associated.
+     * Exact semantics to use (if any) depend on actual annotation in use, but
+     * commonly this value disables behavior for which name would be needed.
+     */
+    public final static PropertyName NO_NAME = new PropertyName(new String(_NO_NAME), null);
+    
+    /**
+     * Basic name of the property.
+     */
+    protected final String _simpleName;
+
+    /**
+     * Additional namespace, for formats that have such concept (JSON
+     * does not, XML does, for example).
+     */
+    protected final String _namespace;
+
+    public PropertyName(String simpleName) {
+        this(simpleName, null);
+    }
+
+    public PropertyName(String simpleName, String namespace)
+    {
+        _simpleName = (simpleName == null) ? "" : 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)) {
+            return NO_NAME;
+        }
+        return this;
+    }
+    
+    public static PropertyName construct(String simpleName, String ns)
+    {
+        if (simpleName == null) {
+            simpleName = "";
+        }
+        if (ns == null && simpleName.length() == 0) {
+            return USE_DEFAULT;
+        }
+        return new PropertyName(simpleName, ns);
+    }
+    
+    /**
+     * Fluent factory method for constructing an instance with different
+     * simple name.
+     */
+    public PropertyName withSimpleName(String simpleName)
+    {
+        if (simpleName == null) {
+            simpleName = "";
+        }
+        if (simpleName.equals(_simpleName)) {
+            return this;
+        }
+        return new PropertyName(simpleName, _namespace);
+    }
+    
+    /**
+     * Fluent factory method for constructing an instance with different
+     * namespace.
+     */
+    public PropertyName withNamespace(String ns) {
+        if (ns == null) {
+            if (_namespace == null) {
+                return this;
+            }
+        } else if (ns.equals(_namespace)) {
+            return this;
+        }
+        return new PropertyName(_simpleName, ns);
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    public String getSimpleName() {
+        return _simpleName;
+    }
+
+    public String getNamespace() {
+        return _namespace;
+    }
+
+    public boolean hasSimpleName() {
+        return _simpleName.length() > 0;
+    }
+
+    public boolean hasNamespace() {
+        return _namespace != null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Std method overrides
+    /**********************************************************
+     */
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        /* 13-Nov-2012, tatu: by default, require strict type equality.
+         *   Re-evaluate if this becomes an issue.
+         */
+        if (o.getClass() != getClass()) return false;
+        // 13-Nov-2012, tatu: Should we have specific rules on matching USE_DEFAULT?
+        //   (like, it only ever matching exact instance)
+        //   If we did, would need to check symmetrically; that is, if either 'this'
+        //   or 'o' was USE_DEFAULT, both would have to be.
+        PropertyName other = (PropertyName) o;
+        if (_simpleName == null) {
+            if (other._simpleName != null) return false;
+        } else if (!_simpleName.equals(other._simpleName)) {
+            return false;
+        }
+        if (_namespace == null) {
+            return (null == other._namespace);
+        }
+        return _namespace.equals(other._namespace);
+    }
+    
+    @Override
+    public int hashCode() {
+        if (_namespace == null) {
+            return _simpleName.hashCode();
+        }
+        return _namespace.hashCode() ^  _simpleName.hashCode();
+    }
+    
+    @Override
+    public String toString() {
+        if (_namespace == null) {
+            return _simpleName;
+        }
+        return "{"+_namespace + "}" + _simpleName;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
new file mode 100644
index 0000000..36999f4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/PropertyNamingStrategy.java
@@ -0,0 +1,301 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+
+/**
+ * Class that defines how names of JSON properties ("external names")
+ * are derived from names of POJO methods and fields ("internal names"),
+ * in cases where they are not
+ * auto-detected and no explicit annotations exist for naming.
+ * Methods are passed information about POJO member for which name is needed,
+ * as well as default name that would be used if no custom strategy was used.
+ *<p>
+ * Default implementation returns suggested ("default") name unmodified.
+ *<p>
+ * Note that the strategy is guaranteed to be called once per logical property
+ * (which may be represented by multiple members; such as pair of a getter and
+ * a setter), but may be called for each: implementations should not count on
+ * exact number of times, and should work for any member that represent a
+ * property.
+ *<p>
+ * In absence of a registered custom strategy, default Java property naming strategy
+ * is used, which leaves field names as is, and removes set/get/is prefix
+ * from methods (as well as lower-cases initial sequence of capitalized
+ * characters).
+ */
+ at SuppressWarnings("serial")
+public abstract class PropertyNamingStrategy
+    implements java.io.Serializable
+{
+    /*
+    /**********************************************************
+    /* API
+    /**********************************************************
+     */
+
+    /**
+     * Method called to find external name (name used in JSON) for given logical
+     * POJO property,
+     * as defined by given field.
+     * 
+     * @param config Configuration in used: either <code>SerializationConfig</code>
+     *   or <code>DeserializationConfig</code>, depending on whether method is called
+     *   during serialization or deserialization
+     * @param field Field used to access property
+     * @param defaultName Default name that would be used for property in absence of custom strategy
+     * 
+     * @return Logical name to use for property that the field represents
+     */
+    public String nameForField(MapperConfig<?> config, AnnotatedField field,
+            String defaultName)
+    {
+        return defaultName;
+    }
+
+    /**
+     * Method called to find external name (name used in JSON) for given logical
+     * POJO property,
+     * as defined by given getter method; typically called when building a serializer.
+     * (but not always -- when using "getter-as-setter", may be called during
+     * deserialization)
+     * 
+     * @param config Configuration in used: either <code>SerializationConfig</code>
+     *   or <code>DeserializationConfig</code>, depending on whether method is called
+     *   during serialization or deserialization
+     * @param method Method used to access property.
+     * @param defaultName Default name that would be used for property in absence of custom strategy
+     * 
+     * @return Logical name to use for property that the method represents
+     */
+    public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,
+            String defaultName)
+    {
+        return defaultName;
+    }
+
+    /**
+     * Method called to find external name (name used in JSON) for given logical
+     * POJO property,
+     * as defined by given setter method; typically called when building a deserializer
+     * (but not necessarily only then).
+     * 
+     * @param config Configuration in used: either <code>SerializationConfig</code>
+     *   or <code>DeserializationConfig</code>, depending on whether method is called
+     *   during serialization or deserialization
+     * @param method Method used to access property.
+     * @param defaultName Default name that would be used for property in absence of custom strategy
+     * 
+     * @return Logical name to use for property that the method represents
+     */
+    public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,
+            String defaultName)
+    {
+        return defaultName;
+    }
+
+    /**
+     * Method called to find external name (name used in JSON) for given logical
+     * POJO property,
+     * as defined by given constructor parameter; typically called when building a deserializer
+     * (but not necessarily only then).
+     * 
+     * @param config Configuration in used: either <code>SerializationConfig</code>
+     *   or <code>DeserializationConfig</code>, depending on whether method is called
+     *   during serialization or deserialization
+     * @param ctorParam Constructor parameter used to pass property.
+     * @param defaultName Default name that would be used for property in absence of custom strategy
+     */
+    public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
+            String defaultName)
+    {
+        return defaultName;
+    }
+
+    /*
+    /**********************************************************
+    /* Standard implementations 
+    /**********************************************************
+     */
+    
+    public static abstract class PropertyNamingStrategyBase extends PropertyNamingStrategy
+    {
+        @Override
+        public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
+        {
+            return translate(defaultName);
+        }
+
+        @Override
+        public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
+        {
+            return translate(defaultName);
+        }
+
+        @Override
+        public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
+        {
+            return translate(defaultName);
+        }
+
+        @Override
+        public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,
+                String defaultName)
+        {
+            return translate(defaultName);
+        }
+        
+        public abstract String translate(String propertyName);
+    }
+        
+    
+    /*
+    /**********************************************************
+    /* Standard implementations 
+    /**********************************************************
+     */
+
+    /**
+     * See {@link LowerCaseWithUnderscoresStrategy} for details.
+     */
+    public static final PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES =
+        new LowerCaseWithUnderscoresStrategy();
+    
+    /**
+     * A {@link PropertyNamingStrategy} that translates typical camel case Java 
+     * property names to lower case JSON element names, separated by 
+     * underscores.  This implementation is somewhat lenient, in that it 
+     * provides some additional translations beyond strictly translating from 
+     * camel case only.  In particular, the following translations are applied 
+     * by this PropertyNamingStrategy.
+     * 
+     * <ul><li>Every upper case letter in the Java property name is translated 
+     * into two characters, an underscore and the lower case equivalent of the 
+     * target character, with three exceptions.
+     * <ol><li>For contiguous sequences of upper case letters, characters after
+     * the first character are replaced only by their lower case equivalent, 
+     * and are not preceded by an underscore.
+     * <ul><li>This provides for reasonable translations of upper case acronyms, 
+     * e.g., "theWWW" is translated to "the_www".</li></ul></li>
+     * <li>An upper case character in the first position of the Java property 
+     * name is not preceded by an underscore character, and is translated only 
+     * to its lower case equivalent.
+     * <ul><li>For example, "Results" is translated to "results", 
+     * and not to "_results".</li></ul></li>
+     * <li>An upper case character in the Java property name that is already 
+     * preceded by an underscore character is translated only to its lower case 
+     * equivalent, and is not preceded by an additional underscore.
+     * <ul><li>For example, "user_Name" is translated to 
+     * "user_name", and not to "user__name" (with two 
+     * underscore characters).</li></ul></li></ol></li>
+     * <li>If the Java property name starts with an underscore, then that 
+     * underscore is not included in the translated name, unless the Java 
+     * property name is just one character in length, i.e., it is the 
+     * underscore character.  This applies only to the first character of the 
+     * Java property name.</li></ul>
+     * 
+     * These rules result in the following additional example translations from 
+     * Java property names to JSON element names.
+     * <ul><li>"userName" is translated to "user_name"</li>
+     * <li>"UserName" is translated to "user_name"</li>
+     * <li>"USER_NAME" is translated to "user_name"</li>
+     * <li>"user_name" is translated to "user_name" (unchanged)</li>
+     * <li>"user" is translated to "user" (unchanged)</li>
+     * <li>"User" is translated to "user"</li>
+     * <li>"USER" is translated to "user"</li>
+     * <li>"_user" is translated to "user"</li>
+     * <li>"_User" is translated to "user"</li>
+     * <li>"__user" is translated to "_user" 
+     * (the first of two underscores was removed)</li>
+     * <li>"user__name" is translated to "user__name"
+     * (unchanged, with two underscores)</li></ul>
+     */
+    public static class LowerCaseWithUnderscoresStrategy extends PropertyNamingStrategyBase
+    {
+        @Override
+        public String translate(String input)
+        {
+            if (input == null) return input; // garbage in, garbage out
+            int length = input.length();
+            StringBuilder result = new StringBuilder(length * 2);
+            int resultLength = 0;
+            boolean wasPrevTranslated = false;
+            for (int i = 0; i < length; i++)
+            {
+                char c = input.charAt(i);
+                if (i > 0 || c != '_') // skip first starting underscore
+                {
+                    if (Character.isUpperCase(c))
+                    {
+                        if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
+                        {
+                            result.append('_');
+                            resultLength++;
+                        }
+                        c = Character.toLowerCase(c);
+                        wasPrevTranslated = true;
+                    }
+                    else
+                    {
+                        wasPrevTranslated = false;
+                    }
+                    result.append(c);
+                    resultLength++;
+                }
+            }
+            return resultLength > 0 ? result.toString() : input;
+        }
+    }
+    
+    /**
+     * See {@link PascalCaseStrategy} for details.
+     * 
+     * @since 2.1
+     */
+    public static final PropertyNamingStrategy PASCAL_CASE_TO_CAMEL_CASE =
+        new PascalCaseStrategy();
+    
+    /**
+     * A {@link PropertyNamingStrategy} that translates typical camelCase Java 
+     * property names to PascalCase JSON element names (i.e., with a capital
+     * first letter).  In particular, the following translations are applied by 
+     * this PropertyNamingStrategy.
+     * 
+     * <ul><li>The first lower-case letter in the Java property name is translated 
+     * into its equivalent upper-case representation.</li></ul>
+     * 
+     * This rules result in the following example translation from 
+     * Java property names to JSON element names.
+     * <ul><li>"userName" is translated to "UserName"</li></ul>
+     * 
+     * @since 2.1
+     */
+    public static class PascalCaseStrategy extends PropertyNamingStrategyBase
+    {
+    	/**
+    	 * Converts camelCase to PascalCase
+    	 * 
+    	 * For example, "userName" would be converted to
+    	 * "UserName".
+      	 * 
+    	 * @param input formatted as camelCase string
+    	 * @return input converted to PascalCase format
+    	 */
+    	@Override
+    	public String translate(String input) {
+    	    if (input == null || input.length() == 0){
+    	        return input; // garbage in, garbage out
+    	    }
+    	    // Replace first lower-case letter with upper-case equivalent
+    	    char c = input.charAt(0);
+    	    if (Character.isUpperCase(c)) {
+    	        return input;
+    	    }
+    	    StringBuilder sb = new StringBuilder(input);
+    	    sb.setCharAt(0, Character.toUpperCase(c));
+    	    return sb.toString();
+    	}	
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java b/src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java
new file mode 100644
index 0000000..22508ac
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/RuntimeJsonMappingException.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.databind;
+
+/**
+ * Wrapper used when interface does not allow throwing a checked
+ * {@link JsonMappingException}
+ */
+ at SuppressWarnings("serial")
+public class RuntimeJsonMappingException extends RuntimeException
+{
+    public RuntimeJsonMappingException(JsonMappingException cause) {
+        super(cause);
+    }
+
+    public RuntimeJsonMappingException(String message) {
+        super(message);
+    }
+
+    public RuntimeJsonMappingException(String message, JsonMappingException cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
new file mode 100644
index 0000000..a6caf5b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationConfig.java
@@ -0,0 +1,503 @@
+package com.fasterxml.jackson.databind;
+
+import java.text.DateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.core.Base64Variant;
+import com.fasterxml.jackson.databind.cfg.BaseSettings;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfigBase;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+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.ClassKey;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Object that contains baseline configuration for serialization
+ * process. An instance is owned by {@link ObjectMapper}, which
+ * passes an immutable instance for serialization process to
+ * {@link SerializerProvider} and {@link SerializerFactory}
+ * (either directly, or through {@link ObjectWriter}.
+ *<p>
+ * Note that instances are considered immutable and as such no copies
+ * should need to be created for sharing; all copying is done with
+ * "fluent factory" methods.
+ * Note also that unlike with Jackson 1, these instances can not be
+ * assigned to {@link ObjectMapper}; in fact, application code should
+ * rarely interact directly with these instance (unlike core Jackson code)
+ */
+public final class SerializationConfig
+    extends MapperConfigBase<SerializationFeature, SerializationConfig>
+    implements java.io.Serializable // since 2.1
+{
+    // for 2.1.0:
+    private static final long serialVersionUID = 8849092838541724233L;
+
+    /**
+     * Set of features enabled; actual type (kind of features)
+     * depends on sub-classes.
+     */
+    protected final int _serFeatures;
+    
+    /**
+     * 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.
+     */
+    protected JsonInclude.Include _serializationInclusion = null;
+    
+    /**
+     * Object used for resolving filter ids to filter instances.
+     * Non-null if explicitly defined; null by default.
+     */
+    protected final FilterProvider _filterProvider;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, constructors
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by ObjectMapper to create default configuration object instance.
+     */
+    public SerializationConfig(BaseSettings base,
+            SubtypeResolver str, Map<ClassKey,Class<?>> mixins)
+    {
+        super(base, str, mixins);
+        _serFeatures = collectFeatureDefaults(SerializationFeature.class);
+        _filterProvider = null;
+    }
+    
+    private SerializationConfig(SerializationConfig src, SubtypeResolver str)
+    {
+        super(src, str);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+
+    private SerializationConfig(SerializationConfig src,
+            int mapperFeatures, int serFeatures)
+    {
+        super(src, mapperFeatures);
+        _serFeatures = serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+    
+    private SerializationConfig(SerializationConfig src, BaseSettings base)
+    {
+        super(src, base);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+
+    private SerializationConfig(SerializationConfig src, FilterProvider filters)
+    {
+        super(src);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = filters;
+    }
+
+    private SerializationConfig(SerializationConfig src, Class<?> view)
+    {
+        super(src, view);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+
+    private SerializationConfig(SerializationConfig src, JsonInclude.Include incl)
+    {
+        super(src);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = incl;
+        _filterProvider = src._filterProvider;
+    }
+
+    private SerializationConfig(SerializationConfig src, String rootName)
+    {
+        super(src, rootName);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+
+    /**
+     * @since 2.1
+     */
+    protected SerializationConfig(SerializationConfig src, Map<ClassKey,Class<?>> mixins)
+    {
+        super(src, mixins);
+        _serFeatures = src._serFeatures;
+        _serializationInclusion = src._serializationInclusion;
+        _filterProvider = src._filterProvider;
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, factory methods from MapperConfig
+    /**********************************************************
+     */
+
+    /**
+     * 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);
+    }
+    
+    /**
+     * 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);
+    }
+
+    @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(String rootName) {
+        if (rootName == null) {
+            if (_rootName == null) {
+                return this;
+            }
+        } else if (rootName.equals(_rootName)) {
+            return this;
+        }
+        return new SerializationConfig(this, rootName);
+    }
+
+    @Override
+    public SerializationConfig with(SubtypeResolver str) {
+        return (str == _subtypeResolver)? this : new SerializationConfig(this, str);
+    }
+
+    @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));
+    }
+    
+    private final SerializationConfig _withBase(BaseSettings newBase) {
+        return (_base == newBase) ? this : new SerializationConfig(this, newBase);
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, SerializationConfig specific factory methods
+    /**********************************************************
+     */
+        
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified feature enabled.
+     */
+    public SerializationConfig with(SerializationFeature feature)
+    {
+        int newSerFeatures = _serFeatures | feature.getMask();
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    public SerializationConfig with(SerializationFeature first, SerializationFeature... features)
+    {
+        int newSerFeatures = _serFeatures | first.getMask();
+        for (SerializationFeature f : features) {
+            newSerFeatures |= f.getMask();
+        }
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+    
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features enabled.
+     */
+    public SerializationConfig withFeatures(SerializationFeature... features)
+    {
+        int newSerFeatures = _serFeatures;
+        for (SerializationFeature f : features) {
+            newSerFeatures |= f.getMask();
+        }
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified feature disabled.
+     */
+    public SerializationConfig without(SerializationFeature feature)
+    {
+        int newSerFeatures = _serFeatures & ~feature.getMask();
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features disabled.
+     */
+    public SerializationConfig without(SerializationFeature first, SerializationFeature... features)
+    {
+        int newSerFeatures = _serFeatures & ~first.getMask();
+        for (SerializationFeature f : features) {
+            newSerFeatures &= ~f.getMask();
+        }
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+
+    /**
+     * Fluent factory method that will construct and return a new configuration
+     * object instance with specified features disabled.
+     */
+    public SerializationConfig withoutFeatures(SerializationFeature... features)
+    {
+        int newSerFeatures = _serFeatures;
+        for (SerializationFeature f : features) {
+            newSerFeatures &= ~f.getMask();
+        }
+        return (newSerFeatures == _serFeatures) ? this
+                : new SerializationConfig(this, _mapperFeatures, newSerFeatures);
+    }
+    
+    public SerializationConfig withFilters(FilterProvider filterProvider) {
+        return (filterProvider == _filterProvider) ? this : new SerializationConfig(this, filterProvider);
+    }
+
+    public SerializationConfig withSerializationInclusion(JsonInclude.Include incl) {
+        return (_serializationInclusion == incl) ? this:  new SerializationConfig(this, incl);
+    }
+    
+    /*
+    /**********************************************************
+    /* MapperConfig implementation/overrides
+    /**********************************************************
+     */
+    
+    @Override
+    public boolean useRootWrapping()
+    {
+        if (_rootName != null) { // empty String disables wrapping; non-empty enables
+            return (_rootName.length() > 0);
+        }
+        return isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+    }
+    
+    @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 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);
+    }
+    
+    @Override
+    public VisibilityChecker<?> getDefaultVisibilityChecker()
+    {
+        VisibilityChecker<?> vchecker = super.getDefaultVisibilityChecker();
+        if (!isEnabled(MapperFeature.AUTO_DETECT_GETTERS)) {
+            vchecker = vchecker.withGetterVisibility(Visibility.NONE);
+        }
+        // then global overrides (disabling)
+        if (!isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS)) {
+            vchecker = vchecker.withIsGetterVisibility(Visibility.NONE);
+        }
+        if (!isEnabled(MapperFeature.AUTO_DETECT_FIELDS)) {
+            vchecker = vchecker.withFieldVisibility(Visibility.NONE);
+        }
+        return vchecker;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration: other
+    /**********************************************************
+     */
+
+    public final boolean isEnabled(SerializationFeature f) {
+        return (_serFeatures & f.getMask()) != 0;
+    }
+    
+    public final int getSerializationFeatures() {
+        return _serFeatures;
+    }
+    
+    public JsonInclude.Include getSerializationInclusion()
+    {
+        if (_serializationInclusion != null) {
+            return _serializationInclusion;
+        }
+        return JsonInclude.Include.ALWAYS;
+    }
+    
+    /**
+     * Method for getting provider used for locating filters given
+     * id (which is usually provided with filter annotations).
+     * Will be null if no provided was set for {@link ObjectWriter}
+     * (or if serialization directly called from {@link ObjectMapper})
+     */
+    public FilterProvider getFilterProvider() {
+        return _filterProvider;
+    }
+
+    /*
+    /**********************************************************
+    /* Introspection methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that will introspect full bean properties for the purpose
+     * of building a bean serializer
+     */
+    @SuppressWarnings("unchecked")
+    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
new file mode 100644
index 0000000..87c228c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializationFeature.java
@@ -0,0 +1,335 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.databind.cfg.ConfigFeature;
+
+/**
+ * Enumeration that defines simple on/off features that affect
+ * the way Java objects are serialized.
+ *<p>
+ * Note that features can be set both through
+ * {@link ObjectMapper} (as sort of defaults) and through
+ * {@link ObjectWriter}.
+ * In first case these defaults must follow "config-then-use" patterns
+ * (i.e. defined once, not changed afterwards); all per-call
+ * changes must be done using {@link ObjectWriter}.
+ */
+public enum SerializationFeature implements ConfigFeature
+{
+    /*
+    /******************************************************
+    /* Generic output features
+    /******************************************************
+     */
+    
+    /**
+     * Feature that can be enabled to make root value (usually JSON
+     * Object but can be any type) wrapped within a single property
+     * JSON object, where key as the "root name", as determined by
+     * annotation introspector (esp. for JAXB that uses
+     * <code>@XmlRootElement.name</code>) or fallback (non-qualified
+     * class name).
+     * Feature is mostly intended for JAXB compatibility.
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRAP_ROOT_VALUE(false),
+
+    /**
+     * Feature that allows enabling (or disabling) indentation
+     * for the underlying generator, using the default pretty
+     * printer (see
+     * {@link com.fasterxml.jackson.core.JsonGenerator#useDefaultPrettyPrinter}
+     * for details).
+     *<p>
+     * Note that this only affects cases where
+     * {@link com.fasterxml.jackson.core.JsonGenerator}
+     * is constructed implicitly by ObjectMapper: if explicit
+     * generator is passed, its configuration is not changed.
+     *<p>
+     * Also note that if you want to configure details of indentation,
+     * you need to directly configure the generator: there is a
+     * method to use any <code>PrettyPrinter</code> instance.
+     * This feature will only allow using the default implementation.
+     *<p>
+     * Feature is enabled by default.
+     */
+    INDENT_OUTPUT(false),
+    
+    /*
+    /******************************************************
+    /* Error handling features
+    /******************************************************
+     */
+    
+    /**
+     * Feature that determines what happens when no accessors are
+     * found for a type (and there are no annotations to indicate
+     * it is meant to be serialized). If enabled (default), an
+     * exception is thrown to indicate these as non-serializable
+     * types; if disabled, they are serialized as empty Objects,
+     * i.e. without any properties.
+     *<p>
+     * Note that empty types that this feature has only effect on
+     * those "empty" beans that do not have any recognized annotations
+     * (like <code>@JsonSerialize</code>): ones that do have annotations
+     * do not result in an exception being thrown.
+     *<p>
+     * Feature is enabled by default.
+     */
+    FAIL_ON_EMPTY_BEANS(true),
+
+    /**
+     * Feature that determines whether Jackson code should catch
+     * and wrap {@link Exception}s (but never {@link Error}s!)
+     * to add additional information about
+     * location (within input) of problem or not. If enabled,
+     * most exceptions will be caught and re-thrown (exception
+     * specifically being that {@link java.io.IOException}s may be passed
+     * as is, since they are declared as throwable); this can be
+     * convenient both in that all exceptions will be checked and
+     * declared, and so there is more contextual information.
+     * However, sometimes calling application may just want "raw"
+     * unchecked exceptions passed as is.
+     *<p>
+     *<p>
+     * Feature is enabled by default.
+     */
+    WRAP_EXCEPTIONS(true),
+
+    /*
+    /******************************************************
+    /* Output life cycle features
+    /******************************************************
+     */
+    
+     /**
+      * Feature that determines whether <code>close</code> method of
+      * serialized <b>root level</b> objects (ones for which <code>ObjectMapper</code>'s
+      * writeValue() (or equivalent) method is called)
+      * that implement {@link java.io.Closeable} 
+      * is called after serialization or not. If enabled, <b>close()</b> will
+      * be called after serialization completes (whether succesfully, or
+      * due to an error manifested by an exception being thrown). You can
+      * think of this as sort of "finally" processing.
+      *<p>
+      * NOTE: only affects behavior with <b>root</b> objects, and not other
+      * objects reachable from the root object. Put another way, only one
+      * call will be made for each 'writeValue' call.
+     *<p>
+     * Feature is disabled by default.
+      */
+    CLOSE_CLOSEABLE(false),
+
+    /**
+     * Feature that determines whether <code>JsonGenerator.flush()</code> is
+     * called after <code>writeValue()</code> method <b>that takes JsonGenerator
+     * as an argument</b> completes (i.e. does NOT affect methods
+     * that use other destinations); same for methods in {@link ObjectWriter}.
+     * This usually makes sense; but there are cases where flushing
+     * should not be forced: for example when underlying stream is
+     * compressing and flush() causes compression state to be flushed
+     * (which occurs with some compression codecs).
+     *<p>
+     * Feature is enabled by default.
+     */
+    FLUSH_AFTER_WRITE_VALUE(true),
+     
+    /*
+    /******************************************************
+    /* Datatype-specific serialization configuration
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether {@link java.util.Date} values
+     * (and Date-based things like {@link java.util.Calendar}s) are to be
+     * serialized as numeric timestamps (true; the default),
+     * or as something else (usually textual representation).
+     * If textual representation is used, the actual format is
+     * one returned by a call to
+     * {@link com.fasterxml.jackson.databind.SerializationConfig#getDateFormat}.
+     *<p>
+     * Note: whether this feature affects handling of other date-related
+     * types depend on handlers of those types, although ideally they
+     * should use this feature
+     *<p>
+     * Note: whether {@link java.util.Map} keys are serialized as Strings
+     * or not is controlled using {@link #WRITE_DATE_KEYS_AS_TIMESTAMPS}.
+     *<p>
+     * Feature is enabled by default.
+     */
+    WRITE_DATES_AS_TIMESTAMPS(true),
+
+    /**
+     * Feature that determines whether {@link java.util.Date}s
+     * (and sub-types) used as {@link java.util.Map} keys are serialized
+     * as timestamps or not (if not, will be serialized as textual
+     * values).
+     *<p>
+     * Default value is 'false', meaning that Date-valued Map keys are serialized
+     * as textual (ISO-8601) values.
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRITE_DATE_KEYS_AS_TIMESTAMPS(false),
+
+    /**
+     * Feature that determines how type <code>char[]</code> is serialized:
+     * when enabled, will be serialized as an explict JSON array (with
+     * single-character Strings as values); when disabled, defaults to
+     * serializing them as Strings (which is more compact).
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),
+
+    /**
+     * Feature that determines standard serialization mechanism used for
+     * Enum values: if enabled, return value of <code>Enum.toString()</code>
+     * is used; if disabled, return value of <code>Enum.name()</code> is used.
+     *<p>
+     * Note: this feature should usually have same value
+     * as {@link DeserializationFeature#READ_ENUMS_USING_TO_STRING}.
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRITE_ENUMS_USING_TO_STRING(false),
+
+    /**
+     * Feature that determines whethere Java Enum values are serialized
+     * as numbers (true), or textual values (false). If textual values are
+     * used, other settings are also considered.
+     * If this feature is enabled,
+     *  return value of <code>Enum.ordinal()</code>
+     * (an integer) will be used as the serialization.
+     *<p>
+     * Note that this feature has precedence over {@link #WRITE_ENUMS_USING_TO_STRING},
+     * which is only considered if this feature is set to false.
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRITE_ENUMS_USING_INDEX(false),
+    
+    /**
+     * Feature that determines whether Map entries with null values are
+     * to be serialized (true) or not (false).
+     *<p>
+     * For further details, check out [JACKSON-314]
+     *<p>
+     * Feature is enabled by default.
+     */
+    WRITE_NULL_MAP_VALUES(true),
+
+    /**
+     * Feature that determines whether Container properties (POJO properties
+     * with declared value of Collection or array; i.e. things that produce JSON
+     * arrays) that are empty (have no elements)
+     * will be serialized as empty JSON arrays (true), or suppressed from output (false).
+     *<p>
+     * Note that this does not change behavior of {@link java.util.Map}s, or
+     * "Collection-like" types.
+     *<p>
+     * Feature is enabled by default.
+     */
+    WRITE_EMPTY_JSON_ARRAYS(true),
+    
+    /**
+     * Feature added for interoperability, to work with oddities of
+     * so-called "BadgerFish" convention.
+     * Feature determines handling of single element {@link java.util.Collection}s
+     * and arrays: if enabled, {@link java.util.Collection}s and arrays that contain exactly
+     * one element will be serialized as if that element itself was serialized.
+     *<p>
+     * When enabled, a POJO with array that normally looks like this:
+     *<pre>
+     *  { "arrayProperty" : [ 1 ] }
+     *</pre>
+     * will instead be serialized as
+     *<pre>
+     *  { "arrayProperty" : 1 }
+     *</pre>
+     *<p>
+     * Note that this feature is counterpart to {@link DeserializationFeature#ACCEPT_SINGLE_VALUE_AS_ARRAY}
+     * (that is, usually both are enabled, or neither is).
+     *<p>
+     * Feature is disabled by default, so that no special handling is done.
+     */
+    WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false),
+
+    /**
+     * Feature that determines whether {@link java.math.BigDecimal} entries are
+     * serialized using {@link java.math.BigDecimal#toPlainString()} to prevent
+     * values to be written using scientific notation.
+     *<p>
+     * NOTE: since this feature typically requires use of
+     * {@link com.fasterxml.jackson.core.JsonGenerator#writeNumber(String)}
+     * ot may cause compatibility problems since not all {@link com.fasterxml.jackson.core.JsonGenerator}
+     * implementations support such mode of output: usually only text-based formats
+     * support it.
+     *<p>
+     * Feature is disabled by default.
+     */
+    WRITE_BIGDECIMAL_AS_PLAIN(false),
+    
+    /**
+     * Feature that controls whether numeric timestamp values are
+     * to be written using nanosecond timestamps (enabled) or not (disabled);
+     * <b>if and only if</b> datatype supports such resolution.
+     * Only newer datatypes (such as Java8 Date/Time) support such resolution --
+     * older types (pre-Java8 <b>java.util.Date</b> etc) and Joda do not --
+     * and this setting <b>has no effect</b> on such types.
+     *<p>
+     * If disabled, standard millisecond timestamps are assumed.
+     * This is the counterpart to {@link SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS}.
+     *<p>
+     * Feature is enabled by default, to support most accurate time values possible.
+     * 
+     * @since 2.2
+     */
+    WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true),    
+    
+    /**
+     * Feature that determines whether {@link java.util.Map} entries are first
+     * sorted by key before serialization or not: if enabled, additional sorting
+     * step is performed if necessary (not necessary for {@link java.util.SortedMap}s),
+     * if disabled, no additional sorting is needed.
+     *<p>
+     * Feature is disabled by default.
+     */
+    ORDER_MAP_ENTRIES_BY_KEYS(false),
+
+    /*
+    /******************************************************
+    /* Other
+    /******************************************************
+     */
+
+    /**
+     * Feature that determines whether {@link ObjectWriter} should
+     * try to eagerly fetch necessary {@link JsonSerializer} when
+     * possible. This improves performance in cases where similarly
+     * configured {@link ObjectWriter} instance is used multiple
+     * times; and should not significantly affect single-use cases.
+     *<p>
+     * Note that there should not be any need to normally disable this
+     * feature: only consider that if there are actual perceived problems.
+     *<p>
+     * Feature is enabled by default.
+     * 
+     * @since 2.1
+     */
+    EAGER_SERIALIZER_FETCH(true)    
+    ;
+
+    private final boolean _defaultState;
+    
+    private SerializationFeature(boolean defaultState) {
+        _defaultState = defaultState;
+    }
+
+    @Override
+    public boolean enabledByDefault() { return _defaultState; }
+
+    @Override
+    public int getMask() { return (1 << ordinal()); }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
new file mode 100644
index 0000000..f1002a7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/SerializerProvider.java
@@ -0,0 +1,931 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.ser.impl.*;
+import com.fasterxml.jackson.databind.ser.std.NullSerializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.RootNameLookup;
+
+/**
+ * Class that defines API used by {@link ObjectMapper} and
+ * {@link JsonSerializer}s to obtain serializers capable of serializing
+ * instances of specific types; as well as the default implementation
+ * of the functionality.
+ *<p>
+ * Provider handles caching aspects of serializer handling; all construction
+ * details are delegated to {@link SerializerFactory} instance.
+ *<p>
+ * Object life-cycle is such that an initial instance ("blueprint") is created
+ * and referenced by {@link ObjectMapper} and {@link ObjectWriter} intances;
+ * but for actual usage, a configured instance is created by using
+ * a create method in sub-class
+ * {@link com.fasterxml.jackson.databind.ser.DefaultSerializerProvider}.
+ * Only this instance can be used for actual serialization calls; blueprint
+ * object is only to be used for creating instances.
+ */
+public abstract class SerializerProvider
+    extends DatabindContext
+{
+    protected final static JavaType TYPE_OBJECT = TypeFactory.defaultInstance().uncheckedSimpleType(Object.class);
+
+    /**
+     * Setting for determining whether mappings for "unknown classes" should be
+     * cached for faster resolution. Usually this isn't needed, but maybe it
+     * is in some cases?
+     */
+    protected final static boolean CACHE_UNKNOWN_MAPPINGS = false;
+
+    public final static JsonSerializer<Object> DEFAULT_NULL_KEY_SERIALIZER =
+        new FailingSerializer("Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)");
+
+    public final static JsonSerializer<Object> DEFAULT_UNKNOWN_SERIALIZER = new UnknownSerializer();
+
+    /*
+    /**********************************************************
+    /* Configuration, general
+    /**********************************************************
+     */
+    
+    /**
+     * Serialization configuration to use for serialization processing.
+     */
+    final protected SerializationConfig _config;
+
+    /**
+     * View used for currently active serialization, if any.
+     */
+    final protected Class<?> _serializationView;
+    
+    /*
+    /**********************************************************
+    /* Configuration, factories
+    /**********************************************************
+     */
+
+    /**
+     * Factory used for constructing actual serializer instances.
+     */
+    final protected SerializerFactory _serializerFactory;
+
+    /*
+    /**********************************************************
+    /* Helper objects for caching
+    /**********************************************************
+     */
+    
+    /**
+     * Cache for doing type-to-value-serializer lookups.
+     */
+    final protected SerializerCache _serializerCache;
+
+    /**
+     * Helper object for keeping track of introspected root names
+     */
+    final protected RootNameLookup _rootNames;
+    
+    /*
+    /**********************************************************
+    /* Configuration, specialized serializers
+    /**********************************************************
+     */
+
+    /**
+     * Serializer that gets called for values of types for which no
+     * serializers can be constructed.
+     *<p>
+     * The default serializer will simply thrown an exception.
+     */
+    protected JsonSerializer<Object> _unknownTypeSerializer = DEFAULT_UNKNOWN_SERIALIZER;
+
+    /**
+     * Serializer used to output non-null keys of Maps (which will get
+     * output as JSON Objects), if not null; if null, us the standard
+     * default key serializer.
+     */
+    protected JsonSerializer<Object> _keySerializer;
+
+    /**
+     * Serializer used to output a null value. Default implementation
+     * writes nulls using {@link JsonGenerator#writeNull}.
+     */
+    protected JsonSerializer<Object> _nullValueSerializer = NullSerializer.instance;
+
+    /**
+     * Serializer used to (try to) output a null key, due to an entry of
+     * {@link java.util.Map} having null key.
+     * The default implementation will throw an exception if this happens;
+     * alternative implementation (like one that would write an Empty String)
+     * can be defined.
+     */
+    protected JsonSerializer<Object> _nullKeySerializer = DEFAULT_NULL_KEY_SERIALIZER;
+
+    /*
+    /**********************************************************
+    /* State, for non-blueprint instances: generic
+    /**********************************************************
+     */
+    
+    /**
+     * For fast lookups, we will have a local non-shared read-only
+     * map that contains serializers previously fetched.
+     */
+    protected final ReadOnlyClassToSerializerMap _knownSerializers;
+
+    /**
+     * Lazily acquired and instantiated formatter object: initialized
+     * first time it is needed, reused afterwards. Used via instances
+     * (not blueprints), so that access need not be thread-safe.
+     */
+    protected DateFormat _dateFormat;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * Constructor for creating master (or "blue-print") provider object,
+     * which is only used as the template for constructing per-binding
+     * instances.
+     */
+    public SerializerProvider()
+    {
+        _config = null;
+        _serializerFactory = null;
+        _serializerCache = new SerializerCache();
+        // Blueprints doesn't have access to any serializers...
+        _knownSerializers = null;
+        _rootNames = new RootNameLookup();
+
+        _serializationView = null;
+    }
+
+    /**
+     * "Copy-constructor", used by sub-classes.
+     *
+     * @param src Blueprint object used as the baseline for this instance
+     */
+    protected SerializerProvider(SerializerProvider src,
+            SerializationConfig config, SerializerFactory f)
+    {
+        if (config == null) {
+            throw new NullPointerException();
+        }
+        _serializerFactory = f;
+        _config = config;
+
+        _serializerCache = src._serializerCache;
+        _unknownTypeSerializer = src._unknownTypeSerializer;
+        _keySerializer = src._keySerializer;
+        _nullValueSerializer = src._nullValueSerializer;
+        _nullKeySerializer = src._nullKeySerializer;
+        _rootNames = src._rootNames;
+
+        /* Non-blueprint instances do have a read-only map; one that doesn't
+         * need synchronization for lookups.
+         */
+        _knownSerializers = _serializerCache.getReadOnlyLookupMap();
+
+        _serializationView = config.getActiveView();
+    }
+    
+    /*
+    /**********************************************************
+    /* Methods for configuring default settings
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to specify serializer that will be
+     * used to write JSON property names matching null keys for Java
+     * Maps (which will throw an exception if try write such property
+     * name)
+     */
+    public void setDefaultKeySerializer(JsonSerializer<Object> ks)
+    {
+        if (ks == null) {
+            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+        }
+        _keySerializer = ks;
+    }
+
+    /**
+     * Method that can be used to specify serializer that will be
+     * used to write JSON values matching Java null values
+     * instead of default one (which simply writes JSON null)
+     */
+    public void setNullValueSerializer(JsonSerializer<Object> nvs)
+    {
+        if (nvs == null) {
+            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+        }
+        _nullValueSerializer = nvs;
+    }
+
+    /**
+     * Method that can be used to specify serializer to use for serializing
+     * all non-null JSON property names, unless more specific key serializer
+     * is found (i.e. if not custom key serializer has been registered for
+     * Java type).
+     *<p>
+     * Note that key serializer registration are different from value serializer
+     * registrations.
+     */
+    public void setNullKeySerializer(JsonSerializer<Object> nks)
+    {
+        if (nks == null) {
+            throw new IllegalArgumentException("Can not pass null JsonSerializer");
+        }
+        _nullKeySerializer = nks;
+    }
+        
+    /*
+    /**********************************************************
+    /* DatabindContext implementation
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing configuration for the serialization processing.
+     */
+    @Override
+    public final SerializationConfig getConfig() { return _config; }
+
+    @Override
+    public final AnnotationIntrospector getAnnotationIntrospector() {
+        return _config.getAnnotationIntrospector();
+    }
+
+    @Override
+    public final TypeFactory getTypeFactory() {
+        return _config.getTypeFactory();
+    }
+    
+    @Override
+    public final Class<?> getActiveView() { return _serializationView; }
+    
+    /**
+     * @deprecated Since 2.2, use {@link #getActiveView} instead.
+     */
+    @Deprecated
+    public final Class<?> getSerializationView() { return _serializationView; }
+    
+    /*
+    /**********************************************************
+    /* Access to general configuration
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for checking whether specified serialization
+     * feature is enabled or not.
+     * Shortcut for:
+     *<pre>
+     *  getConfig().isEnabled(feature);
+     *</pre>
+     */
+    public final boolean isEnabled(SerializationFeature feature) {
+        return _config.isEnabled(feature);
+    }
+
+    /**
+     * Convenience method for accessing provider to find serialization filters used,
+     * equivalent to calling:
+     *<pre>
+     *   getConfig().getFilterProvider();
+     *</pre>
+     */
+    public final FilterProvider getFilterProvider() {
+        return _config.getFilterProvider();
+    }
+
+    /**
+     * Method for accessing default Locale to use: convenience method for
+     *<pre>
+     *   getConfig().getLocale();
+     *</pre>
+     */
+    public Locale getLocale() {
+        return _config.getLocale();
+    }
+
+    /**
+     * Method for accessing default TimeZone to use: convenience method for
+     *<pre>
+     *   getConfig().getTimeZone();
+     *</pre>
+     */
+    public TimeZone getTimeZone() {
+        return _config.getTimeZone();
+    }
+
+    /*
+    /**********************************************************
+    /* Access to Object Id aspects
+    /**********************************************************
+     */
+
+    /**
+     * Method called to find the Object Id for given POJO, if one
+     * has been generated. Will always return a non-null Object;
+     * contents vary depending on whether an Object Id already
+     * exists or not.
+     */
+    public abstract WritableObjectId findObjectId(Object forPojo,
+            ObjectIdGenerator<?> generatorType);
+    
+    /*
+    /**********************************************************
+    /* General serializer locating functionality
+    /**********************************************************
+     */
+
+    /**
+     * Method called to get hold of a serializer for a value of given type;
+     * or if no such serializer can be found, a default handler (which
+     * may do a best-effort generic serialization or just simply
+     * throw an exception when invoked).
+     *<p>
+     * Note: this method is only called for non-null values; not for keys
+     * or null values. For these, check out other accessor methods.
+     *<p>
+     * Note that starting with version 1.5, serializers should also be type-aware
+     * if they handle polymorphic types. That means that it may be necessary
+     * to also use a {@link TypeSerializer} based on declared (static) type
+     * being serializer (whereas actual data may be serialized using dynamic
+     * type)
+     *
+     * @throws JsonMappingException if there are fatal problems with
+     *   accessing suitable serializer; including that of not
+     *   finding any serializer
+     */
+    public JsonSerializer<Object> findValueSerializer(Class<?> valueType,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        // Fast lookup from local lookup thingy works?
+        JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
+        if (ser == null) {
+            // If not, maybe shared map already has it?
+            ser = _serializerCache.untypedValueSerializer(valueType);
+            if (ser == null) {
+                // ... possibly as fully typed?
+                ser = _serializerCache.untypedValueSerializer(_config.constructType(valueType));
+                if (ser == null) {
+                    // If neither, must create
+                    ser = _createAndCacheUntypedSerializer(valueType);
+                    // Not found? Must use the unknown type serializer
+                    /* Couldn't create? Need to return the fallback serializer, which
+                     * most likely will report an error: but one question is whether
+                     * we should cache it?
+                     */
+                    if (ser == null) {
+                        ser = getUnknownTypeSerializer(valueType);
+                        // Should this be added to lookups?
+                        if (CACHE_UNKNOWN_MAPPINGS) {
+                            _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
+                        }
+                        return ser;
+                    }
+                }
+            }
+        }
+        // at this point, resolution has occured, but not contextualization
+        return _handleContextual(ser, property);
+    }
+
+    /**
+     * Similar to {@link #findValueSerializer(Class,BeanProperty)}, but takes
+     * full generics-aware type instead of raw class.
+     * This is necessary for accurate handling of external type information,
+     * to handle polymorphic types.
+     * 
+     * @param property When creating secondary serializers, property for which
+     *   serializer is needed: annotations of the property (or bean that contains it)
+     *   may be checked to create contextual serializers.
+     */
+    public JsonSerializer<Object> findValueSerializer(JavaType valueType, BeanProperty property)
+        throws JsonMappingException
+    {
+        // Fast lookup from local lookup thingy works?
+        JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(valueType);
+        if (ser == null) {
+            // If not, maybe shared map already has it?
+            ser = _serializerCache.untypedValueSerializer(valueType);
+            if (ser == null) {
+                // If neither, must create
+                ser = _createAndCacheUntypedSerializer(valueType);
+                // Not found? Must use the unknown type serializer
+                /* Couldn't create? Need to return the fallback serializer, which
+                 * most likely will report an error: but one question is whether
+                 * we should cache it?
+                 */
+                if (ser == null) {
+                    ser = getUnknownTypeSerializer(valueType.getRawClass());
+                    // Should this be added to lookups?
+                    if (CACHE_UNKNOWN_MAPPINGS) {
+                        _serializerCache.addAndResolveNonTypedSerializer(valueType, ser, this);
+                    }
+                    return ser;
+                }
+            }
+        }
+        return _handleContextual(ser, property);
+    }
+    
+    /**
+     * Method called to locate regular serializer, matching type serializer,
+     * and if both found, wrap them in a serializer that calls both in correct
+     * sequence. This method is currently only used for root-level serializer
+     * handling to allow for simpler caching. A call can always be replaced
+     * by equivalent calls to access serializer and type serializer separately.
+     * 
+     * @param valueType Type for purpose of locating a serializer; usually dynamic
+     *   runtime type, but can also be static declared type, depending on configuration
+     * @param cache Whether resulting value serializer should be cached or not; this is just
+     *    a hint
+     * @param property When creating secondary serializers, property for which
+     *   serializer is needed: annotations of the property (or bean that contains it)
+     *   may be checked to create contextual serializers.
+     */
+    public JsonSerializer<Object> findTypedValueSerializer(Class<?> valueType,
+            boolean cache, BeanProperty property)
+        throws JsonMappingException
+    {
+        // Two-phase lookups; local non-shared cache, then shared:
+        JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
+        if (ser != null) {
+            return ser;
+        }
+        // If not, maybe shared map already has it?
+        ser = _serializerCache.typedValueSerializer(valueType);
+        if (ser != null) {
+            return ser;
+        }
+
+        // Well, let's just compose from pieces:
+        ser = findValueSerializer(valueType, property);
+        TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config,
+                _config.constructType(valueType));
+        if (typeSer != null) {
+            typeSer = typeSer.forProperty(property);
+            ser = new TypeWrappedSerializer(typeSer, ser);
+        }
+        if (cache) {
+            _serializerCache.addTypedSerializer(valueType, ser);
+        }
+        return ser;
+    }
+
+    /**
+     * Method called to locate regular serializer, matching type serializer,
+     * and if both found, wrap them in a serializer that calls both in correct
+     * sequence. This method is currently only used for root-level serializer
+     * handling to allow for simpler caching. A call can always be replaced
+     * by equivalent calls to access serializer and type serializer separately.
+     * 
+     * @param valueType Declared type of value being serialized (which may not
+     *    be actual runtime type); used for finding both value serializer and
+     *    type serializer to use for adding polymorphic type (if any)
+     * @param cache Whether resulting value serializer should be cached or not; this is just
+     *    a hint 
+     * @param property When creating secondary serializers, property for which
+     *   serializer is needed: annotations of the property (or bean that contains it)
+     *   may be checked to create contextual serializers.
+     */
+    public JsonSerializer<Object> findTypedValueSerializer(JavaType valueType, boolean cache,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        // Two-phase lookups; local non-shared cache, then shared:
+        JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
+        if (ser != null) {
+            return ser;
+        }
+        // If not, maybe shared map already has it?
+        ser = _serializerCache.typedValueSerializer(valueType);
+        if (ser != null) {
+            return ser;
+        }
+
+        // Well, let's just compose from pieces:
+        ser = findValueSerializer(valueType, property);
+        TypeSerializer typeSer = _serializerFactory.createTypeSerializer(_config, valueType);
+        if (typeSer != null) {
+            typeSer = typeSer.forProperty(property);
+            ser = new TypeWrappedSerializer(typeSer, ser);
+        }
+        if (cache) {
+            _serializerCache.addTypedSerializer(valueType, ser);
+        }
+        return ser;
+    }
+
+    /**
+     * Method called to get the serializer to use for serializing
+     * non-null Map keys. Separation from regular
+     * {@link #findValueSerializer} method is because actual write
+     * method must be different (@link JsonGenerator#writeFieldName};
+     * but also since behavior for some key types may differ.
+     *<p>
+     * Note that the serializer itself can be called with instances
+     * of any Java object, but not nulls.
+     */
+    public JsonSerializer<Object> findKeySerializer(JavaType keyType,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        JsonSerializer<Object> ser = _serializerFactory.createKeySerializer(_config, keyType, _keySerializer);
+        // 25-Feb-2011, tatu: As per [JACKSON-519], need to ensure contextuality works here, too
+        return _handleContextualResolvable(ser, property);
+    }
+    
+    /*
+    /********************************************************
+    /* Accessors for specialized serializers
+    /********************************************************
+     */
+
+    /**
+     * @since 2.0
+     */
+    public JsonSerializer<Object> getDefaultNullKeySerializer() {
+        return _nullKeySerializer;
+    }
+
+    /**
+     * @since 2.0
+     */
+    public JsonSerializer<Object> getDefaultNullValueSerializer() {
+        return _nullValueSerializer;
+    }
+    
+    /**
+     * Method called to get the serializer to use for serializing
+     * Map keys that are nulls: this is needed since JSON does not allow
+     * any non-String value as key, including null.
+     *<p>
+     * Typically, returned serializer
+     * will either throw an exception, or use an empty String; but
+     * other behaviors are possible.
+     */
+    /**
+     * 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
+     * determined.
+     * 
+     * @since 2.0
+     */
+    public JsonSerializer<Object> findNullKeySerializer(JavaType serializationType,
+            BeanProperty property)
+        throws JsonMappingException {
+        return getDefaultNullKeySerializer();
+    }
+
+    /**
+     * Method called to get the serializer to use for serializing null
+     * property values.
+     *<p>
+     * Default implementation simply calls {@link #getDefaultNullValueSerializer()};
+     * can be overridden to add custom null serialization for properties
+     * of certain type or name.
+     * 
+     * @since 2.0
+     */
+    public JsonSerializer<Object> findNullValueSerializer(BeanProperty property)
+        throws JsonMappingException {
+        return getDefaultNullValueSerializer();
+    }
+
+    /**
+     * Method called to get the serializer to use if provider
+     * can not determine an actual type-specific serializer
+     * to use; typically when none of {@link SerializerFactory}
+     * instances are able to construct a serializer.
+     *<p>
+     * Typically, returned serializer will throw an exception,
+     * although alternatively {@link com.fasterxml.jackson.databind.ser.std.ToStringSerializer}
+     * could be returned as well.
+     *
+     * @param unknownType Type for which no serializer is found
+     */
+    public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) {
+        return _unknownTypeSerializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Methods for creating instances based on annotations
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to construct and configure serializer instance,
+     * either given a {@link Class} to instantiate (with default constructor),
+     * or an uninitialized serializer instance.
+     * Either way, serialize will be properly resolved
+     * (via {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer}) and/or contextualized
+     * (via {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}) as necessary.
+     * 
+     * @param annotated Annotated entity that contained definition
+     * @param serDef Serializer definition: either an instance or class
+     */
+    public abstract JsonSerializer<Object> serializerInstance(Annotated annotated,
+            Object serDef)
+        throws JsonMappingException;
+
+    /*
+    /********************************************************
+    /* Convenience methods
+    /********************************************************
+     */
+
+    /**
+     * Convenience method that will serialize given value (which can be
+     * null) using standard serializer locating functionality. It can
+     * be called for all values including field and Map values, but usually
+     * field values are best handled calling
+     * {@link #defaultSerializeField} instead.
+     */
+    public final void defaultSerializeValue(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        if (value == null) {
+            getDefaultNullValueSerializer().serialize(null, jgen, this);
+        } else {
+            Class<?> cls = value.getClass();
+            findTypedValueSerializer(cls, true, null).serialize(value, jgen, this);
+        }
+    }
+    
+    /**
+     * Convenience method that will serialize given field with specified
+     * value. Value may be null. Serializer is done using the usual
+     * null) using standard serializer locating functionality.
+     */
+    public final void defaultSerializeField(String fieldName, Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeFieldName(fieldName);
+        if (value == null) {
+            /* Note: can't easily check for suppression at this point
+             * any more; caller must check it.
+             */
+            getDefaultNullValueSerializer().serialize(null, jgen, this);
+        } else {
+            Class<?> cls = value.getClass();
+            findTypedValueSerializer(cls, true, null).serialize(value, jgen, this);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Convenience methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that will handle serialization of Date(-like) values, using
+     * {@link SerializationConfig} settings to determine expected serialization
+     * behavior.
+     * Note: date here means "full" date, that is, date AND time, as per
+     * Java convention (and not date-only values like in SQL)
+     */
+    public final void defaultSerializeDateValue(long timestamp, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-87]: Support both numeric timestamps and textual
+        if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) {
+            jgen.writeNumber(timestamp);
+        } else {
+            jgen.writeString(_dateFormat().format(new Date(timestamp)));
+        }
+    }
+
+    /**
+     * Method that will handle serialization of Date(-like) values, using
+     * {@link SerializationConfig} settings to determine expected serialization
+     * behavior.
+     * Note: date here means "full" date, that is, date AND time, as per
+     * Java convention (and not date-only values like in SQL)
+     */
+    public final void defaultSerializeDateValue(Date date, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-87]: Support both numeric timestamps and textual
+        if (isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)) {
+            jgen.writeNumber(date.getTime());
+        } else {
+            jgen.writeString(_dateFormat().format(date));
+        }
+    }
+
+    /**
+     * Method that will handle serialization of Dates used as {@link java.util.Map} keys,
+     * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS}
+     * value (and if using textual representation, configured date format)
+     */
+    public void defaultSerializeDateKey(long timestamp, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) {
+            jgen.writeFieldName(String.valueOf(timestamp));
+        } else {
+            jgen.writeFieldName(_dateFormat().format(new Date(timestamp)));
+        }
+    }
+
+    /**
+     * Method that will handle serialization of Dates used as {@link java.util.Map} keys,
+     * based on {@link SerializationFeature#WRITE_DATE_KEYS_AS_TIMESTAMPS}
+     * value (and if using textual representation, configured date format)
+     */
+    public void defaultSerializeDateKey(Date date, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        if (isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS)) {
+            jgen.writeFieldName(String.valueOf(date.getTime()));
+        } else {
+            jgen.writeFieldName(_dateFormat().format(date));
+        }
+    }
+    
+    public final void defaultSerializeNull(JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        getDefaultNullValueSerializer().serialize(null, jgen, this);
+    }
+
+    /*
+    /********************************************************
+    /* Helper methods
+    /********************************************************
+     */
+    
+    protected void _reportIncompatibleRootType(Object value, JavaType rootType)
+        throws IOException, JsonProcessingException
+    {
+        /* 07-Jan-2010, tatu: As per [JACKSON-456] better handle distinction between wrapper types,
+         *    primitives
+         */
+        if (rootType.isPrimitive()) {
+            Class<?> wrapperType = ClassUtil.wrapperType(rootType.getRawClass());
+            // If it's just difference between wrapper, primitive, let it slide
+            if (wrapperType.isAssignableFrom(value.getClass())) {
+                return;
+            }
+        }
+        throw new JsonMappingException("Incompatible types: declared root type ("+rootType+") vs "
+                +value.getClass().getName());
+    }
+    
+    /**
+     * 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.
+     *
+     * @return Serializer if one can be found, null if not.
+     */
+    protected JsonSerializer<Object> _findExplicitUntypedSerializer(Class<?> runtimeType)
+		throws JsonMappingException
+    {        
+        // Fast lookup from local lookup thingy works?
+        JsonSerializer<Object> ser = _knownSerializers.untypedValueSerializer(runtimeType);
+        if (ser != null) {
+            return ser;
+        }
+        // If not, maybe shared map already has it?
+        ser = _serializerCache.untypedValueSerializer(runtimeType);
+        if (ser != null) {
+            return ser;
+        }
+        return _createAndCacheUntypedSerializer(runtimeType);
+    }
+
+    /*
+    /**********************************************************
+    /* Low-level methods for actually constructing and initializing
+    /* serializers
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will try to construct a value serializer; and if
+     * one is successfully created, cache it for reuse.
+     */
+    protected JsonSerializer<Object> _createAndCacheUntypedSerializer(Class<?> type)
+        throws JsonMappingException
+    {        
+        JsonSerializer<Object> ser;
+        try {
+            ser = _createUntypedSerializer(_config.constructType(type));
+        } catch (IllegalArgumentException iae) {
+            /* We better only expose checked exceptions, since those
+             * are what caller is expected to handle
+             */
+            throw new JsonMappingException(iae.getMessage(), null, iae);
+        }
+
+        if (ser != null) {
+            _serializerCache.addAndResolveNonTypedSerializer(type, ser, this);
+        }
+        return ser;
+    }
+
+    protected JsonSerializer<Object> _createAndCacheUntypedSerializer(JavaType type)
+        throws JsonMappingException
+    {        
+        JsonSerializer<Object> ser;
+        try {
+            ser = _createUntypedSerializer(type);
+        } catch (IllegalArgumentException iae) {
+            /* We better only expose checked exceptions, since those
+             * are what caller is expected to handle
+             */
+            throw new JsonMappingException(iae.getMessage(), null, iae);
+        }
+    
+        if (ser != null) {
+            _serializerCache.addAndResolveNonTypedSerializer(type, ser, this);
+        }
+        return ser;
+    }
+
+    /**
+     * @since 2.1
+     */
+    protected JsonSerializer<Object> _createUntypedSerializer(JavaType type)
+        throws JsonMappingException
+    {
+        // 17-Feb-2013, tatu: Used to call deprecated method (that passed property)
+        return (JsonSerializer<Object>)_serializerFactory.createSerializer(this, type);
+    }
+
+    /**
+     * Helper method called to resolve and contextualize given
+     * serializer, if and as necessary.
+     */
+    protected JsonSerializer<Object> _handleContextualResolvable(JsonSerializer<?> ser,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        if (ser instanceof ResolvableSerializer) {
+            ((ResolvableSerializer) ser).resolve(this);
+        }
+        return _handleContextual(ser, property);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected JsonSerializer<Object> _handleResolvable(JsonSerializer<?> ser)
+        throws JsonMappingException
+    {
+        if (ser instanceof ResolvableSerializer) {
+            ((ResolvableSerializer) ser).resolve(this);
+        }
+        return (JsonSerializer<Object>) ser;
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected JsonSerializer<Object> _handleContextual(JsonSerializer<?> ser,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(this, property);
+        }
+        return (JsonSerializer<Object>) ser;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected final DateFormat _dateFormat()
+    {
+        if (_dateFormat != null) {
+            return _dateFormat;
+        }
+        /* 24-Feb-2012, tatu: At this point, all timezone configuration
+         *    should have occured, with respect to default dateformat
+         *    and timezone configuration. But we still better clone
+         *    an instance as formatters may be stateful.
+         */
+        DateFormat df = _config.getDateFormat();
+        _dateFormat = df = (DateFormat) df.clone();
+        return df;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java
new file mode 100644
index 0000000..5ed3dbd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JacksonStdImpl.java
@@ -0,0 +1,23 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotation;
+
+/**
+ * Marker interface used to indicate implementation classes
+ * (serializers, deserializers etc) that are standard ones Jackson
+ * uses; not custom ones that application has added. It can be
+ * added in cases where certain optimizations can be made if
+ * default instances are uses; for example when handling conversions
+ * of "natural" JSON types like Strings, booleans and numbers.
+ */
+ at Target({ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at JacksonAnnotation
+public @interface JacksonStdImpl {
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java
new file mode 100644
index 0000000..96a24b2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonDeserialize.java
@@ -0,0 +1,135 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Annotation use for configuring deserialization aspects, by attaching
+ * to "setter" methods or fields, or to value classes.
+ * When annotating value classes, configuration is used for instances
+ * of the value class but can be overridden by more specific annotations
+ * (ones that attach to methods or fields).
+ *<p>
+ * An example annotation would be:
+ *<pre>
+ *  @JsonDeserialize(using=MySerializer.class,
+ *    as=MyHashMap.class,
+ *    keyAs=MyHashKey.class,
+ *    contentAs=MyHashValue.class
+ *  )
+ *</pre>
+ *<p>
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
+ at Retention(RetentionPolicy.RUNTIME)
+ at com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JsonDeserialize
+{
+    // // // Annotations for explicitly specifying deserialize/builder
+
+    /**
+     * Deserializer class to use for deserializing associated value.
+     * Depending on what is annotated,
+     * value is either an instance of annotated class (used globablly
+     * anywhere where class deserializer is needed); or only used for
+     * deserializing property access via a setter method.
+     */
+    public Class<? extends JsonDeserializer<?>> using()
+        default JsonDeserializer.None.class;
+
+    /**
+     * Deserializer class to use for deserializing contents (elements
+     * of a Collection/array, values of Maps) of annotated property.
+     * Can only be used on instances (methods, fields, constructors),
+     * and not value classes themselves.
+     */
+    public Class<? extends JsonDeserializer<?>> contentUsing()
+        default JsonDeserializer.None.class;
+
+    /**
+     * Deserializer class to use for deserializing Map keys
+     * of annotated property.
+     * Can only be used on instances (methods, fields, constructors),
+     * and not value classes themselves.
+     */
+    public Class<? extends KeyDeserializer> keyUsing()
+        default KeyDeserializer.None.class;
+
+    /**
+     * Annotation for specifying if an external Builder class is to
+     * be used for building up deserialized instances of annotated
+     * class. If so, an instance of referenced class is first constructed
+     * (possibly using a Creator method; or if none defined, using default
+     * constructor), and its "with-methods" are used for populating fields;
+     * and finally "build-method" is invoked to complete deserialization.
+     */
+    public Class<?> builder()
+        default NoClass.class;
+
+    // // // Annotations for specifying intermediate Converters (2.2+)
+    
+    /**
+     * Which helper object (if any) is to be used to convert from Jackson-bound
+     * intermediate type (source type of converter) into actual property type
+     * (which must be same as result type of converter). This is often used
+     * for two-step deserialization; Jackson binds data into suitable intermediate
+     * type (like Tree representation), and converter then builds actual property
+     * type.
+     *
+     * @since 2.2
+     */
+    public Class<? extends Converter<?,?>> converter() default Converter.None.class;
+
+    /**
+     * Similar to {@link #converter}, but used for values of structures types
+     * (List, arrays, Maps).
+     *
+     * @since 2.2
+     */
+    public Class<? extends Converter<?,?>> contentConverter() default Converter.None.class;
+    
+    
+    // // // Annotations for explicitly specifying deserialization type
+    // // // (which is used for choosing deserializer, if not explicitly
+    // // // specified
+
+    /**
+     * Concrete type to deserialize values as, instead of type otherwise
+     * declared. Must be a subtype of declared type; otherwise an
+     * exception may be thrown by deserializer.
+     *<p>
+     * Bogus type {@link NoClass} can be used to indicate that declared
+     * type is used as is (i.e. this annotation property has no setting);
+     * this since annotation properties are not allowed to have null value.
+     *<p>
+     * Note: if {@link #using} is also used it has precedence
+     * (since it directly specified
+     * deserializer, whereas this would only be used to locate the
+     * deserializer)
+     * and value of this annotation property is ignored.
+     */
+    public Class<?> as() default NoClass.class;
+
+    /**
+     * Concrete type to deserialize keys of {@link java.util.Map} as,
+     * instead of type otherwise declared.
+     * Must be a subtype of declared type; otherwise an exception may be
+     * thrown by deserializer.
+     */
+    public Class<?> keyAs() default NoClass.class;
+
+    /**
+     * Concrete type to deserialize content (elements
+     * of a Collection/array, values of Maps) values as,
+     * instead of type otherwise declared.
+     * Must be a subtype of declared type; otherwise an exception may be
+     * thrown by deserializer.
+     */
+    public Class<?> contentAs() default NoClass.class;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java
new file mode 100644
index 0000000..c5fb85c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonNaming.java
@@ -0,0 +1,19 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.*;
+
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+
+/**
+ * Annotation that can be used to indicate a {@link PropertyNamingStrategy}
+ * to use for annotated class. Overrides the global (default) strategy.
+ * 
+ * @since 2.1
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JsonNaming
+{
+    public Class<? extends PropertyNamingStrategy> value();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
new file mode 100644
index 0000000..9e8d46f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonPOJOBuilder.java
@@ -0,0 +1,74 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * Annotation used to configure details of a Builder class:
+ * instances of which are used as Builders for deserialized
+ * POJO values, instead of POJOs being instantiated using
+ * constructors or factory methods.
+ * Note that this annotation is NOT used to define what is
+ * the Builder class for a POJO: rather, this is determined
+ * by {@link JsonDeserialize#builder} property of {@link JsonDeserialize}.
+ *<p>
+ * Annotation is typically used if the naming convention
+ * of a Builder class is different from defaults:
+ *<ul>
+ * </ul>
+ * 
+ * @since 2.0
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at 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";
+
+	/**
+	 * 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";
+
+    /*
+    /**********************************************************
+    /* 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();
+	    }
+	}
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java
new file mode 100644
index 0000000..5c3e6fe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonSerialize.java
@@ -0,0 +1,224 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Annotation used for configuring serialization aspects, by attaching
+ * to "getter" methods or fields, or to value classes.
+ * When annotating value classes, configuration is used for instances
+ * of the value class but can be overridden by more specific annotations
+ * (ones that attach to methods or fields).
+ *<p>
+ * An example annotation would be:
+ *<pre>
+ *  @JsonSerialize(using=MySerializer.class,
+ *    as=MySubClass.class,
+ *    typing=JsonSerialize.Typing.STATIC
+ *  )
+ *</pre>
+ * (which would be redundant, since some properties block others:
+ * specifically, 'using' has precedence over 'as', which has precedence
+ * over 'typing' setting)
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
+ at Retention(RetentionPolicy.RUNTIME)
+ at com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JsonSerialize
+{
+    // // // Annotations for explicitly specifying deserializer
+
+    /**
+     * Serializer class to use for
+     * serializing associated value. Depending on what is annotated,
+     * value is either an instance of annotated class (used globablly
+     * anywhere where class serializer is needed); or only used for
+     * serializing property access via a getter method.
+     */
+    public Class<? extends JsonSerializer<?>> using() default JsonSerializer.None.class;
+
+    /**
+     * Serializer class to use for serializing contents (elements
+     * of a Collection/array, values of Maps) of annotated property.
+     * Can only be used on properties (methods, fields, constructors),
+     * and not value classes themselves (as they are typically generic)
+     */
+    public Class<? extends JsonSerializer<?>> contentUsing()
+        default JsonSerializer.None.class;
+
+    /**
+     * Serializer class to use for serializing Map keys
+     * of annotated property.
+     * Can only be used on properties (methods, fields, constructors),
+     * and not value classes themselves.
+     */
+    public Class<? extends JsonSerializer<?>> keyUsing()
+        default JsonSerializer.None.class;
+    
+    // // // Annotations for type handling, explicit declaration
+    // // // (type used for choosing deserializer, if not explicitly
+    // // // specified)
+
+    /**
+     * Supertype (of declared type, which itself is supertype of runtime type)
+     * to use as type when locating serializer to use.
+     *<p>
+     * Bogus type {@link NoClass} can be used to indicate that declared
+     * type is used as is (i.e. this annotation property has no setting);
+     * this since annotation properties are not allowed to have null value.
+     *<p>
+     * Note: if {@link #using} is also used it has precedence
+     * (since it directly specifies
+     * serializer, whereas this would only be used to locate the
+     * serializer)
+     * and value of this annotation property is ignored.
+     */
+    public Class<?> as() default NoClass.class;
+
+    /**
+     * Concrete type to serialize keys of {@link java.util.Map} as,
+     * instead of type otherwise declared.
+     * Must be a supertype of declared type; otherwise an exception may be
+     * thrown by serializer.
+     */
+    public Class<?> keyAs() default NoClass.class;
+
+    /**
+     * Concrete type to serialize content value (elements
+     * of a Collection/array, values of Maps) as,
+     * instead of type otherwise declared.
+     * Must be a supertype of declared type; otherwise an exception may be
+     * thrown by serializer.
+     */
+    public Class<?> contentAs() default NoClass.class;
+    
+    /**
+     * Whether type detection used is dynamic or static: that is,
+     * whether actual runtime type is used (dynamic), or just the
+     * declared type (static).
+     */
+    public Typing typing() default Typing.DYNAMIC;
+
+    // // // Annotations for specifying intermediate Converters (2.2+)
+    
+    /**
+     * 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.
+     *
+     * @since 2.2
+     */
+    public Class<? extends Converter<?,?>> converter() default Converter.None.class;
+
+    /**
+     * Similar to {@link #converter}, but used for values of structures types
+     * (List, arrays, Maps).
+     * Note that this property does NOT have effect when used as Class annotation;
+     * it can only be used as property annotation: this because association between
+     * container and value types is loose and as such converters seldom make sense
+     * for such usage.
+     *
+     * @since 2.2
+     */
+    public Class<? extends Converter<?,?>> contentConverter() default Converter.None.class;
+    
+    // // // Annotation(s) for inclusion criteria
+
+    /**
+     * Which properties of annotated Bean are
+     * to be included in serialization (has no effect on other types
+     * like enums, primitives or collections).
+     * Choices are "all", "properties that have value other than null"
+     * and "properties that have non-default value" (i.e. default value
+     * being property setting for a Bean constructed with default no-arg
+     * constructor, often null).
+     *
+     * @deprecated As of Jackson 2.0, this annotation has been replaced
+     *    by {@link com.fasterxml.jackson.annotation.JsonInclude}
+     */
+    @Deprecated
+    public Inclusion include() default Inclusion.ALWAYS;
+    
+    /*
+    /**********************************************************
+    /* Value enumerations needed
+    /**********************************************************
+     */
+
+    /**
+     * Enumeration used with {@link JsonSerialize#include} property
+     * to define which properties
+     * of Java Beans are to be included in serialization
+     */
+    public enum Inclusion
+    {
+        /**
+         * Value that indicates that properties are to be always included,
+         * independent of value
+         */
+        ALWAYS,
+
+        /**
+         * Value that indicates that only properties with non-null
+         * values are to be included.
+         */
+        NON_NULL,
+
+        /**
+         * Value that indicates that only properties that have values
+         * that differ from default settings (meaning values they have
+         * when Bean is constructed with its no-arguments constructor)
+         * are to be included. Value is generally not useful with
+         * {@link java.util.Map}s, since they have no default values;
+         * and if used, works same as {@link #ALWAYS}.
+         */
+        NON_DEFAULT,
+
+        /**
+         * Value that indicates that only properties that have values
+         * that values that are null or what is considered empty are
+         * not to be included.
+         * Emptiness is defined for following type:
+         *<ul>
+         * <li>For {@link java.util.Collection}s and {@link java.util.Map}s,
+         *    method <code>isEmpty()</code> is called;
+         *   </li>
+         * <li>For Java arrays, empty arrays are ones with length of 0
+         *   </li>
+         * <li>For Java {@link java.lang.String}s, <code>length()</code> is called,
+         *   and return value of 0 indicates empty String (note that <code>String.isEmpty()</code>
+         *   was added in Java 1.6 and as such can not be used by Jackson
+         *   </li>
+         * <ul>
+         *  For other types, non-null values are to be included.
+         */
+        NON_EMPTY
+        ;
+    }
+
+    /**
+     * Enumeration used with {@link JsonSerialize#typing} property
+     * to define whether type detection is based on dynamic runtime
+     * type (DYNAMIC) or declared type (STATIC).
+     */
+    public enum Typing
+    {
+        /**
+         * Value that indicates that the actual dynamic runtime type is to
+         * be used.
+         */
+        DYNAMIC,
+
+        /**
+         * Value that indicates that the static declared type is to
+         * be used.
+         */
+        STATIC
+        ;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java
new file mode 100644
index 0000000..c8d0f4f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeIdResolver.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+import com.fasterxml.jackson.annotation.JacksonAnnotation;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+/**
+ * Annotation that can be used to plug a custom type identifier handler
+ * ({@link TypeIdResolver})
+ * to be used by
+ * {@link com.fasterxml.jackson.databind.jsontype.TypeSerializer}s
+ * and {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}s
+ * for converting between java types and type id included in JSON content.
+ * In simplest cases this can be a simple class with static mapping between
+ * type names and matching classes.
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at JacksonAnnotation
+public @interface JsonTypeIdResolver
+{
+    /**
+     * Defines implementation class of {@link TypeIdResolver} to use for
+     * converting between external type id (type name) and actual
+     * type of object.
+     */
+    public Class<? extends TypeIdResolver> value();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java
new file mode 100644
index 0000000..53156d2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonTypeResolver.java
@@ -0,0 +1,25 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.*;
+
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+
+/**
+ * Annotation that can be used to explicitly define custom resolver
+ * used for handling serialization and deserialization of type information,
+ * needed for handling of polymorphic types (or sometimes just for linking
+ * abstract types to concrete types)
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JsonTypeResolver
+{
+    /**
+     * Defines implementation class of {@link TypeResolverBuilder} which is used to construct
+     * actual {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer} and {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}
+     * instances that handle reading and writing addition type information needed to support polymorphic
+     * deserialization.
+     */
+    public Class<? extends TypeResolverBuilder<?>> value();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java
new file mode 100644
index 0000000..be9ff03
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/JsonValueInstantiator.java
@@ -0,0 +1,23 @@
+package com.fasterxml.jackson.databind.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+
+/**
+ * Annotation that can be used to indicate a {@link ValueInstantiator} to use
+ * for creating instances of specified type.
+ */
+ at Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+ at Retention(RetentionPolicy.RUNTIME)
+ at com.fasterxml.jackson.annotation.JacksonAnnotation
+public @interface JsonValueInstantiator
+{
+    /**
+     * @return  {@link ValueInstantiator} to use for annotated type
+     */
+    public Class<? extends ValueInstantiator> value();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java b/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java
new file mode 100644
index 0000000..f957f71
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/NoClass.java
@@ -0,0 +1,14 @@
+package com.fasterxml.jackson.databind.annotation;
+
+/**
+ * Marker class used with annotations to indicate "no class". This is
+ * a silly but necessary work-around -- annotations can not 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).
+ */
+public final class NoClass
+{
+    private NoClass() { }
+}
+
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
new file mode 100644
index 0000000..3a73fd8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/annotation/package-info.java
@@ -0,0 +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
+ * 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
new file mode 100644
index 0000000..830e898
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/BaseSettings.java
@@ -0,0 +1,343 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.text.DateFormat;
+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;
+
+/**
+ * Immutable container class used to store simple configuration
+ * settings. Since instances are fully immutable, instances can
+ * be freely shared and used without synchronization.
+ */
+public final class BaseSettings
+    implements java.io.Serializable // since 2.1
+{
+    // for 2.1.0:
+    private static final long serialVersionUID = 4939673998947122190L;
+
+    /*
+    /**********************************************************
+    /* Configuration settings; introspection, related
+    /**********************************************************
+     */
+    
+    /**
+     * Introspector used to figure out Bean properties needed for bean serialization
+     * and deserialization. Overridable so that it is possible to change low-level
+     * details of introspection, like adding new annotation types.
+     */
+    protected final ClassIntrospector _classIntrospector;
+
+    /**
+     * Introspector used for accessing annotation value based configuration.
+     */
+    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;
+
+    /**
+     * Specific factory used for creating {@link JavaType} instances;
+     * needed to allow modules to add more custom type handling
+     * (mostly to support types of non-Java JVM languages)
+     */
+    protected final TypeFactory _typeFactory;
+
+    /*
+    /**********************************************************
+    /* Configuration settings; type resolution
+    /**********************************************************
+     */
+
+    /**
+     * Type information handler used for "untyped" values (ones declared
+     * to have type <code>Object.class</code>)
+     */
+    protected final TypeResolverBuilder<?> _typeResolverBuilder;
+    
+    /*
+    /**********************************************************
+    /* Configuration settings; other
+    /**********************************************************
+     */
+    
+    /**
+     * Custom date format to use for de-serialization. If specified, will be
+     * used instead of {@link com.fasterxml.jackson.databind.util.StdDateFormat}.
+     *<p>
+     * Note that the configured format object will be cloned once per
+     * deserialization process (first time it is needed)
+     */
+    protected final DateFormat _dateFormat;
+
+    /**
+     * Object used for creating instances of handlers (serializers, deserializers,
+     * type and type id resolvers), given class to instantiate. This is typically
+     * used to do additional configuration (with dependency injection, for example)
+     * beyond simply construction of instances; or to use alternative constructors.
+     */
+    protected final HandlerInstantiator _handlerInstantiator;
+
+    /**
+     * Default {@link java.util.Locale} used with serialization formats.
+     * Default value is {@link Locale#getDefault()}.
+     */
+    protected final Locale _locale;
+
+    /**
+     * Default {@link java.util.TimeZone} used with serialization formats.
+     * Default value is {@link TimeZone#getDefault()}, which is typically the
+     * local time zone (unless overridden for JVM).
+     *<p>
+     * Note that if a new value is set, time zone is also assigned to
+     * {@link #_dateFormat} of this object.
+     */
+    protected final TimeZone _timeZone;
+
+    /**
+     * Explicitly default {@link Base64Variant} to use for handling
+     * binary data (<code>byte[]</code>), used with data formats
+     * that use base64 encoding (like JSON, CSV).
+     * 
+     * @since 2.1
+     */
+    protected final Base64Variant _defaultBase64;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public BaseSettings(ClassIntrospector ci, AnnotationIntrospector ai,
+            VisibilityChecker<?> vc, 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;
+        _dateFormat = dateFormat;
+        _handlerInstantiator = hi;
+        _locale = locale;
+        _timeZone = tz;
+        _defaultBase64 = defaultBase64;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods
+    /**********************************************************
+     */
+    
+    public BaseSettings withClassIntrospector(ClassIntrospector ci) {
+        if (_classIntrospector == ci) {
+            return this;
+        }
+        return new BaseSettings(ci, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+    
+    public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) {
+        if (_annotationIntrospector == ai) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, ai, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+
+    public BaseSettings withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
+        return withAnnotationIntrospector(AnnotationIntrospectorPair.create(ai, _annotationIntrospector));
+    }
+
+    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),
+                _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+    
+    public BaseSettings withPropertyNamingStrategy(PropertyNamingStrategy pns) {
+        if (_propertyNamingStrategy == pns) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, pns, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+
+    public BaseSettings withTypeFactory(TypeFactory tf) {
+        if (_typeFactory == tf) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, tf,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+
+    public BaseSettings withTypeResolverBuilder(TypeResolverBuilder<?> typer) {
+        if (_typeResolverBuilder == typer) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                typer, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+    
+    public BaseSettings withDateFormat(DateFormat df) {
+        if (_dateFormat == df) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, df, _handlerInstantiator, _locale,
+                _timeZone, _defaultBase64);
+    }
+
+    public BaseSettings withHandlerInstantiator(HandlerInstantiator hi) {
+        if (_handlerInstantiator == hi) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, hi, _locale,
+                _timeZone, _defaultBase64);
+    }
+
+    public BaseSettings with(Locale l) {
+        if (_locale == l) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector, _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, l,
+                _timeZone, _defaultBase64);
+    }
+
+    /**
+     * Fluent factory for constructing a new instance that uses specified TimeZone.
+     * Note that timezone used with also be assigned to configured {@link DateFormat},
+     * changing time formatting defaults.
+     */
+    public BaseSettings with(TimeZone tz)
+    {
+        if (tz == null) {
+            throw new IllegalArgumentException();
+        }
+        DateFormat df = _dateFormat;
+        if (df instanceof StdDateFormat) {
+            df = ((StdDateFormat) df).withTimeZone(tz);
+        } else {
+            // we don't know if original format might be shared; better create a clone:
+            df = (DateFormat) df.clone();
+            df.setTimeZone(tz);
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector,
+                _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, df, _handlerInstantiator, _locale,
+                tz, _defaultBase64);
+    }
+
+    /**
+     * @since 2.1
+     */
+    public BaseSettings with(Base64Variant base64) {
+        if (base64 == _defaultBase64) {
+            return this;
+        }
+        return new BaseSettings(_classIntrospector, _annotationIntrospector,
+                _visibilityChecker, _propertyNamingStrategy, _typeFactory,
+                _typeResolverBuilder, _dateFormat, _handlerInstantiator, _locale,
+                _timeZone, base64);
+    }
+    
+    /*
+    /**********************************************************
+    /* API
+    /**********************************************************
+     */
+
+    public ClassIntrospector getClassIntrospector() {
+        return _classIntrospector;
+    }
+    
+    public AnnotationIntrospector getAnnotationIntrospector() {
+        return _annotationIntrospector;
+    }
+
+    public VisibilityChecker<?> getVisibilityChecker() {
+        return _visibilityChecker;
+    }
+
+    public PropertyNamingStrategy getPropertyNamingStrategy() {
+        return _propertyNamingStrategy;
+    }
+
+    public TypeFactory getTypeFactory() {
+        return _typeFactory;
+    }
+
+    public TypeResolverBuilder<?> getTypeResolverBuilder() {
+        return _typeResolverBuilder;
+    }
+    
+    public DateFormat getDateFormat() {
+        return _dateFormat;
+    }
+
+    public HandlerInstantiator getHandlerInstantiator() {
+        return _handlerInstantiator;
+    }
+
+    public Locale getLocale() {
+        return _locale;
+    }
+
+    public TimeZone getTimeZone() {
+        return _timeZone;
+    }
+
+    public Base64Variant getBase64Variant() {
+        return _defaultBase64;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java
new file mode 100644
index 0000000..d38d321
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/ConfigFeature.java
@@ -0,0 +1,20 @@
+package com.fasterxml.jackson.databind.cfg;
+
+/**
+ * Interface that actual SerializationFeature enumerations used by
+ * {@link MapperConfig} implementations must implement.
+ * Necessary since enums can not be extended using normal
+ * inheritance, but can implement interfaces
+ */
+public interface ConfigFeature
+{
+    /**
+     * Accessor for checking whether this feature is enabled by default.
+     */
+    public boolean enabledByDefault();
+    
+    /**
+     * Returns bit mask for this feature instance
+     */
+    public int getMask();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
new file mode 100644
index 0000000..ce7b155
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/DeserializerFactoryConfig.java
@@ -0,0 +1,206 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.databind.AbstractTypeResolver;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.deser.std.StdKeyDeserializers;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+
+/**
+ * Configuration settings container class for {@link DeserializerFactory}.
+ */
+public class DeserializerFactoryConfig
+    implements java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 3683541151102256824L;
+
+    protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0];
+    protected final static BeanDeserializerModifier[] NO_MODIFIERS = new BeanDeserializerModifier[0];
+    protected final static AbstractTypeResolver[] NO_ABSTRACT_TYPE_RESOLVERS = new AbstractTypeResolver[0];
+    protected final static ValueInstantiators[] NO_VALUE_INSTANTIATORS = new ValueInstantiators[0];
+
+    /**
+     * By default we plug default key deserializers using as "just another" set of
+     * of key deserializers.
+     * 
+     * @since 2.2
+     */
+    protected final static KeyDeserializers[] DEFAULT_KEY_DESERIALIZERS = new KeyDeserializers[] {
+        new StdKeyDeserializers()
+    };
+    
+    /**
+     * List of providers for additional deserializers, checked before considering default
+     * basic or bean deserializers.
+     */
+    protected final Deserializers[] _additionalDeserializers;
+
+    /**
+     * List of providers for additional key deserializers, checked before considering
+     * standard key deserializers.
+     */
+    protected final KeyDeserializers[] _additionalKeyDeserializers;
+    
+    /**
+     * List of modifiers that can change the way {@link BeanDeserializer} instances
+     * are configured and constructed.
+     */
+    protected final BeanDeserializerModifier[] _modifiers;
+
+
+    /**
+     * List of objects that may be able to resolve abstract types to
+     * concrete types. Used by functionality like "mr Bean" to materialize
+     * types as needed.
+     */
+    protected final AbstractTypeResolver[] _abstractTypeResolvers;
+
+    /**
+     * List of objects that know how to create instances of POJO types;
+     * possibly using custom construction (non-annoted constructors; factory
+     * methods external to value type etc).
+     * Used to support objects that are created using non-standard methods;
+     * or to support post-constructor functionality.
+     */
+    protected final ValueInstantiators[] _valueInstantiators;
+    
+    /**
+     * Constructor for creating basic configuration with no additional
+     * handlers.
+     */
+    public DeserializerFactoryConfig() {
+        this(null, null, null, null, null);
+    }
+
+    /**
+     * Copy-constructor that will create an instance that contains defined
+     * set of additional deserializer providers.
+     */
+    protected DeserializerFactoryConfig(Deserializers[] allAdditionalDeserializers,
+            KeyDeserializers[] allAdditionalKeyDeserializers,
+            BeanDeserializerModifier[] modifiers,
+            AbstractTypeResolver[] atr,
+            ValueInstantiators[] vi)
+    {
+        _additionalDeserializers = (allAdditionalDeserializers == null) ?
+                NO_DESERIALIZERS : allAdditionalDeserializers;
+        _additionalKeyDeserializers = (allAdditionalKeyDeserializers == null) ?
+                DEFAULT_KEY_DESERIALIZERS : allAdditionalKeyDeserializers;
+        _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers;
+        _abstractTypeResolvers = (atr == null) ? NO_ABSTRACT_TYPE_RESOLVERS : atr;
+        _valueInstantiators = (vi == null) ? NO_VALUE_INSTANTIATORS : vi;
+    }
+
+    /**
+     * Fluent/factory method used to construct a configuration object that
+     * has same deserializer providers as this instance, plus one specified
+     * as argument. Additional provider will be added before existing ones,
+     * meaning it has priority over existing definitions.
+     */
+    public DeserializerFactoryConfig withAdditionalDeserializers(Deserializers additional)
+    {
+        if (additional == null) {
+            throw new IllegalArgumentException("Can not pass null Deserializers");
+        }
+        Deserializers[] all = ArrayBuilders.insertInListNoDup(_additionalDeserializers, additional);
+        return new DeserializerFactoryConfig(all, _additionalKeyDeserializers, _modifiers,
+                _abstractTypeResolvers, _valueInstantiators);
+    }
+    /**
+     * Fluent/factory method used to construct a configuration object that
+     * has same key deserializer providers as this instance, plus one specified
+     * as argument. Additional provider will be added before existing ones,
+     * meaning it has priority over existing definitions.
+     */
+    public DeserializerFactoryConfig withAdditionalKeyDeserializers(KeyDeserializers additional)
+    {
+        if (additional == null) {
+            throw new IllegalArgumentException("Can not pass null KeyDeserializers");
+        }
+        KeyDeserializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeyDeserializers, additional);
+        return new DeserializerFactoryConfig(_additionalDeserializers, all, _modifiers,
+                _abstractTypeResolvers, _valueInstantiators);
+    }
+
+    /**
+     * Fluent/factory method used to construct a configuration object that
+     * has same configuration as this instance plus one additional
+     * deserialiazer modifier. Added modifier has the highest priority (that is, it
+     * gets called before any already registered modifier).
+     */
+    public DeserializerFactoryConfig withDeserializerModifier(BeanDeserializerModifier modifier)
+    {
+        if (modifier == null) {
+            throw new IllegalArgumentException("Can not pass null modifier");
+        }
+        BeanDeserializerModifier[] all = ArrayBuilders.insertInListNoDup(_modifiers, modifier);
+        return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, all,
+                _abstractTypeResolvers, _valueInstantiators);
+    }
+
+    /**
+     * Fluent/factory method used to construct a configuration object that
+     * has same configuration as this instance plus one additional
+     * abstract type resolver.
+     * Added resolver has the highest priority (that is, it
+     * gets called before any already registered resolver).
+     */
+    public DeserializerFactoryConfig withAbstractTypeResolver(AbstractTypeResolver resolver)
+    {
+        if (resolver == null) {
+            throw new IllegalArgumentException("Can not pass null resolver");
+        }
+        AbstractTypeResolver[] all = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver);
+        return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers,
+                all, _valueInstantiators);
+    }
+
+    /**
+     * Fluent/factory method used to construct a configuration object that
+     * has same configuration as this instance plus specified additional
+     * value instantiator provider object.
+     * Added instantiator provider has the highest priority (that is, it
+     * gets called before any already registered resolver).
+     * 
+     * @param instantiators Object that can provide {@link com.fasterxml.jackson.databind.deser.ValueInstantiator}s for
+     *    constructing POJO values during deserialization
+     */
+    public DeserializerFactoryConfig withValueInstantiators(ValueInstantiators instantiators) 
+    {
+        if (instantiators == null) {
+            throw new IllegalArgumentException("Can not pass null resolver");
+        }
+        ValueInstantiators[] all = ArrayBuilders.insertInListNoDup(_valueInstantiators, instantiators);
+        return new DeserializerFactoryConfig(_additionalDeserializers, _additionalKeyDeserializers, _modifiers,
+                _abstractTypeResolvers, all);
+    }
+    
+    public boolean hasDeserializers() { return _additionalDeserializers.length > 0; }
+
+    public boolean hasKeyDeserializers() { return _additionalKeyDeserializers.length > 0; }
+    
+    public boolean hasDeserializerModifiers() { return _modifiers.length > 0; }
+
+    public boolean hasAbstractTypeResolvers() { return _abstractTypeResolvers.length > 0; }
+
+    public boolean hasValueInstantiators() { return _valueInstantiators.length > 0; }
+    
+    public Iterable<Deserializers> deserializers() {
+        return ArrayBuilders.arrayAsIterable(_additionalDeserializers);
+    }
+
+    public Iterable<KeyDeserializers> keyDeserializers() {
+        return ArrayBuilders.arrayAsIterable(_additionalKeyDeserializers);
+    }
+    
+    public Iterable<BeanDeserializerModifier> deserializerModifiers() {
+        return ArrayBuilders.arrayAsIterable(_modifiers);
+    }
+
+    public Iterable<AbstractTypeResolver> abstractTypeResolvers() {
+        return ArrayBuilders.arrayAsIterable(_abstractTypeResolvers);
+    }
+
+    public Iterable<ValueInstantiators> valueInstantiators() {
+        return ArrayBuilders.arrayAsIterable(_valueInstantiators);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java
new file mode 100644
index 0000000..4cc0eb6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/HandlerInstantiator.java
@@ -0,0 +1,146 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Helper class used for handling details of creating handler instances (things
+ * like {@link JsonSerializer}s, {@link JsonDeserializer}s, various type
+ * handlers) of specific types. Actual handler type has been resolved at this
+ * point, so instantiator is strictly responsible for providing a configured
+ * instance by constructing and configuring a new instance, or possibly by
+ * recycling a shared instance. One use case is that of allowing
+ * dependency injection, which would otherwise be difficult to do.
+ *<p>
+ * Custom instances are allowed to return null to indicate that caller should
+ * use the default instantiation handling (which just means calling no-argument
+ * constructor via reflection).
+ *<p>
+ * Care has to be taken to ensure that if instance returned is shared, it will
+ * be thread-safe; caller will not synchronize access to returned instances.
+ */
+public abstract class HandlerInstantiator
+{
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method called to get an instance of deserializer of specified type.
+     * 
+     * @param config Deserialization configuration in effect
+     * @param annotated Element (Class, Method, Field, constructor parameter) that
+     *    had annotation defining class of deserializer to construct (to allow
+     *    implementation use information from other annotations)
+     * @param deserClass Class of deserializer instance to return
+     * 
+     * @return Deserializer instance to use
+     */
+    public abstract JsonDeserializer<?> deserializerInstance(DeserializationConfig config,
+            Annotated annotated, Class<?> deserClass);
+
+    /**
+     * Method called to get an instance of key deserializer of specified type.
+     * 
+     * @param config Deserialization configuration in effect
+     * @param annotated Element (Class, Method, Field, constructor parameter) that
+     *    had annotation defining class of key deserializer to construct (to allow
+     *    implementation use information from other annotations)
+     * @param keyDeserClass Class of key deserializer instance to return
+     * 
+     * @return Key deserializer instance to use
+     */
+    public abstract KeyDeserializer keyDeserializerInstance(DeserializationConfig config,
+            Annotated annotated, Class<?> keyDeserClass);
+    
+    /**
+     * Method called to get an instance of serializer of specified type.
+     * 
+     * @param config Serialization configuration in effect
+     * @param annotated Element (Class, Method, Field) that
+     *    had annotation defining class of serializer to construct (to allow
+     *    implementation use information from other annotations)
+     * @param serClass Class of serializer instance to return
+     * 
+     * @return Serializer instance to use
+     */
+    public abstract JsonSerializer<?> serializerInstance(SerializationConfig config,
+            Annotated annotated, Class<?> serClass);
+
+    /**
+     * Method called to get an instance of TypeResolverBuilder of specified type.
+     * 
+     * @param config Mapper configuration in effect (either SerializationConfig or
+     *   DeserializationConfig, depending on when instance is being constructed)
+     * @param annotated annotated Element (Class, Method, Field) that
+     *    had annotation defining class of builder to construct (to allow
+     *    implementation use information from other annotations)
+     * @param builderClass Class of builder instance to return
+     * 
+     * @return TypeResolverBuilder instance to use
+     */
+    public abstract TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> builderClass);
+
+    /**
+     * Method called to get an instance of TypeIdResolver of specified type.
+     * 
+     * @param config Mapper configuration in effect (either SerializationConfig or
+     *   DeserializationConfig, depending on when instance is being constructed)
+     * @param annotated annotated Element (Class, Method, Field) that
+     *    had annotation defining class of resolver to construct (to allow
+     *    implementation use information from other annotations)
+     * @param resolverClass Class of resolver instance to return
+     * 
+     * @return TypeResolverBuilder instance to use
+     */
+    public abstract TypeIdResolver typeIdResolverInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> resolverClass);
+
+    /**
+     * Method called to construct an instance of ValueInstantiator of specified type.
+     */
+    public ValueInstantiator valueInstantiatorInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> resolverClass) {
+        return null;
+    }
+
+    
+    /**
+     * Method called to construct a ObjectIdHandler instance of specified type.
+     * 
+     * @since 2.0
+     */
+    public ObjectIdGenerator<?> objectIdGeneratorInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> implClass) {
+        return null;
+    }
+
+    /**
+     * Method called to construct a NamingStrategy instance used for specified
+     * class.
+     * 
+     * @since 2.1
+     */
+    public PropertyNamingStrategy namingStrategyInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> implClass) {
+        return null;
+    }
+
+    /**
+     * Method called to construct a Converter instance used for specified class.
+     * 
+     * @since 2.2
+     */
+    public Converter<?,?> converterInstance(MapperConfig<?> config,
+            Annotated annotated, Class<?> implClass) {
+        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
new file mode 100644
index 0000000..b1b7dc8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfig.java
@@ -0,0 +1,372 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.text.DateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.Base64Variant;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
+import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Interface that defines functionality accessible through both
+ * serialization and deserialization configuration objects;
+ * accessors to mode-independent configuration settings
+ * and such.
+ * In addition, shared features are defined
+ * in {@link MapperFeature}.
+ *<p>
+ * Small part of implementation is included here by aggregating
+ * {@link BaseSettings} instance that contains configuration
+ * that is shared between different types of instances.
+ */
+public abstract class MapperConfig<T extends MapperConfig<T>>
+    implements ClassIntrospector.MixInResolver,
+        java.io.Serializable
+{
+    private static final long serialVersionUID = 8891625428805876137L;
+
+    /**
+     * Set of shared mapper features enabled.
+     */
+    protected final int _mapperFeatures;
+    
+    /**
+     * Immutable container object for simple configuration settings.
+     */
+    protected final BaseSettings _base;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: constructors
+    /**********************************************************
+     */
+
+    protected MapperConfig(BaseSettings base, int mapperFeatures)
+    {
+        _base = base;
+        _mapperFeatures = mapperFeatures;
+    }
+
+    protected MapperConfig(MapperConfig<T> src)
+    {
+        _base = src._base;
+        _mapperFeatures = src._mapperFeatures;
+    }
+    
+    /**
+     * Method that calculates bit set (flags) of all features that
+     * are enabled by default.
+     */
+    public static <F extends Enum<F> & ConfigFeature> int collectFeatureDefaults(Class<F> enumClass)
+    {
+        int flags = 0;
+        for (F value : enumClass.getEnumConstants()) {
+            if (value.enabledByDefault()) {
+                flags |= value.getMask();
+            }
+        }
+        return flags;
+    }
+    /*
+    /**********************************************************
+    /* Life-cycle: factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method for constructing and returning a new instance with specified
+     * mapper features enabled.
+     */
+    public abstract T with(MapperFeature... features);
+
+    /**
+     * Method for constructing and returning a new instance with specified
+     * mapper features disabled.
+     */
+    public abstract T without(MapperFeature... features);
+    
+    /*
+    /**********************************************************
+    /* Configuration: simple features
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for simple mapper features (which are shared for
+     * serialization, deserialization)
+     */
+    public final boolean isEnabled(MapperFeature f) {
+        return (_mapperFeatures & f.getMask()) != 0;
+    }
+    
+    /**
+     * Method for determining whether annotation processing is enabled or not
+     * (default settings are typically that it is enabled; must explicitly disable).
+     * 
+     * @return True if annotation processing is enabled; false if not
+     */
+    public final boolean isAnnotationProcessingEnabled() {
+        return isEnabled(MapperFeature.USE_ANNOTATIONS);
+    }
+
+    /**
+     * Accessor for determining whether it is ok to try to force override of access
+     * modifiers to be able to get or set values of non-public Methods, Fields;
+     * to invoke non-public Constructors, Methods; or to instantiate non-public
+     * Classes. By default this is enabled, but on some platforms it needs to be
+     * prevented since if this would violate security constraints and cause failures.
+     * 
+     * @return True if access modifier overriding is allowed (and may be done for
+     *   any Field, Method, Constructor or Class); false to prevent any attempts
+     *   to override.
+     */
+    public final boolean canOverrideAccessModifiers() {
+        return isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
+    }
+
+    /**
+     * Accessor for checking whether default settings for property handling
+     * indicate that properties should be alphabetically ordered or not.
+     */
+    public final boolean shouldSortPropertiesAlphabetically() {
+        return isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
+    }
+
+    /**
+     * Accessor for checking whether configuration indicates that
+     * "root wrapping" (use of an extra property/name pair at root level)
+     * is expected or not.
+     */
+    public abstract boolean useRootWrapping();
+    
+    /*
+    /**********************************************************
+    /* Configuration: introspectors, mix-ins
+    /**********************************************************
+     */
+    
+    public ClassIntrospector getClassIntrospector() {
+        return _base.getClassIntrospector();
+    }
+
+    /**
+     * Method for getting {@link AnnotationIntrospector} configured
+     * to introspect annotation values used for configuration.
+     *<p>
+     * Non-final since it is actually overridden by sub-classes (for now?)
+     */
+    public AnnotationIntrospector getAnnotationIntrospector() {
+        return _base.getAnnotationIntrospector();
+    }
+
+    /**
+     * 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();
+    }
+
+    public final HandlerInstantiator getHandlerInstantiator() {
+        return _base.getHandlerInstantiator();
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration: type and subtype handling
+    /**********************************************************
+     */
+
+    /**
+     * Method called to locate a type info handler for types that do not have
+     * one explicitly declared via annotations (or other configuration).
+     * If such default handler is configured, it is returned; otherwise
+     * null is returned.
+     */
+    public final TypeResolverBuilder<?> getDefaultTyper(JavaType baseType) {
+        return _base.getTypeResolverBuilder();
+    }
+    
+    public abstract SubtypeResolver getSubtypeResolver();
+
+    public final TypeFactory getTypeFactory() {
+        return _base.getTypeFactory();
+    }
+
+    /**
+     * Helper method that will construct {@link JavaType} for given
+     * raw class.
+     * This is a simple short-cut for:
+     *<pre>
+     *    getTypeFactory().constructType(cls);
+     *</pre>
+     */
+    public final JavaType constructType(Class<?> cls) {
+        return getTypeFactory().constructType(cls, (TypeBindings) null);
+    }
+
+    /**
+     * Helper method that will construct {@link JavaType} for given
+     * type reference
+     * This is a simple short-cut for:
+     *<pre>
+     *    getTypeFactory().constructType(valueTypeRef);
+     *</pre>
+     */
+    public final JavaType constructType(TypeReference<?> valueTypeRef) {
+        return getTypeFactory().constructType(valueTypeRef.getType(), (TypeBindings) null);
+    }
+
+    public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass) {
+        return getTypeFactory().constructSpecializedType(baseType, subclass);
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration: introspection support
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for getting bean description that only contains class
+     * annotations: useful if no getter/setter/creator information is needed.
+     */
+    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);
+
+    /**
+     * 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 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);
+
+    /*
+    /**********************************************************
+    /* Configuration: other
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing currently configured (textual) date format
+     * that will be used for reading or writing date values (in case
+     * of writing, only if textual output is configured; not if dates
+     * are to be serialized as time stamps).
+     *<p>
+     * Note that typically {@link DateFormat} instances are <b>not thread-safe</b>
+     * (at least ones provided by JDK):
+     * this means that calling code should clone format instance before
+     * using it.
+     *<p>
+     * This method is usually only called by framework itself, since there
+     * are convenience methods available via
+     * {@link DeserializationContext} and {@link SerializerProvider} that
+     * take care of cloning and thread-safe reuse.
+     */
+    public final DateFormat getDateFormat() { return _base.getDateFormat(); }
+
+    /**
+     * Method for accessing the default {@link java.util.Locale} to use
+     * for formatting, unless overridden by local annotations.
+     * Initially set to {@link Locale#getDefault()}.
+     */
+    public final Locale getLocale() { return _base.getLocale(); }
+    
+    /**
+     * Method for accessing the default {@link java.util.TimeZone} to use
+     * for formatting, unless overridden by local annotations.
+     * Initially set to {@link TimeZone#getDefault()}.
+     */
+    public final TimeZone getTimeZone() { return _base.getTimeZone(); }
+    
+    /**
+     * Accessor for finding currently active view, if any (null if none)
+     */
+    public abstract Class<?> getActiveView();
+
+    /**
+     * Method called during deserialization if Base64 encoded content
+     * needs to be decoded. Default version just returns default Jackson
+     * uses, which is modified-mime which does not add linefeeds (because
+     * those would have to be escaped in JSON strings); but this can
+     * be configured on {@link ObjectWriter}.
+     */
+    public Base64Variant getBase64Variant() {
+        return _base.getBase64Variant();
+    }
+    
+    /*
+    /**********************************************************
+    /* Methods for instantiating handlers
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to obtain an instance of <code>TypeIdResolver</code> of
+     * specified type.
+     */
+    public TypeResolverBuilder<?> typeResolverBuilderInstance(Annotated annotated,
+            Class<? extends TypeResolverBuilder<?>> builderClass)
+    {
+        HandlerInstantiator hi = getHandlerInstantiator();
+        if (hi != null) {
+            TypeResolverBuilder<?> builder = hi.typeResolverBuilderInstance(this, annotated, builderClass);
+            if (builder != null) {
+                return builder;
+            }
+        }
+        return (TypeResolverBuilder<?>) ClassUtil.createInstance(builderClass, canOverrideAccessModifiers());
+    }
+
+    /**
+     * Method that can be called to obtain an instance of <code>TypeIdResolver</code> of
+     * specified type.
+     */
+    public TypeIdResolver typeIdResolverInstance(Annotated annotated,
+            Class<? extends TypeIdResolver> resolverClass)
+    {
+        HandlerInstantiator hi = getHandlerInstantiator();
+        if (hi != null) {
+            TypeIdResolver builder = hi.typeIdResolverInstance(this, annotated, resolverClass);
+            if (builder != null) {
+                return builder;
+            }
+        }
+        return (TypeIdResolver) ClassUtil.createInstance(resolverClass, canOverrideAccessModifiers());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
new file mode 100644
index 0000000..d7e1771
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/MapperConfigBase.java
@@ -0,0 +1,337 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import java.text.DateFormat;
+import java.util.Locale;
+import java.util.Map;
+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.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+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.type.ClassKey;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public abstract class MapperConfigBase<CFG extends ConfigFeature,
+    T extends MapperConfigBase<CFG,T>>
+    extends MapperConfig<T>
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = -8378230381628000111L;
+
+    private final static int DEFAULT_MAPPER_FEATURES = collectFeatureDefaults(MapperFeature.class);
+
+    /*
+    /**********************************************************
+    /* Immutable config
+    /**********************************************************
+     */
+
+    /**
+     * Mix-in annotation mappings to use, if any: immutable,
+     * can not be changed once defined.
+     */
+    protected final Map<ClassKey,Class<?>> _mixInAnnotations;
+
+    /**
+     * Registered concrete subtypes that can be used instead of (or
+     * in addition to) ones declared using annotations.
+     */
+    protected final SubtypeResolver _subtypeResolver;
+
+    /**
+     * Explicitly defined root name to use, if any; if empty
+     * String, will disable root-name wrapping; if null, will
+     * use defaults
+     */
+    protected final String _rootName;
+
+    /**
+     * View to use for filtering out properties to serialize
+     * or deserialize.
+     * Null if none (will also be assigned null if <code>Object.class</code>
+     * is defined), meaning that all properties are to be included.
+     */
+    protected final Class<?> _view;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used when creating a new instance (compared to
+     * that of creating fluent copies)
+     */
+    protected MapperConfigBase(BaseSettings base,
+            SubtypeResolver str, Map<ClassKey,Class<?>> mixins)
+    {
+        super(base, DEFAULT_MAPPER_FEATURES);
+        _mixInAnnotations = mixins;
+        _subtypeResolver = str;
+        _rootName = null;
+        _view = null;
+    }
+    
+    /**
+     * Pass-through constructor used when no changes are needed to the
+     * base class.
+     */
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src)
+    {
+        super(src);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = src._rootName;
+        _view = src._view;
+    }
+
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, BaseSettings base)
+    {
+        super(base, src._mapperFeatures);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = src._rootName;
+        _view = src._view;
+    }
+    
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, int mapperFeatures)
+    {
+        super(src._base, mapperFeatures);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = src._rootName;
+        _view = src._view;
+    }
+
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, SubtypeResolver str) {
+        super(src);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = str;
+        _rootName = src._rootName;
+        _view = src._view;
+    }
+
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, String rootName) {
+        super(src);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = rootName;
+        _view = src._view;
+    }
+
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, Class<?> view)
+    {
+        super(src);
+        _mixInAnnotations = src._mixInAnnotations;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = src._rootName;
+        _view = view;
+    }
+
+    /**
+     * @since 2.1
+     */
+    protected MapperConfigBase(MapperConfigBase<CFG,T> src, Map<ClassKey,Class<?>> mixins)
+    {
+        super(src);
+        _mixInAnnotations = mixins;
+        _subtypeResolver = src._subtypeResolver;
+        _rootName = src._rootName;
+        _view = src._view;
+    }
+    
+    /*
+    /**********************************************************
+    /* Addition fluent factory methods, common to all sub-types
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link AnnotationIntrospector} to use (replacing old one).
+     *<p>
+     * NOTE: make sure to register new instance with <code>ObjectMapper</code>
+     * if directly calling this method.
+     */
+    public abstract T with(AnnotationIntrospector 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);
+
+    /**
+     * Method for constructing and returning a new instance with additional
+     * {@link AnnotationIntrospector} inserted (as the highest priority one)
+     */
+    public abstract T withInsertedAnnotationIntrospector(AnnotationIntrospector introspector);
+    
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link ClassIntrospector}
+     * to use.
+     *<p>
+     * 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.
+     */
+    public abstract T withRootName(String 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
+     * {@link TypeFactory}
+     * to use.
+     */
+    public abstract T with(TypeFactory typeFactory);
+
+    /**
+     * Method for constructing and returning a new instance with different
+     * {@link TypeResolverBuilder} to use.
+     */
+    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);
+    
+    /*
+    /**********************************************************
+    /* Simple accessors
+    /**********************************************************
+     */
+    
+    /**
+     * Accessor for object used for finding out all reachable subtypes
+     * for supertypes; needed when a logical type name is used instead
+     * of class name (or custom scheme).
+     */
+    @Override
+    public final SubtypeResolver getSubtypeResolver() {
+        return _subtypeResolver;
+    }
+
+    public final String getRootName() {
+        return _rootName;
+    }
+
+    @Override
+    public final Class<?> getActiveView() {
+        return _view;
+    }
+    
+    /*
+    /**********************************************************
+    /* ClassIntrospector.MixInResolver impl:
+    /**********************************************************
+     */
+
+    /**
+     * Method that will check if there are "mix-in" classes (with mix-in
+     * annotations) for given class
+     */
+    @Override
+    public final Class<?> findMixInClassFor(Class<?> cls) {
+        return (_mixInAnnotations == null) ? null : _mixInAnnotations.get(new ClassKey(cls));
+    }
+
+    public final int mixInCount() {
+        return (_mixInAnnotations == null) ? 0 : _mixInAnnotations.size();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in b/src/main/java/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in
new file mode 100644
index 0000000..7860aa1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/PackageVersion.java.in
@@ -0,0 +1,20 @@
+package @package@;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.Versioned;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * Automatically generated from PackageVersion.java.in during
+ * packageVersion-generate execution of maven-replacer-plugin in
+ * pom.xml.
+ */
+public final class PackageVersion implements Versioned {
+    public final static Version VERSION = VersionUtil.parseVersion(
+        "@projectversion@", "@projectgroupid@", "@projectartifactid@");
+
+    @Override
+    public Version version() {
+        return VERSION;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java
new file mode 100644
index 0000000..57f8518
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/SerializerFactoryConfig.java
@@ -0,0 +1,99 @@
+package com.fasterxml.jackson.databind.cfg;
+
+import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+
+/**
+ * Configuration settings container class for
+ * {@link SerializerFactory} implementations.
+ */
+public final class SerializerFactoryConfig
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+    /**
+     * Constant for empty <code>Serializers</code> array (which by definition
+     * is stateless and reusable)
+     */
+    protected final static Serializers[] NO_SERIALIZERS = new Serializers[0];
+
+    protected final static BeanSerializerModifier[] NO_MODIFIERS = new BeanSerializerModifier[0];
+    
+    /**
+     * List of providers for additional serializers, checked before considering default
+     * basic or bean serialializers.
+     */
+    protected final Serializers[] _additionalSerializers;
+
+    /**
+     * List of providers for additional key serializers, checked before considering default
+     * key serialializers.
+     */
+    protected final Serializers[] _additionalKeySerializers;
+    
+    /**
+     * List of modifiers that can change the way {@link BeanSerializer} instances
+     * are configured and constructed.
+     */
+    protected final BeanSerializerModifier[] _modifiers;
+    
+    public SerializerFactoryConfig() {
+        this(null, null, null);
+    }
+
+    protected SerializerFactoryConfig(Serializers[] allAdditionalSerializers,
+            Serializers[] allAdditionalKeySerializers,
+            BeanSerializerModifier[] modifiers)
+    {
+        _additionalSerializers = (allAdditionalSerializers == null) ?
+                NO_SERIALIZERS : allAdditionalSerializers;
+        _additionalKeySerializers = (allAdditionalKeySerializers == null) ?
+                NO_SERIALIZERS : allAdditionalKeySerializers;
+        _modifiers = (modifiers == null) ? NO_MODIFIERS : modifiers;
+    }
+
+    public SerializerFactoryConfig withAdditionalSerializers(Serializers additional)
+    {
+        if (additional == null) {
+            throw new IllegalArgumentException("Can not pass null Serializers");
+        }
+        Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalSerializers, additional);
+        return new SerializerFactoryConfig(all, _additionalKeySerializers, _modifiers);
+    }
+
+    public SerializerFactoryConfig withAdditionalKeySerializers(Serializers additional)
+    {
+        if (additional == null) {
+            throw new IllegalArgumentException("Can not pass null Serializers");
+        }
+        Serializers[] all = ArrayBuilders.insertInListNoDup(_additionalKeySerializers, additional);
+        return new SerializerFactoryConfig(_additionalSerializers, all, _modifiers);
+    }
+    
+    public SerializerFactoryConfig withSerializerModifier(BeanSerializerModifier modifier)
+    {
+        if (modifier == null) {
+            throw new IllegalArgumentException("Can not pass null modifier");
+        }
+        BeanSerializerModifier[] modifiers = ArrayBuilders.insertInListNoDup(_modifiers, modifier);
+        return new SerializerFactoryConfig(_additionalSerializers, _additionalKeySerializers, modifiers);
+    }
+
+    public boolean hasSerializers() { return _additionalSerializers.length > 0; }
+
+    public boolean hasKeySerializers() { return _additionalKeySerializers.length > 0; }
+    
+    public boolean hasSerializerModifiers() { return _modifiers.length > 0; }
+    
+    public Iterable<Serializers> serializers() {
+        return ArrayBuilders.arrayAsIterable(_additionalSerializers);
+    }
+
+    public Iterable<Serializers> keySerializers() {
+        return ArrayBuilders.arrayAsIterable(_additionalKeySerializers);
+    }
+    
+    public Iterable<BeanSerializerModifier> serializerModifiers() {
+        return ArrayBuilders.arrayAsIterable(_modifiers);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/cfg/package-info.java b/src/main/java/com/fasterxml/jackson/databind/cfg/package-info.java
new file mode 100644
index 0000000..5afd53a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/cfg/package-info.java
@@ -0,0 +1,8 @@
+/**
+Package that contains most of configuration-related classes;
+exception being couple of most-commonly used configuration
+things (like Feature enumerations) that are at the
+main level (<code>com.fasterxml.jackson.databind</code>).
+*/
+
+package com.fasterxml.jackson.databind.cfg;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
new file mode 100644
index 0000000..cf75377
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/AbstractDeserializer.java
@@ -0,0 +1,182 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
+import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Deserializer only used for abstract types used as placeholders during polymorphic
+ * type handling deserialization. If so, there is no real deserializer associated
+ * with nominal type, just {@link TypeDeserializer}; and any calls that do not
+ * pass such resolver will result in an error.
+ * 
+ * @author tatu
+ */
+public class AbstractDeserializer
+    extends JsonDeserializer<Object>
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = -3010349050434697698L;
+
+    protected final JavaType _baseType;
+
+    protected final ObjectIdReader _objectIdReader;
+
+    protected final Map<String, SettableBeanProperty> _backRefProperties;
+    
+    // support for "native" types, which require special care:
+    
+    protected final boolean _acceptString;
+    protected final boolean _acceptBoolean;
+    protected final boolean _acceptInt;
+    protected final boolean _acceptDouble;
+    
+    public AbstractDeserializer(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc, Map<String, SettableBeanProperty> backRefProps)
+    {
+        _baseType = beanDesc.getType();
+        _objectIdReader = builder.getObjectIdReader();
+        _backRefProperties = backRefProps;
+        Class<?> cls = _baseType.getRawClass();
+        _acceptString = cls.isAssignableFrom(String.class);
+        _acceptBoolean = (cls == Boolean.TYPE) || cls.isAssignableFrom(Boolean.class);
+        _acceptInt = (cls == Integer.TYPE) || cls.isAssignableFrom(Integer.class);
+        _acceptDouble = (cls == Double.TYPE) || cls.isAssignableFrom(Double.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Public accessors
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isCachable() { return true; }
+    
+    /**
+     * Overridden to return true for those instances that are
+     * handling value for which Object Identity handling is enabled
+     * (either via value type or referring property).
+     */
+    @Override
+    public ObjectIdReader getObjectIdReader() {
+        return _objectIdReader;
+    }
+
+    /**
+     * Method called by <code>BeanDeserializer</code> to resolve back reference
+     * part of managed references.
+     */
+    public SettableBeanProperty findBackReference(String logicalName)
+    {
+        return (_backRefProperties == null) ? null : _backRefProperties.get(logicalName);
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializer implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // Hmmh. One tricky question; for scalar, is it an Object Id, or "Natural" type?
+        // for now, prefer Object Id:
+        if (_objectIdReader != null) {
+            JsonToken t = jp.getCurrentToken();
+            // should be good enough check; we only care about Strings, integral numbers:
+            if (t != null && t.isScalarValue()) {
+                return _deserializeFromObjectId(jp, ctxt);
+            }
+        }
+        
+        // First: support "natural" values (which are always serialized without type info!)
+        Object result = _deserializeIfNatural(jp, ctxt);
+        if (result != null) {
+            return result;
+        }
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // This method should never be called...
+        throw ctxt.instantiationException(_baseType.getRawClass(),
+                "abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information");
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("incomplete-switch")
+    protected Object _deserializeIfNatural(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        /* As per [JACKSON-417], there is a chance we might be "natural" types
+         * (String, Boolean, Integer, Double), which do not include any type information...
+         * Care must be taken to only return this if return type matches, however.
+         * Finally, we may have to consider possibility of custom handlers for
+         * these values: but for now this should work ok.
+         */
+        switch (jp.getCurrentToken()) {
+        case VALUE_STRING:
+            if (_acceptString) {
+                return jp.getText();
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            if (_acceptInt) {
+                return jp.getIntValue();
+            }
+            break;
+
+        case VALUE_NUMBER_FLOAT:
+            if (_acceptDouble) {
+                return Double.valueOf(jp.getDoubleValue());
+            }
+            break;
+        case VALUE_TRUE:
+            if (_acceptBoolean) {
+                return Boolean.TRUE;
+            }
+            break;
+        case VALUE_FALSE:
+            if (_acceptBoolean) {
+                return Boolean.FALSE;
+            }
+            break;
+        }
+        return null;
+    }
+
+    /**
+     * Method called in cases where it looks like we got an Object Id
+     * to parse and use as a reference.
+     */
+    protected Object _deserializeFromObjectId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        Object id = _objectIdReader.deserializer.deserialize(jp, ctxt);
+        ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
+        // do we have it resolved?
+        Object pojo = roid.item;
+        if (pojo == null) { // not yet; should wait...
+            throw new IllegalStateException("Could not resolve Object Id ["+id+"] -- unresolved forward-reference?");
+        }
+        return pojo;
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
new file mode 100644
index 0000000..655869d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BasicDeserializerFactory.java
@@ -0,0 +1,1558 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.CreatorCollector;
+import com.fasterxml.jackson.databind.deser.std.*;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.EnumResolver;
+
+/**
+ * Abstract factory base class that can provide deserializers for standard
+ * JDK classes, including collection classes and simple heuristics for
+ * "upcasting" commmon collection interface types
+ * (such as {@link java.util.Collection}).
+ *<p>
+ * Since all simple deserializers are eagerly instantiated, and there is
+ * no additional introspection or customizability of these types,
+ * this factory is stateless.
+ */
+ at SuppressWarnings("serial")
+public abstract class BasicDeserializerFactory
+    extends DeserializerFactory
+    implements java.io.Serializable
+{
+    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_ITERABLE = Iterable.class;
+    
+    /**
+     * Also special array deserializers for primitive array types.
+     */
+//    final protected static HashMap<JavaType,JsonDeserializer<Object>> _arrayDeserializers = PrimitiveArrayDeserializers.getAll();
+    
+    /* We do some defaulting for abstract Map classes and
+     * interfaces, to avoid having to use exact types or annotations in
+     * cases where the most common concrete Maps will do.
+     */
+    @SuppressWarnings("rawtypes")
+    final static HashMap<String, Class<? extends Map>> _mapFallbacks =
+        new HashMap<String, Class<? extends Map>>();
+    static {
+        _mapFallbacks.put(Map.class.getName(), LinkedHashMap.class);
+        _mapFallbacks.put(ConcurrentMap.class.getName(), ConcurrentHashMap.class);
+        _mapFallbacks.put(SortedMap.class.getName(), TreeMap.class);
+
+        /* 11-Jan-2009, tatu: Let's see if we can still add support for
+         *    JDK 1.6 interfaces, even if we run on 1.5. Just need to be
+         *    more careful with typos, since compiler won't notice any
+         *    problems...
+         */
+        _mapFallbacks.put("java.util.NavigableMap", TreeMap.class);
+        try {
+            Class<?> key = java.util.concurrent.ConcurrentNavigableMap.class;
+            Class<?> value = java.util.concurrent.ConcurrentSkipListMap.class;
+            @SuppressWarnings("unchecked")
+                Class<? extends Map<?,?>> mapValue = (Class<? extends Map<?,?>>) value;
+            _mapFallbacks.put(key.getName(), mapValue);
+        } catch (Throwable e) { // some class loading problems are Errors, others Exceptions
+            System.err.println("Problems with (optional) types: "+e);
+        }
+    }
+
+    /* We do some defaulting for abstract Collection classes and
+     * interfaces, to avoid having to use exact types or annotations in
+     * cases where the most common concrete Collection will do.
+     */
+    @SuppressWarnings("rawtypes")
+    final static HashMap<String, Class<? extends Collection>> _collectionFallbacks =
+        new HashMap<String, Class<? extends Collection>>();
+    static {
+        _collectionFallbacks.put(Collection.class.getName(), ArrayList.class);
+        _collectionFallbacks.put(List.class.getName(), ArrayList.class);
+        _collectionFallbacks.put(Set.class.getName(), HashSet.class);
+        _collectionFallbacks.put(SortedSet.class.getName(), TreeSet.class);
+        _collectionFallbacks.put(Queue.class.getName(), LinkedList.class);
+
+        // then 1.6 types:
+        /* 17-May-2013, tatu: [Issue#216] Should be fine to use straight Class references EXCEPT
+         *   that some godforsaken platforms (... looking at you, Android) do not
+         *   include these. So, use "soft" references...
+         */
+        _collectionFallbacks.put("java.util.Deque", LinkedList.class);
+        _collectionFallbacks.put("java.util.NavigableSet", TreeSet.class);
+    }
+    
+    /*
+    /**********************************************************
+    /* Config
+    /**********************************************************
+     */
+    
+    /**
+     * Configuration settings for this factory; immutable instance (just like this
+     * factory), new version created via copy-constructor (fluent-style)
+     */
+    protected final DeserializerFactoryConfig _factoryConfig;
+    
+    /*
+    /**********************************************************
+    /* Life cycle
+    /**********************************************************
+     */
+
+    protected BasicDeserializerFactory(DeserializerFactoryConfig config) {
+        _factoryConfig = config;
+    }
+    
+    /**
+     * Method for getting current {@link DeserializerFactoryConfig}.
+      *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of config object.
+     */
+    public DeserializerFactoryConfig getFactoryConfig() {
+        return _factoryConfig;
+    }
+
+    protected abstract DeserializerFactory withConfig(DeserializerFactoryConfig config);
+    
+    /*
+    /********************************************************
+    /* Configuration handling: fluent factories
+    /********************************************************
+     */
+
+    /**
+     * Convenience method for creating a new factory instance with additional deserializer
+     * provider.
+     */
+    @Override
+    public final DeserializerFactory withAdditionalDeserializers(Deserializers additional) {
+        return withConfig(_factoryConfig.withAdditionalDeserializers(additional));
+    }
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link KeyDeserializers}.
+     */
+    @Override
+    public final DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional) {
+        return withConfig(_factoryConfig.withAdditionalKeyDeserializers(additional));
+    }
+    
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link BeanDeserializerModifier}.
+     */
+    @Override
+    public final DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier) {
+        return withConfig(_factoryConfig.withDeserializerModifier(modifier));
+    }
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link AbstractTypeResolver}.
+     */
+    @Override
+    public final DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver) {
+        return withConfig(_factoryConfig.withAbstractTypeResolver(resolver));
+    }
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link ValueInstantiators}.
+     */
+    @Override
+    public final DeserializerFactory withValueInstantiators(ValueInstantiators instantiators) {
+        return withConfig(_factoryConfig.withValueInstantiators(instantiators));
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl (partial): type mappings
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType mapAbstractType(DeserializationConfig config, JavaType type)
+        throws JsonMappingException
+    {
+        // first, general mappings
+        while (true) {
+            JavaType next = _mapAbstractType2(config, type);
+            if (next == null) {
+                return type;
+            }
+            /* Should not have to worry about cycles; but better verify since they will invariably
+             * occur... :-)
+             * (also: guard against invalid resolution to a non-related type)
+             */
+            Class<?> prevCls = type.getRawClass();
+            Class<?> nextCls = next.getRawClass();
+            if ((prevCls == nextCls) || !prevCls.isAssignableFrom(nextCls)) {
+                throw new IllegalArgumentException("Invalid abstract type resolution from "+type+" to "+next+": latter is not a subtype of former");
+            }
+            type = next;
+        }
+    }
+
+    /**
+     * Method that will find abstract type mapping for specified type, doing a single
+     * lookup through registered abstract type resolvers; will not do recursive lookups.
+     */
+    private JavaType _mapAbstractType2(DeserializationConfig config, JavaType type)
+        throws JsonMappingException
+    {
+        Class<?> currClass = type.getRawClass();
+        if (_factoryConfig.hasAbstractTypeResolvers()) {
+            for (AbstractTypeResolver resolver : _factoryConfig.abstractTypeResolvers()) {
+                JavaType concrete = resolver.findTypeMapping(config, type);
+                if (concrete != null && concrete.getRawClass() != currClass) {
+                    return concrete;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl (partial): ValueInstantiators
+    /**********************************************************
+     */
+
+    /**
+     * Value instantiator is created both based on creator annotations,
+     * and on optional externally provided instantiators (registered through
+     * module interface).
+     */
+    @Override
+    public ValueInstantiator findValueInstantiator(DeserializationContext ctxt,
+            BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+
+        ValueInstantiator instantiator = null;
+        // [JACKSON-633] 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.
+             */
+            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) {
+                    throw new JsonMappingException("Broken registered ValueInstantiators (of type "
+                            +insts.getClass().getName()+"): returned null ValueInstantiator");
+                }
+            }
+        }
+
+        // Sanity check: does the chosen instantatior have incomplete creators?
+        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-paramater constructor annotated as Creator");
+        }
+
+        return instantiator;
+    }
+
+    private ValueInstantiator _findStdValueInstantiator(DeserializationConfig config,
+            BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        return JacksonDeserializers.findValueInstantiator(config, beanDesc);
+    }
+
+    /**
+     * Method that will construct standard default {@link ValueInstantiator}
+     * using annotations (like @JsonCreator) and visibility rules
+     */
+    protected ValueInstantiator _constructDefaultValueInstantiator(DeserializationContext ctxt,
+            BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        boolean fixAccess = ctxt.canOverrideAccessModifiers();
+        CreatorCollector creators =  new CreatorCollector(beanDesc, fixAccess);
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        
+        // need to construct suitable visibility checker:
+        final DeserializationConfig config = ctxt.getConfig();
+        VisibilityChecker<?> vchecker = config.getDefaultVisibilityChecker();
+        vchecker = intr.findAutoDetectVisibility(beanDesc.getClassInfo(), vchecker);
+
+        /* Important: first add factory methods; then constructors, so
+         * latter can override former!
+         */
+        _addDeserializerFactoryMethods(ctxt, beanDesc, vchecker, intr, creators);
+        // constructors only usable on concrete types:
+        if (beanDesc.getType().isConcrete()) {
+            _addDeserializerConstructors(ctxt, beanDesc, vchecker, intr, creators);
+        }
+        return creators.constructValueInstantiator(config);
+    }
+
+    public ValueInstantiator _valueInstantiatorInstance(DeserializationConfig config,
+            Annotated annotated, Object instDef)
+        throws JsonMappingException
+    {
+        if (instDef == null) {
+            return null;
+        }
+
+        ValueInstantiator inst;
+        
+        if (instDef instanceof ValueInstantiator) {
+            return (ValueInstantiator) instDef;
+        }
+        if (!(instDef instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+                    +instDef.getClass().getName()
+                    +"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
+        }
+        Class<?> instClass = (Class<?>)instDef;
+        if (instClass == NoClass.class) {
+            return null;
+        }
+        if (!ValueInstantiator.class.isAssignableFrom(instClass)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Class "+instClass.getName()
+                    +"; expected Class<ValueInstantiator>");
+        }
+        HandlerInstantiator hi = config.getHandlerInstantiator();
+        if (hi != null) {
+            inst = hi.valueInstantiatorInstance(config, annotated, instClass);
+            if (inst != null) {
+                return inst;
+            }
+        }
+        return (ValueInstantiator) ClassUtil.createInstance(instClass,
+                config.canOverrideAccessModifiers());
+    }
+    
+    protected void _addDeserializerConstructors
+        (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+         AnnotationIntrospector intr, CreatorCollector creators)
+        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);
+            }
+        }
+
+        String[] ctorPropNames = null;
+        AnnotatedConstructor propertyCtor = null;
+        for (BeanPropertyDefinition propDef : beanDesc.findProperties()) {
+            if (propDef.getConstructorParameter() != null) {
+                AnnotatedParameter param = propDef.getConstructorParameter();
+                AnnotatedWithParams owner = param.getOwner();
+                if (owner instanceof AnnotatedConstructor) {
+                    if (propertyCtor == null) {
+                        propertyCtor = (AnnotatedConstructor) owner;
+                        ctorPropNames = new String[propertyCtor.getParameterCount()];
+                    }
+                    ctorPropNames[param.getIndex()] = propDef.getName();
+                }
+            }
+        }
+
+        for (AnnotatedConstructor ctor : beanDesc.getConstructors()) {
+            int argCount = ctor.getParameterCount();
+            boolean isCreator = intr.hasCreatorAnnotation(ctor) || ctor == propertyCtor;
+            boolean isVisible =  vchecker.isCreatorVisible(ctor);
+            // some single-arg constructors (String, number) are auto-detected
+            if (argCount == 1) {
+                String name = ctor == propertyCtor ? ctorPropNames[0] : null;
+                _handleSingleArgumentConstructor(ctxt, beanDesc, vchecker, intr, creators,
+                        ctor, isCreator, isVisible, name);
+                continue;
+            }
+            if (!isCreator && !isVisible) {
+                continue;
+            }
+            // [JACKSON-541] improved handling a bit so:
+            // 2 or more args; all params must have name annotations
+            // ... or @JacksonInject (or equivalent)
+            /* [JACKSON-711] One more possibility; can have 1 or more injectables, and
+             * exactly one non-annotated parameter: if so, it's still delegating.
+             */
+            AnnotatedParameter nonAnnotatedParam = null;
+            int namedCount = 0;
+            int injectCount = 0;
+            CreatorProperty[] properties = new CreatorProperty[argCount];
+            for (int i = 0; i < argCount; ++i) {
+                AnnotatedParameter param = ctor.getParameter(i);
+                String name = null;
+                if (ctor == propertyCtor) {
+                    name = ctorPropNames[i];
+                }
+                if (name == null) {
+                    PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
+                    name = (pn == null) ? null : pn.getSimpleName();
+                }
+                Object injectId = intr.findInjectableValueId(param);
+                if (name != null && name.length() > 0) {
+                    ++namedCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+                } else if (injectId != null) {
+                    ++injectCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+                } else if (nonAnnotatedParam == null) {
+                    nonAnnotatedParam = param;
+                }
+            }
+
+            // Ok: if named or injectable, we have more work to do
+            if (isCreator || namedCount > 0 || injectCount > 0) {
+                // simple case; everything covered:
+                if ((namedCount + injectCount) == argCount) {
+                    creators.addPropertyCreator(ctor, properties);
+                } else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
+                    // [712] secondary: all but one injectable, one un-annotated (un-named)
+                    creators.addDelegatingCreator(ctor, properties);
+                } else { // otherwise, record the incomplete parameter for later error messaging.
+                    creators.addIncompeteParameter(nonAnnotatedParam);
+                }
+            }
+        }
+    }
+
+    protected boolean _handleSingleArgumentConstructor(DeserializationContext ctxt,
+            BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+            AnnotationIntrospector intr, CreatorCollector creators,
+            AnnotatedConstructor ctor, boolean isCreator, boolean isVisible, String name)
+        throws JsonMappingException
+    {
+        // note: if we do have parameter name, it'll be "property constructor":
+        AnnotatedParameter param = ctor.getParameter(0);
+        if (name == null) {
+            PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
+            name = (pn == null) ? null : pn.getSimpleName();
+        }
+        Object injectId = intr.findInjectableValueId(param);
+    
+        if ((injectId != null) || (name != null && name.length() > 0)) { // property-based
+            // We know there's a name and it's only 1 parameter.
+            CreatorProperty[] properties = new CreatorProperty[1];
+            properties[0] = constructCreatorProperty(ctxt, beanDesc, name, 0, param, injectId);
+            creators.addPropertyCreator(ctor, properties);
+            return true;
+        }
+    
+        // otherwise either 'simple' number, String, or general delegate:
+        Class<?> type = ctor.getRawParameterType(0);
+        if (type == String.class) {
+            if (isCreator || isVisible) {
+                creators.addStringCreator(ctor);
+            }
+            return true;
+        }
+        if (type == int.class || type == Integer.class) {
+            if (isCreator || isVisible) {
+                creators.addIntCreator(ctor);
+            }
+            return true;
+        }
+        if (type == long.class || type == Long.class) {
+            if (isCreator || isVisible) {
+                creators.addLongCreator(ctor);
+            }
+            return true;
+        }
+        if (type == double.class || type == Double.class) {
+            if (isCreator || isVisible) {
+                creators.addDoubleCreator(ctor);
+            }
+            return true;
+        }
+    
+        // Delegating Creator ok iff it has @JsonCreator (etc)
+        if (isCreator) {
+            creators.addDelegatingCreator(ctor, null);
+            return true;
+        }
+        return false;
+    }
+
+    protected void _addDeserializerFactoryMethods
+        (DeserializationContext ctxt, BeanDescription beanDesc, VisibilityChecker<?> vchecker,
+         AnnotationIntrospector intr, CreatorCollector creators)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
+            boolean isCreator = intr.hasCreatorAnnotation(factory);
+            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;
+            }
+            // some single-arg factory methods (String, number) are auto-detected
+            if (argCount == 1) {
+                AnnotatedParameter param = factory.getParameter(0);
+                PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
+                String name = (pn == null) ? null : pn.getSimpleName();
+                Object injectId = intr.findInjectableValueId(param);
+
+                if ((injectId == null) && (name == null || name.length() == 0)) { // not property based
+                    _handleSingleArgumentFactory(config, beanDesc, vchecker, intr, creators,
+                            factory, isCreator);
+                    // otherwise just ignored
+                    continue;
+                }
+                // fall through if there's name
+            } else {
+                // more than 2 args, must be @JsonCreator
+                if (!intr.hasCreatorAnnotation(factory)) {
+                    continue;
+                }
+            }
+            // 1 or more args; all params must have name annotations
+            AnnotatedParameter nonAnnotatedParam = null;            
+            CreatorProperty[] properties = new CreatorProperty[argCount];
+            int namedCount = 0;
+            int injectCount = 0;            
+            for (int i = 0; i < argCount; ++i) {
+                AnnotatedParameter param = factory.getParameter(i);
+                PropertyName pn = (param == null) ? null : intr.findNameForDeserialization(param);
+                String name = (pn == null) ? null : pn.getSimpleName();
+                Object injectId = intr.findInjectableValueId(param);
+                if (name != null && name.length() > 0) {
+                    ++namedCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+                } else if (injectId != null) {
+                    ++injectCount;
+                    properties[i] = constructCreatorProperty(ctxt, beanDesc, name, i, param, injectId);
+                } else if (nonAnnotatedParam == null) {
+                    nonAnnotatedParam = param;
+                }
+            }
+
+            // Ok: if named or injectable, we have more work to do
+            if (isCreator || namedCount > 0 || injectCount > 0) {
+                // simple case; everything covered:
+                if ((namedCount + injectCount) == argCount) {
+                    creators.addPropertyCreator(factory, properties);
+                } else if ((namedCount == 0) && ((injectCount + 1) == argCount)) {
+                    // [712] secondary: all but one injectable, one un-annotated (un-named)
+                    creators.addDelegatingCreator(factory, 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-paramater 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) {
+            if (isCreator || vchecker.isCreatorVisible(factory)) {
+                creators.addStringCreator(factory);
+            }
+            return true;
+        }
+        if (type == int.class || type == Integer.class) {
+            if (isCreator || vchecker.isCreatorVisible(factory)) {
+                creators.addIntCreator(factory);
+            }
+            return true;
+        }
+        if (type == long.class || type == Long.class) {
+            if (isCreator || vchecker.isCreatorVisible(factory)) {
+                creators.addLongCreator(factory);
+            }
+            return true;
+        }
+        if (type == double.class || type == Double.class) {
+            if (isCreator || vchecker.isCreatorVisible(factory)) {
+                creators.addDoubleCreator(factory);
+            }
+            return true;
+        }
+        if (type == boolean.class || type == Boolean.class) {
+            if (isCreator || vchecker.isCreatorVisible(factory)) {
+                creators.addBooleanCreator(factory);
+            }
+            return true;
+        }
+        if (intr.hasCreatorAnnotation(factory)) {
+            creators.addDelegatingCreator(factory, null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Method that will construct a property object that represents
+     * a logical property passed via Creator (constructor or static
+     * factory method)
+     */
+    protected CreatorProperty constructCreatorProperty(DeserializationContext ctxt,
+            BeanDescription beanDesc, String name, int index,
+            AnnotatedParameter param,
+            Object injectableValueId)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        Boolean b = (intr == null) ? null : intr.hasRequiredMarker(param);
+        // how to default? Other code assumes missing value means 'false', so:
+        boolean req = (b == null) ? false : b.booleanValue();
+
+        JavaType t0 = config.getTypeFactory().constructType(param.getParameterType(), beanDesc.bindingsForBeanType());
+        BeanProperty.Std property = new BeanProperty.Std(name, t0,
+                intr.findWrapperName(param),
+                beanDesc.getClassAnnotations(), param, req);
+        JavaType type = resolveType(ctxt, beanDesc, t0, param);
+        if (type != t0) {
+            property = property.withType(type);
+        }
+        // Is there an annotation that specifies exact deserializer?
+        JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, param);
+        // If yes, we are mostly done:
+        type = modifyTypeByAnnotation(ctxt, param, type);
+
+        // Type deserializer: either comes from property (and already resolved)
+        TypeDeserializer typeDeser = (TypeDeserializer) type.getTypeHandler();
+        // or if not, based on type being referenced:
+        if (typeDeser == null) {
+            typeDeser = findTypeDeserializer(config, type);
+        }
+
+        CreatorProperty prop = new CreatorProperty(name, type, property.getWrapperName(),
+                typeDeser, beanDesc.getClassAnnotations(), param, index, injectableValueId,
+                property.isRequired());
+        if (deser != null) {
+            prop = prop.withValueDeserializer(deser);
+        }
+        return prop;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl: array deserializers
+    /**********************************************************
+     */
+        
+    @Override
+    public JsonDeserializer<?> createArrayDeserializer(DeserializationContext ctxt,
+            ArrayType type, final BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        JavaType elemType = type.getContentType();
+        
+        // Very first thing: is deserializer hard-coded for elements?
+        JsonDeserializer<Object> contentDeser = elemType.getValueHandler();
+        // Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
+        TypeDeserializer elemTypeDeser = elemType.getTypeHandler();
+        // but if not, may still be possible to find:
+        if (elemTypeDeser == null) {
+            elemTypeDeser = findTypeDeserializer(config, elemType);
+        }
+        // 23-Nov-2010, tatu: Custom array deserializer?
+        JsonDeserializer<?>  deser = _findCustomArrayDeserializer(type,
+                config, beanDesc, elemTypeDeser, contentDeser);
+        if (deser == null) {
+            if (contentDeser == null) {
+                Class<?> raw = elemType.getRawClass();
+                if (elemType.isPrimitive()) {
+                    return PrimitiveArrayDeserializers.forType(raw);
+                } else if (raw == String.class) {
+                    return StringArrayDeserializer.instance;
+                }
+            }
+            if (deser == null) {
+                deser = new ObjectArrayDeserializer(type, contentDeser, elemTypeDeser);
+            }
+        }
+        // and then new with 2.2: ability to post-process it too (Issue#120)
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deser = mod.modifyArrayDeserializer(config, type, beanDesc, deser);
+            }
+        }
+        return deser;
+    }
+
+    protected JsonDeserializer<?> _findCustomArrayDeserializer(ArrayType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findArrayDeserializer(type, config,
+                    beanDesc, elementTypeDeserializer, elementDeserializer);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl: Collection(-like) deserializers
+    /**********************************************************
+     */
+
+    @Override
+    public JsonDeserializer<?> createCollectionDeserializer(DeserializationContext ctxt,
+            CollectionType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        JavaType contentType = type.getContentType();
+        // Very first thing: is deserializer hard-coded for elements?
+        JsonDeserializer<Object> contentDeser = contentType.getValueHandler();
+        final DeserializationConfig config = ctxt.getConfig();
+
+        // Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
+        TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
+        // but if not, may still be possible to find:
+        if (contentTypeDeser == null) {
+            contentTypeDeser = findTypeDeserializer(config, contentType);
+        }
+
+        // 23-Nov-2010, tatu: Custom deserializer?
+        JsonDeserializer<?> deser = _findCustomCollectionDeserializer(type,
+                config, beanDesc, contentTypeDeser, contentDeser);
+        if (deser == null) {
+            Class<?> collectionClass = type.getRawClass();
+            if (contentDeser == null) { // not defined by annotation
+                // One special type: EnumSet:
+                if (EnumSet.class.isAssignableFrom(collectionClass)) {
+                    deser = new EnumSetDeserializer(contentType, null);
+                }
+            }
+        }
+        
+        /* One twist: if we are being asked to instantiate an interface or
+         * abstract Collection, we need to either find something that implements
+         * the thing, or give up.
+         *
+         * Note that we do NOT try to guess based on secondary interfaces
+         * here; that would probably not work correctly since casts would
+         * fail later on (as the primary type is not the interface we'd
+         * be implementing)
+         */
+        if (deser == null) {
+            if (type.isInterface() || type.isAbstract()) {
+                CollectionType implType = _mapAbstractCollectionType(type, config);
+                if (implType == null) {
+                    throw new IllegalArgumentException("Can not find a deserializer for non-concrete Collection type "+type);
+                }
+                type = implType;
+                // But if so, also need to re-check creators...
+                beanDesc = config.introspectForCreation(type);
+            }
+            ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
+            if (!inst.canCreateUsingDefault()) {
+                // [Issue#161]: No default constructor for ArrayBlockingQueue...
+                if (type.getRawClass() == ArrayBlockingQueue.class) {
+                    return new ArrayBlockingQueueDeserializer(type, contentDeser, contentTypeDeser, inst, null);
+                }
+            }
+            // 13-Dec-2010, tatu: Can use more optimal deserializer if content type is String, so:
+            if (contentType.getRawClass() == String.class) {
+                // no value type deserializer because Strings are one of natural/native types:
+                deser = new StringCollectionDeserializer(type, contentDeser, inst);
+            } else {
+                deser = new CollectionDeserializer(type, contentDeser, contentTypeDeser, inst);
+            }
+        }
+        // and then new with 2.2: ability to post-process it too (Issue#120)
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deser = mod.modifyCollectionDeserializer(config, type, beanDesc, deser);
+            }
+        }
+        return deser;
+    }
+
+    protected CollectionType _mapAbstractCollectionType(JavaType type, DeserializationConfig config)
+    {
+        Class<?> collectionClass = type.getRawClass();
+        collectionClass = _collectionFallbacks.get(collectionClass.getName());
+        if (collectionClass == null) {
+            return null;
+        }
+        return (CollectionType) config.constructSpecializedType(type, collectionClass);
+    }
+
+    protected JsonDeserializer<?> _findCustomCollectionDeserializer(CollectionType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findCollectionDeserializer(type, config, beanDesc,
+                    elementTypeDeserializer, elementDeserializer);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+    
+    // Copied almost verbatim from "createCollectionDeserializer" -- should try to share more code
+    @Override
+    public JsonDeserializer<?> createCollectionLikeDeserializer(DeserializationContext ctxt,
+            CollectionLikeType type, final BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        JavaType contentType = type.getContentType();
+        // Very first thing: is deserializer hard-coded for elements?
+        JsonDeserializer<Object> contentDeser = contentType.getValueHandler();
+        final DeserializationConfig config = ctxt.getConfig();
+
+        // Then optional type info (1.5): if type has been resolved, we may already know type deserializer:
+        TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
+        // but if not, may still be possible to find:
+        if (contentTypeDeser == null) {
+            contentTypeDeser = findTypeDeserializer(config, contentType);
+        }
+        JsonDeserializer<?> deser = _findCustomCollectionLikeDeserializer(type, config, beanDesc,
+                contentTypeDeser, contentDeser);
+        if (deser != null) {
+            // and then new with 2.2: ability to post-process it too (Issue#120)
+            if (_factoryConfig.hasDeserializerModifiers()) {
+                for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                    deser = mod.modifyCollectionLikeDeserializer(config, type, beanDesc, deser);
+                }
+            }
+        }
+        return deser;
+    }
+
+    protected JsonDeserializer<?> _findCustomCollectionLikeDeserializer(CollectionLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findCollectionLikeDeserializer(type, config, beanDesc,
+                    elementTypeDeserializer, elementDeserializer);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl: Map(-like) deserializers
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
+            MapType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        JavaType keyType = type.getKeyType();
+        JavaType contentType = type.getContentType();
+        
+        // First: is there annotation-specified deserializer for values?
+        @SuppressWarnings("unchecked")
+        JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
+        
+        // Ok: need a key deserializer (null indicates 'default' here)
+        KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
+        // Then optional type info (1.5); either attached to type, or resolved separately:
+        TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
+        // but if not, may still be possible to find:
+        if (contentTypeDeser == null) {
+            contentTypeDeser = findTypeDeserializer(config, contentType);
+        }
+
+        // 23-Nov-2010, tatu: Custom deserializer?
+        JsonDeserializer<?> deser = _findCustomMapDeserializer(type, config, beanDesc,
+                keyDes, contentTypeDeser, contentDeser);
+
+        if (deser == null) {
+            // Value handling is identical for all, but EnumMap requires special handling for keys
+            Class<?> mapClass = type.getRawClass();
+            if (EnumMap.class.isAssignableFrom(mapClass)) {
+                Class<?> kt = keyType.getRawClass();
+                if (kt == null || !kt.isEnum()) {
+                    throw new IllegalArgumentException("Can not construct EnumMap; generic (key) type not available");
+                }
+                deser = new EnumMapDeserializer(type, null, contentDeser, contentTypeDeser);
+            }
+
+            // Otherwise, generic handler works ok.
+    
+            /* But there is one more twist: if we are being asked to instantiate
+             * an interface or abstract Map, we need to either find something
+             * that implements the thing, or give up.
+             *
+             * Note that we do NOT try to guess based on secondary interfaces
+             * here; that would probably not work correctly since casts would
+             * fail later on (as the primary type is not the interface we'd
+             * be implementing)
+             */
+            if (deser == null) {
+                if (type.isInterface() || type.isAbstract()) {
+                    @SuppressWarnings("rawtypes")
+                    Class<? extends Map> fallback = _mapFallbacks.get(mapClass.getName());
+                    if (fallback == null) {
+                        throw new IllegalArgumentException("Can not find a deserializer for non-concrete Map type "+type);
+                    }
+                    mapClass = fallback;
+                    type = (MapType) config.constructSpecializedType(type, mapClass);
+                    // But if so, also need to re-check creators...
+                    beanDesc = config.introspectForCreation(type);
+                }
+                ValueInstantiator inst = findValueInstantiator(ctxt, beanDesc);
+                MapDeserializer md = new MapDeserializer(type, inst, keyDes, contentDeser, contentTypeDeser);
+                md.setIgnorableProperties(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()));
+                deser = md;
+            }
+        }
+        // and then new with 2.2: ability to post-process it too (Issue#120)
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deser = mod.modifyMapDeserializer(config, type, beanDesc, deser);
+            }
+        }
+        return deser;
+    }
+
+    // Copied almost verbatim from "createMapDeserializer" -- should try to share more code
+    @Override
+    public JsonDeserializer<?> createMapLikeDeserializer(DeserializationContext ctxt,
+            MapLikeType type, final BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        JavaType keyType = type.getKeyType();
+        JavaType contentType = type.getContentType();
+        final DeserializationConfig config = ctxt.getConfig();
+        
+        // First: is there annotation-specified deserializer for values?
+        @SuppressWarnings("unchecked")
+        JsonDeserializer<Object> contentDeser = (JsonDeserializer<Object>) contentType.getValueHandler();
+        
+        // Ok: need a key deserializer (null indicates 'default' here)
+        KeyDeserializer keyDes = (KeyDeserializer) keyType.getValueHandler();
+        /* !!! 24-Jan-2012, tatu: NOTE: impls MUST use resolve() to find key deserializer!
+        if (keyDes == null) {
+            keyDes = p.findKeyDeserializer(config, keyType, property);
+        }
+        */
+        // Then optional type info (1.5); either attached to type, or resolve separately:
+        TypeDeserializer contentTypeDeser = contentType.getTypeHandler();
+        // but if not, may still be possible to find:
+        if (contentTypeDeser == null) {
+            contentTypeDeser = findTypeDeserializer(config, contentType);
+        }
+        JsonDeserializer<?> deser = _findCustomMapLikeDeserializer(type, config,
+                beanDesc, keyDes, contentTypeDeser, contentDeser);
+        if (deser != null) {
+            // and then new with 2.2: ability to post-process it too (Issue#120)
+            if (_factoryConfig.hasDeserializerModifiers()) {
+                for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                    deser = mod.modifyMapLikeDeserializer(config, type, beanDesc, deser);
+                }
+            }
+        }
+        return deser;
+    }
+
+    protected JsonDeserializer<?> _findCustomMapDeserializer(MapType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findMapDeserializer(type, config, beanDesc,
+                    keyDeserializer, elementTypeDeserializer, elementDeserializer);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+
+    protected JsonDeserializer<?> _findCustomMapLikeDeserializer(MapLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findMapLikeDeserializer(type, config, beanDesc,
+                    keyDeserializer, elementTypeDeserializer, elementDeserializer);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl: Enum deserializers
+    /**********************************************************
+     */
+    
+    /**
+     * Factory method for constructing serializers of {@link Enum} types.
+     */
+    @Override
+    public JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        final Class<?> enumClass = type.getRawClass();
+        // 23-Nov-2010, tatu: Custom deserializer?
+        JsonDeserializer<?> deser = _findCustomEnumDeserializer(enumClass, config, beanDesc);
+        if (deser == null) {
+            // [JACKSON-193] May have @JsonCreator for static factory method:
+            for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
+                if (ctxt.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
+                    int argCount = factory.getParameterCount();
+                    if (argCount == 1) {
+                        Class<?> returnType = factory.getRawReturnType();
+                        // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
+                        if (returnType.isAssignableFrom(enumClass)) {
+                            deser = EnumDeserializer.deserializerForCreator(config, enumClass, factory);
+                            break;
+                        }
+                    }
+                    throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type "
+                            +enumClass.getName()+")");
+                }
+            }
+            // [JACKSON-749] Also, need to consider @JsonValue, if one found
+            if (deser == null) {
+                deser = new EnumDeserializer(constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod()));
+            }
+        }
+
+        // and then new with 2.2: ability to post-process it too (Issue#120)
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deser = mod.modifyEnumDeserializer(config, type, beanDesc, deser);
+            }
+        }
+        return deser;
+    }
+
+    protected JsonDeserializer<?> _findCustomEnumDeserializer(Class<?> type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findEnumDeserializer(type, config, beanDesc);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl: Tree deserializers
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonDeserializer<?> createTreeDeserializer(DeserializationConfig config,
+            JavaType nodeType, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        @SuppressWarnings("unchecked")
+        Class<? extends JsonNode> nodeClass = (Class<? extends JsonNode>) nodeType.getRawClass();
+        // 23-Nov-2010, tatu: Custom deserializer?
+        JsonDeserializer<?> custom = _findCustomTreeNodeDeserializer(nodeClass, config,
+                beanDesc);
+        if (custom != null) {
+            return custom;
+        }
+        return JsonNodeDeserializer.getDeserializer(nodeClass);
+    }
+
+    protected JsonDeserializer<?> _findCustomTreeNodeDeserializer(Class<? extends JsonNode> type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findTreeNodeDeserializer(type, config, beanDesc);
+            if (deser != null) {
+                return deser;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl (partial): type deserializers
+    /**********************************************************
+     */
+
+    @Override
+    public TypeDeserializer findTypeDeserializer(DeserializationConfig config,
+            JavaType baseType)
+        throws JsonMappingException
+    {
+        Class<?> cls = baseType.getRawClass();
+        BeanDescription bean = config.introspectClassAnnotations(cls);
+        AnnotatedClass ac = bean.getClassInfo();
+        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.
+         */
+        Collection<NamedType> subtypes = null;
+        if (b == null) {
+            b = config.getDefaultTyper(baseType);
+            if (b == null) {
+                return null;
+            }
+        } else {
+            subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(ac, config, ai);
+        }
+        // [JACKSON-505]: May need to figure out default implementation, if none found yet
+        // (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()) {
+                b = b.defaultImpl(defaultType.getRawClass());
+            }
+        }
+        return b.buildTypeDeserializer(config, baseType, subtypes);
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializerFactory impl (partial): key deserializers
+    /**********************************************************
+     */
+    
+    @Override
+    public KeyDeserializer createKeyDeserializer(DeserializationContext ctxt,
+            JavaType type)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        KeyDeserializer deser = null;
+        if (_factoryConfig.hasKeyDeserializers()) {
+            BeanDescription beanDesc = config.introspectClassAnnotations(type.getRawClass());
+            for (KeyDeserializers d  : _factoryConfig.keyDeserializers()) {
+                deser = d.findKeyDeserializer(type, config, beanDesc);
+                if (deser != null) {
+                    break;
+                }
+            }
+        }
+        // the only non-standard thing is this:
+        if (deser == null) {
+            if (type.isEnumType()) {
+                return _createEnumKeyDeserializer(ctxt, type);
+            }
+            deser = StdKeyDeserializers.findStringBasedKeyDeserializer(config, type);
+        }
+        
+        // and then new with 2.2: ability to post-process it too (Issue#120)
+        if (deser != null) {
+            if (_factoryConfig.hasDeserializerModifiers()) {
+                for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                    deser = mod.modifyKeyDeserializer(config, type, deser);
+                }
+            }
+        }
+        return deser;
+    }
+
+    private KeyDeserializer _createEnumKeyDeserializer(DeserializationContext ctxt,
+            JavaType type)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        BeanDescription beanDesc = config.introspect(type);
+        JsonDeserializer<?> des = findDeserializerFromAnnotation(ctxt, beanDesc.getClassInfo());
+        if (des != null) {
+            return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, des);
+        }
+        Class<?> enumClass = type.getRawClass();
+        // 23-Nov-2010, tatu: Custom deserializer?
+        JsonDeserializer<?> custom = _findCustomEnumDeserializer(enumClass, config, beanDesc);
+        if (custom != null) {
+            return StdKeyDeserializers.constructDelegatingKeyDeserializer(config, type, des);
+        }
+
+        EnumResolver<?> enumRes = constructEnumResolver(enumClass, config, beanDesc.findJsonValueMethod());
+        // [JACKSON-193] May have @JsonCreator for static factory method:
+        for (AnnotatedMethod factory : beanDesc.getFactoryMethods()) {
+            if (config.getAnnotationIntrospector().hasCreatorAnnotation(factory)) {
+                int argCount = factory.getParameterCount();
+                if (argCount == 1) {
+                    Class<?> returnType = factory.getRawReturnType();
+                    // usually should be class, but may be just plain Enum<?> (for Enum.valueOf()?)
+                    if (returnType.isAssignableFrom(enumClass)) {
+                        // note: mostly copied from 'EnumDeserializer.deserializerForCreator(...)'
+                        if (factory.getGenericParameterType(0) != String.class) {
+                            throw new IllegalArgumentException("Parameter #0 type for factory method ("+factory+") not suitable, must be java.lang.String");
+                        }
+                        if (config.canOverrideAccessModifiers()) {
+                            ClassUtil.checkAndFixAccess(factory.getMember());
+                        }
+                        return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes, factory);
+                    }
+                }
+                throw new IllegalArgumentException("Unsuitable method ("+factory+") decorated with @JsonCreator (for Enum type "
+                        +enumClass.getName()+")");
+            }
+        }
+        // [JACKSON-749] Also, need to consider @JsonValue, if one found
+        return StdKeyDeserializers.constructEnumKeyDeserializer(enumRes);
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Method called to create a type information deserializer for values of
+     * given non-container property, if one is needed.
+     * If not needed (no polymorphic handling configured for property), should return null.
+     *<p>
+     * Note that this method is only called for non-container bean properties,
+     * and not for values in container types or root values (or container properties)
+     *
+     * @param baseType Declared base type of the value to deserializer (actual
+     *    deserializer type will be this type or its subtype)
+     * 
+     * @return Type deserializer to use for given base type, if one is needed; null if not.
+     */
+    public TypeDeserializer findPropertyTypeDeserializer(DeserializationConfig config,
+            JavaType baseType, AnnotatedMember annotated)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector ai = config.getAnnotationIntrospector();
+        TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, annotated, baseType);        
+        // Defaulting: if no annotations on member, check value class
+        if (b == null) {
+            return findTypeDeserializer(config, baseType);
+        }
+        // but if annotations found, may need to resolve subtypes:
+        Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(
+                annotated, config, ai, baseType);
+        return b.buildTypeDeserializer(config, baseType, subtypes);
+    }
+    
+    /**
+     * Method called to find and create a type information deserializer for values of
+     * given container (list, array, map) property, if one is needed.
+     * If not needed (no polymorphic handling configured for property), should return null.
+     *<p>
+     * Note that this method is only called for container bean properties,
+     * and not for values in container types or root values (or non-container properties)
+     * 
+     * @param containerType Type of property; must be a container type
+     * @param propertyEntity Field or method that contains container property
+     */    
+    public TypeDeserializer findPropertyContentTypeDeserializer(DeserializationConfig config,
+            JavaType containerType, AnnotatedMember propertyEntity)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector ai = config.getAnnotationIntrospector();
+        TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, propertyEntity, containerType);        
+        JavaType contentType = containerType.getContentType();
+        // Defaulting: if no annotations on member, check class
+        if (b == null) {
+            return findTypeDeserializer(config, contentType);
+        }
+        // but if annotations found, may need to resolve subtypes:
+        Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(
+                propertyEntity, config, ai, contentType);
+        return b.buildTypeDeserializer(config, contentType, subtypes);
+    }
+
+    /**
+     * Helper method called to find one of default serializers for "well-known"
+     * platform types: JDK-provided types, and small number of public Jackson
+     * API types.
+     * 
+     * @since 2.2
+     */
+    public JsonDeserializer<?> findDefaultDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        Class<?> rawType = type.getRawClass();
+        String clsName = rawType.getName();
+        if (rawType.isPrimitive() || clsName.startsWith("java.")) {
+            // Object ("untyped"), String equivalents:
+            if (rawType == CLASS_OBJECT) {
+                return UntypedObjectDeserializer.instance;
+            }
+            if (rawType == CLASS_STRING || rawType == CLASS_CHAR_BUFFER) {
+                return StringDeserializer.instance;
+            }
+            if (rawType == CLASS_ITERABLE) {
+                // [Issue#199]: Can and should 'upgrade' to a Collection type:
+                TypeFactory tf = ctxt.getTypeFactory();
+                JavaType elemType = (type.containedTypeCount() > 0) ? type.containedType(0) : TypeFactory.unknownType();
+                CollectionType ct = tf.constructCollectionType(Collection.class, elemType);
+                // Should we re-introspect beanDesc? For now let's not...
+                return createCollectionDeserializer(ctxt, ct, beanDesc);
+            }
+            // Primitives/wrappers, other Numbers:
+            JsonDeserializer<?> deser = NumberDeserializers.find(rawType, clsName);
+            if (deser == null) {
+                deser = DateDeserializers.find(rawType, clsName);
+                if (deser == null) {
+                    deser = JdkDeserializers.find(rawType, clsName);
+                }
+            }
+            return deser;
+        }
+        if (clsName.startsWith("com.fasterxml.")) {
+            // and a few Jackson types as well:
+            return JacksonDeserializers.find(rawType);
+        }
+        return null;
+    }
+
+    
+    /*
+    /**********************************************************
+    /* Helper methods, value/content/key type introspection
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method called to check if a class or method
+     * has annotation that tells which class to use for deserialization.
+     * Returns null if no such annotation found.
+     */
+    protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
+            Annotated ann)
+        throws JsonMappingException
+    {
+        Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
+        if (deserDef == null) {
+            return null;
+        }
+        return ctxt.deserializerInstance(ann, deserDef);
+    }
+
+    /**
+     * Method called to see if given method has annotations that indicate
+     * a more specific type than what the argument specifies.
+     * If annotations are present, they must specify compatible Class;
+     * instance of which can be assigned using the method. This means
+     * that the Class has to be raw class of type, or its sub-class
+     * (or, implementing class if original Class instance is an interface).
+     *
+     * @param a Method or field that the type is associated with
+     * @param type Type of field, or the setter argument
+     *
+     * @return Original type if no annotations are present; or a more
+     *   specific type derived from it if type annotation(s) was found
+     *
+     * @throws JsonMappingException if invalid annotation is found
+     */
+    @SuppressWarnings({ "unchecked" })
+    protected <T extends JavaType> T modifyTypeByAnnotation(DeserializationContext ctxt,
+            Annotated a, T type)
+        throws JsonMappingException
+    {
+        // first: let's check class for the instance itself:
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        Class<?> subclass = intr.findDeserializationType(a, type);
+        if (subclass != null) {
+            try {
+                type = (T) type.narrowBy(subclass);
+            } catch (IllegalArgumentException iae) {
+                throw new JsonMappingException("Failed to narrow type "+type+" with concrete-type annotation (value "+subclass.getName()+"), method '"+a.getName()+"': "+iae.getMessage(), null, iae);
+            }
+        }
+
+        // then key class
+        if (type.isContainerType()) {
+            Class<?> keyClass = intr.findDeserializationKeyType(a, type.getKeyType());
+            if (keyClass != null) {
+                // illegal to use on non-Maps
+                if (!(type instanceof MapLikeType)) {
+                    throw new JsonMappingException("Illegal key-type annotation: type "+type+" is not a Map(-like) type");
+                }
+                try {
+                    type = (T) ((MapLikeType) type).narrowKey(keyClass);
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException("Failed to narrow key type "+type+" with key-type annotation ("+keyClass.getName()+"): "+iae.getMessage(), null, iae);
+                }
+            }
+            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
+                }
+            }            
+           
+           // and finally content class; only applicable to structured types
+           Class<?> cc = intr.findDeserializationContentType(a, type.getContentType());
+           if (cc != null) {
+               try {
+                   type = (T) type.narrowContentsBy(cc);
+               } catch (IllegalArgumentException iae) {
+                   throw new JsonMappingException("Failed to narrow content type "+type+" with content-type annotation ("+cc.getName()+"): "+iae.getMessage(), null, iae);
+               }
+           }
+           // ... as well as deserializer for contents:
+           JavaType contentType = type.getContentType();
+           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);
+                }
+            }
+        }
+        return type;
+    }
+    
+    /**
+     * Helper method used to resolve method return types and field
+     * types. The main trick here is that the containing bean may
+     * have type variable binding information (when deserializing
+     * using generic type passed as type reference), which is
+     * needed in some cases.
+     */
+    protected JavaType resolveType(DeserializationContext ctxt,
+            BeanDescription beanDesc, JavaType type, AnnotatedMember member)
+        throws JsonMappingException
+    {
+        // [JACKSON-154]: Also need to handle keyUsing, contentUsing
+        if (type.isContainerType()) {
+            AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+            JavaType keyType = type.getKeyType();
+            if (keyType != null) {
+                Object kdDef = intr.findKeyDeserializer(member);
+                KeyDeserializer kd = ctxt.keyDeserializerInstance(member, kdDef);
+                if (kd != null) {
+                    type = ((MapLikeType) type).withKeyValueHandler(kd);
+                    keyType = type.getKeyType(); // just in case it's used below
+                }
+            }
+            // and all container types have content types...
+            Object cdDef = intr.findContentDeserializer(member);
+            JsonDeserializer<?> cd = ctxt.deserializerInstance(member, cdDef);
+            if (cd != null) {
+                type = type.withContentValueHandler(cd);
+            }
+            /* 04-Feb-2010, tatu: Need to figure out JAXB annotations that indicate type
+             *    information to use for polymorphic members; and specifically types for
+             *    collection values (contents).
+             *    ... but only applies to members (fields, methods), not classes
+             */
+            if (member instanceof AnnotatedMember) {
+            	TypeDeserializer contentTypeDeser = findPropertyContentTypeDeserializer(
+            	        ctxt.getConfig(), type, (AnnotatedMember) member);            	
+            	if (contentTypeDeser != null) {
+            	    type = type.withContentTypeHandler(contentTypeDeser);
+            	}
+            }
+        }
+        TypeDeserializer valueTypeDeser;
+
+        if (member instanceof AnnotatedMember) { // JAXB allows per-property annotations
+            valueTypeDeser = findPropertyTypeDeserializer(ctxt.getConfig(),
+                    type, (AnnotatedMember) member);
+        } else { // classes just have Jackson annotations
+            // probably only occurs if 'property' is null anyway
+            valueTypeDeser = findTypeDeserializer(ctxt.getConfig(), type);
+        }
+    	if (valueTypeDeser != null) {
+            type = type.withTypeHandler(valueTypeDeser);
+    	}
+    	return type;
+    }
+    
+    protected EnumResolver<?> constructEnumResolver(Class<?> enumClass,
+            DeserializationConfig config, AnnotatedMethod jsonValueMethod)
+    {
+        if (jsonValueMethod != null) {
+            Method accessor = jsonValueMethod.getAnnotated();
+            if (config.canOverrideAccessModifiers()) {
+                ClassUtil.checkAndFixAccess(accessor);
+            }
+            return EnumResolver.constructUnsafeUsingMethod(enumClass, accessor);
+        }
+        // [JACKSON-212]: may need to use Enum.toString()
+        if (config.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)) {
+            return EnumResolver.constructUnsafeUsingToString(enumClass);
+        }
+        return EnumResolver.constructUnsafe(enumClass, config.getAnnotationIntrospector());
+    }
+
+    protected AnnotatedMethod _findJsonValueFor(DeserializationConfig config, JavaType enumType)
+    {
+        if (enumType == null) {
+            return null;
+        }
+        BeanDescription beanDesc = config.introspect(enumType);
+        return beanDesc.findJsonValueMethod();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
new file mode 100644
index 0000000..787133d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializer.java
@@ -0,0 +1,802 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.*;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Deserializer class that can deserialize instances of
+ * arbitrary bean objects, usually from JSON Object structs,
+ * but possibly also from simple types like String values.
+ */
+public class BeanDeserializer
+    extends BeanDeserializerBase
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, initialization
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by {@link BeanDeserializerBuilder}.
+     */
+    public BeanDeserializer(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc,
+            BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+            HashSet<String> ignorableProps, boolean ignoreAllUnknown,
+            boolean hasViews)
+    {
+        super(builder, beanDesc, properties, backRefs,
+                ignorableProps, ignoreAllUnknown, hasViews);
+    }
+
+    /**
+     * Copy-constructor that can be used by sub-classes to allow
+     * copy-on-write style copying of settings of an existing instance.
+     */
+    protected BeanDeserializer(BeanDeserializerBase src) {
+        super(src, src._ignoreAllUnknown);
+    }
+
+    protected BeanDeserializer(BeanDeserializerBase src, boolean ignoreAllUnknown) {
+        super(src, ignoreAllUnknown);
+    }
+    
+    protected BeanDeserializer(BeanDeserializerBase src, NameTransformer unwrapper) {
+        super(src, unwrapper);
+    }
+
+    public BeanDeserializer(BeanDeserializerBase src, ObjectIdReader oir) {
+        super(src, oir);
+    }
+
+    public BeanDeserializer(BeanDeserializerBase src, HashSet<String> ignorableProps) {
+        super(src, ignorableProps);
+    }
+    
+    @Override
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    {
+        /* 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);
+    }
+
+    @Override
+    public BeanDeserializer withObjectIdReader(ObjectIdReader oir) {
+        return new BeanDeserializer(this, oir);
+    }
+
+    @Override
+    public BeanDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+        return new BeanDeserializer(this, ignorableProps);
+    }
+
+    @Override
+    protected BeanDeserializerBase asArrayDeserializer() {
+        SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
+        return new BeanAsArrayDeserializer(this, props);
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer implementation
+    /**********************************************************
+     */
+
+    /**
+     * Main deserialization method for bean-based objects (POJOs).
+     */
+    @Override
+    public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        // common case first:
+        if (t == JsonToken.START_OBJECT) {
+            if (_vanillaProcessing) {
+                return vanillaDeserialize(jp, ctxt, jp.nextToken());
+            }
+            jp.nextToken();
+            if (_objectIdReader != null) {
+                return deserializeWithObjectId(jp, ctxt);
+            }
+            return deserializeFromObject(jp, ctxt);
+        }
+        return _deserializeOther(jp, ctxt, t);
+    }
+
+    private final Object _deserializeOther(JsonParser jp, DeserializationContext ctxt,
+            JsonToken t)
+        throws IOException, JsonProcessingException
+    {
+        if (t == null) {
+            return _missingToken(jp, ctxt);
+        }
+        // and then others, generally requiring use of @JsonCreator
+        switch (t) {
+        case VALUE_STRING:
+            return deserializeFromString(jp, ctxt);
+        case VALUE_NUMBER_INT:
+            return deserializeFromNumber(jp, ctxt);
+        case VALUE_NUMBER_FLOAT:
+	    return deserializeFromDouble(jp, ctxt);
+        case VALUE_EMBEDDED_OBJECT:
+            return jp.getEmbeddedObject();
+        case VALUE_TRUE:
+        case VALUE_FALSE:
+            return deserializeFromBoolean(jp, ctxt);
+        case START_ARRAY:
+            // these only work if there's a (delegating) creator...
+            return deserializeFromArray(jp, ctxt);
+        case FIELD_NAME:
+        case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
+            if (_vanillaProcessing) {
+                return vanillaDeserialize(jp, ctxt, t);
+            }
+            if (_objectIdReader != null) {
+                return deserializeWithObjectId(jp, ctxt);
+            }
+            return deserializeFromObject(jp, ctxt);
+        default:
+            throw ctxt.mappingException(getBeanClass());
+        }
+    }
+
+    protected Object _missingToken(JsonParser jp, DeserializationContext ctxt)
+        throws JsonProcessingException
+    {
+        throw ctxt.endOfInputException(getBeanClass());
+    }
+    
+    /**
+     * Secondary deserialization method, called in cases where POJO
+     * instance is created as part of deserialization, potentially
+     * after collecting some or all of the properties to set.
+     */
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+        if (_unwrappedPropertyHandler != null) {
+            return deserializeWithUnwrapped(jp, ctxt, bean);
+        }
+        if (_externalTypeIdHandler != null) {
+            return deserializeWithExternalTypeId(jp, ctxt, bean);
+        }
+        JsonToken t = jp.getCurrentToken();
+        // 23-Mar-2010, tatu: In some cases, we start with full JSON object too...
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        if (_needViewProcesing) {
+            Class<?> view = ctxt.getActiveView();
+            if (view != null) {
+                return deserializeWithView(jp, ctxt, bean, view);
+            }
+        }
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            
+            if (prop != null) { // normal case
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);
+            }
+        }
+        return bean;
+    }
+    
+    /*
+    /**********************************************************
+    /* Concrete deserialization methods
+    /**********************************************************
+     */
+
+    /**
+     * Streamlined version that is only used when no "special"
+     * features are enabled.
+     */
+    private final Object vanillaDeserialize(JsonParser jp,
+    		DeserializationContext ctxt, JsonToken t)
+        throws IOException, JsonProcessingException
+    {
+        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+            } else {
+                handleUnknownVanilla(jp, ctxt, bean, propName);
+            }
+        }
+        return bean;
+    }
+
+    /**
+     * General version used when handling needs more advanced
+     * features.
+     */
+    @Override
+    public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_nonStandardCreation) {
+            if (_unwrappedPropertyHandler != null) {
+                return deserializeWithUnwrapped(jp, ctxt);
+            }
+            if (_externalTypeIdHandler != null) {
+                return deserializeWithExternalTypeId(jp, ctxt);
+            }
+            return deserializeFromObjectUsingNonDefault(jp, ctxt);
+        }
+        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+        if (_needViewProcesing) {
+            Class<?> view = ctxt.getActiveView();
+            if (view != null) {
+                return deserializeWithView(jp, ctxt, bean, view);
+            }
+        }
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);         
+            }
+        }
+        return bean;
+    }
+
+    /**
+     * Method called to deserialize bean using "property-based creator":
+     * this means that a non-default constructor or factory method is
+     * called, and then possibly other setters. The trick is that
+     * values for creator method need to be buffered, first; and 
+     * due to non-guaranteed ordering possibly some other properties
+     * as well.
+     */
+    @Override
+    protected Object _deserializeUsingPropertyBased(final JsonParser jp, final DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    { 
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+        
+        // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases
+        TokenBuffer unknown = null;
+
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken(); // to point to value
+            // creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                    Object bean;
+                    try {
+                        bean = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        bean = null; // never gets here
+                    }
+                    //  polymorphic?
+                    if (bean.getClass() != _beanType.getRawClass()) {
+                        return handlePolymorphic(jp, ctxt, bean, unknown);
+                    }
+                    if (unknown != null) { // nope, just extra unknown stuff...
+                        bean = handleUnknownProperties(ctxt, bean, unknown);
+                    }
+                    // or just clean?
+                    return deserialize(jp, ctxt, bean);
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+                continue;
+            }
+            // As per [JACKSON-313], things marked as ignorable should not be
+            // passed to any setter
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // "any property"?
+            if (_anySetter != null) {
+                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt));
+                continue;
+            }
+            // Ok then, let's collect the whole field; name and value
+            if (unknown == null) {
+                unknown = new TokenBuffer(jp.getCodec());
+            }
+            unknown.writeFieldName(propName);
+            unknown.copyCurrentStructure(jp);
+        }
+
+        // We hit END_OBJECT, so:
+        Object bean;
+        try {
+            bean =  creator.build(ctxt, buffer);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            bean = null; // never gets here
+        }
+        if (unknown != null) {
+            // polymorphic?
+            if (bean.getClass() != _beanType.getRawClass()) {
+                return handlePolymorphic(null, ctxt, bean, unknown);
+            }
+            // no, just some extra unknown properties
+            return handleUnknownProperties(ctxt, bean, unknown);
+        }
+        return bean;
+    }
+
+    /*
+    /**********************************************************
+    /* Deserializing when we have to consider an active View
+    /**********************************************************
+     */
+    
+    protected final Object deserializeWithView(JsonParser jp, DeserializationContext ctxt,
+            Object bean, Class<?> activeView)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                if (!prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);
+            }
+        }
+        return bean;
+    }
+    
+    /*
+    /**********************************************************
+    /* Handling for cases where we have "unwrapped" values
+    /**********************************************************
+     */
+
+    /**
+     * Method called when there are declared "unwrapped" properties
+     * which need special handling
+     */
+    protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_propertyBasedCreator != null) {
+            return deserializeUsingPropertyBasedWithUnwrapped(jp, ctxt);
+        }
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
+
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            // ignorable things should be ignored
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but... others should be passed to unwrapped property deserializers
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // how about any setter? We'll get copies but...
+            if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+        }
+        tokens.writeEndObject();
+        _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+        return bean;
+    }    
+
+    protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            jp.nextToken();
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but... others should be passed to unwrapped property deserializers
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // how about any setter? We'll get copies but...
+            if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+            }
+        }
+        tokens.writeEndObject();
+        _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+        return bean;
+    }
+
+    @SuppressWarnings("resource")
+    protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken(); // to point to value
+            // creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    t = jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                    Object bean;
+                    try {
+                        bean = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        continue; // never gets here
+                    }
+                    // if so, need to copy all remaining tokens into buffer
+                    while (t == JsonToken.FIELD_NAME) {
+                        jp.nextToken(); // to skip name
+                        tokens.copyCurrentStructure(jp);
+                        t = jp.nextToken();
+                    }
+                    tokens.writeEndObject();
+                    if (bean.getClass() != _beanType.getRawClass()) {
+                        // !!! 08-Jul-2011, tatu: Could probably support; but for now
+                        //   it's too complicated, so bail out
+                        tokens.close();
+                        throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+                    }
+                    return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // "any property"?
+            if (_anySetter != null) {
+                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt));
+            }
+        }
+
+        // We hit END_OBJECT, so:
+        Object bean;
+        try {
+            bean =  creator.build(ctxt, buffer);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            return null; // never gets here
+        }
+        return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+    }
+
+    /*
+    /**********************************************************
+    /* Handling for cases where we have property/-ies with
+    /* external type id
+    /**********************************************************
+     */
+    
+    protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_propertyBasedCreator != null) {
+            return deserializeUsingPropertyBasedWithExternalTypeId(jp, ctxt);
+        }
+        return deserializeWithExternalTypeId(jp, ctxt, _valueInstantiator.createUsingDefault(ctxt));
+    }
+    
+    protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt,
+            Object bean)
+        throws IOException, JsonProcessingException
+    {
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        final ExternalTypeHandler ext = _externalTypeIdHandler.start();
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                // [JACKSON-831]: may have property AND be used as external type id:
+                if (jp.getCurrentToken().isScalarValue()) {
+                    ext.handleTypePropertyValue(jp, ctxt, propName, bean);
+                }
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            // ignorable things should be ignored
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but others are likely to be part of external type id thingy...
+            if (ext.handlePropertyValue(jp, ctxt, propName, bean)) {
+                continue;
+            }
+            // if not, the usual fallback handling:
+            if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            // Unknown: let's call handler method
+            handleUnknownProperty(jp, ctxt, bean, propName);         
+        }
+        // and when we get this far, let's try finalizing the deal:
+        return ext.complete(jp, ctxt, bean);
+    }
+
+    @SuppressWarnings("resource")
+    protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final ExternalTypeHandler ext = _externalTypeIdHandler.start();
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken(); // to point to value
+            // creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // first: let's check to see if this might be part of value with external type id:
+                if (ext.handlePropertyValue(jp, ctxt, propName, buffer)) {
+                    ;
+                } else {
+                    // Last creator property to set?
+                    Object value = creatorProp.deserialize(jp, ctxt);
+                    if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                        t = jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                        Object bean;
+                        try {
+                            bean = creator.build(ctxt, buffer);
+                        } catch (Exception e) {
+                            wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                            continue; // never gets here
+                        }
+                        // if so, need to copy all remaining tokens into buffer
+                        while (t == JsonToken.FIELD_NAME) {
+                            jp.nextToken(); // to skip name
+                            tokens.copyCurrentStructure(jp);
+                            t = jp.nextToken();
+                        }
+                        if (bean.getClass() != _beanType.getRawClass()) {
+                            // !!! 08-Jul-2011, tatu: Could probably support; but for now
+                            //   it's too complicated, so bail out
+                            throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+                        }
+                        return ext.complete(jp, ctxt, bean);
+                    }
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+                continue;
+            }
+            // external type id (or property that depends on it)?
+            if (ext.handlePropertyValue(jp, ctxt, propName, null)) {
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // "any property"?
+            if (_anySetter != null) {
+                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt));
+            }
+        }
+
+        // We hit END_OBJECT; resolve the pieces:
+        try {
+            return ext.complete(jp, ctxt, buffer, creator);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            return null; // never gets here
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
new file mode 100644
index 0000000..7f23a17
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBase.java
@@ -0,0 +1,1370 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.*;
+import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
+import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.ClassKey;
+import com.fasterxml.jackson.databind.util.*;
+
+/**
+ * Base class for <code>BeanDeserializer</code>.
+ */
+public abstract class BeanDeserializerBase
+    extends StdDeserializer<Object>
+    implements ContextualDeserializer, ResolvableDeserializer,
+        java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = -2038793552422727904L;
+
+    /*
+    /**********************************************************
+    /* Information regarding type being deserialized
+    /**********************************************************
+     */
+
+    /**
+     * 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;
+
+    /**
+     * Requested shape from bean class annotations.
+     */
+    final protected JsonFormat.Shape _serializationShape;
+    
+    /*
+    /**********************************************************
+    /* Configuration for creating value instance
+    /**********************************************************
+     */
+
+    /**
+     * Object that handles details of constructing initial 
+     * bean value (to which bind data to), unless instance
+     * is passed (via updateValue())
+     */
+    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 bean needs to be instantiated using constructor
+     * or factory method
+     * that takes one or more named properties as argument(s),
+     * this creator is used for instantiation.
+     * This value gets resolved during general resolution.
+     */
+    protected PropertyBasedCreator _propertyBasedCreator;
+
+    /**
+     * Flag that is set to mark "non-standard" cases; where either
+     * we use one of non-default creators, or there are unwrapped
+     * values to consider.
+     */
+    protected boolean _nonStandardCreation;
+
+    /**
+     * Flag that indicates that no "special features" whatsoever
+     * are enabled, so the simplest processing is possible.
+     */
+    protected boolean _vanillaProcessing;
+
+    /*
+    /**********************************************************
+    /* Property information, setters
+    /**********************************************************
+     */
+
+    /**
+     * Mapping of property names to properties, built when all properties
+     * to use have been successfully resolved.
+     */
+    final protected BeanPropertyMap _beanProperties;
+    
+    /**
+     * List of {@link ValueInjector}s, if any injectable values are
+     * expected by the bean; otherwise null.
+     * This includes injectors used for injecting values via setters
+     * and fields, but not ones passed through constructor parameters.
+     */
+    final protected ValueInjector[] _injectables;
+    
+    /**
+     * Fallback setter used for handling any properties that are not
+     * mapped to regular setters. If setter is not null, it will be
+     * called once for each such property.
+     */
+    protected SettableAnyProperty _anySetter;
+
+    /**
+     * In addition to properties that are set, we will also keep
+     * track of recognized but ignorable properties: these will
+     * be skipped without errors or warnings.
+     */
+    final protected HashSet<String> _ignorableProps;
+
+    /**
+     * Flag that can be set to ignore and skip unknown properties.
+     * If set, will not throw an exception for unknown properties.
+     */
+    final protected boolean _ignoreAllUnknown;
+
+    /**
+     * Flag that indicates that some aspect of deserialization depends
+     * 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
+    /**********************************************************
+     */
+
+    /**
+     * Lazily constructed map used to contain deserializers needed
+     * for polymorphic subtypes.
+     * Note that this is <b>only needed</b> for polymorphic types,
+     * that is, when the actual type is not statically known.
+     * For other types this remains null.
+     */
+    protected transient HashMap<ClassKey, JsonDeserializer<Object>> _subDeserializers;
+
+    /**
+     * If one of properties has "unwrapped" value, we need separate
+     * helper object
+     */
+    protected UnwrappedPropertyHandler _unwrappedPropertyHandler;
+
+    /**
+     * Handler that we need iff any of properties uses external
+     * type id.
+     */
+    protected ExternalTypeHandler _externalTypeIdHandler;
+
+    /**
+     * If an Object Id is to be used for value handled by this
+     * deserializer, this reader is used for handling.
+     */
+    protected final ObjectIdReader _objectIdReader;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, initialization
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used when initially building a deserializer
+     * instance, given a {@link BeanDeserializerBuilder} that
+     * contains configuration.
+     */
+    protected BeanDeserializerBase(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc,
+            BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+            HashSet<String> ignorableProps, boolean ignoreAllUnknown,
+            boolean hasViews)
+    {
+        super(beanDesc.getType());
+
+        AnnotatedClass ac = beanDesc.getClassInfo();
+        _classAnnotations = ac.getAnnotations();       
+        _beanType = beanDesc.getType();
+        _valueInstantiator = builder.getValueInstantiator();
+        
+        _beanProperties = properties;
+        _backRefs = backRefs;
+        _ignorableProps = ignorableProps;
+        _ignoreAllUnknown = ignoreAllUnknown;
+
+        _anySetter = builder.getAnySetter();
+        List<ValueInjector> injectables = builder.getInjectables();
+        _injectables = (injectables == null || injectables.isEmpty()) ? null
+                : injectables.toArray(new ValueInjector[injectables.size()]);
+        _objectIdReader = builder.getObjectIdReader();
+        _nonStandardCreation = (_unwrappedPropertyHandler != null)
+            || _valueInstantiator.canCreateUsingDelegate()
+            || _valueInstantiator.canCreateFromObjectWith()
+            || !_valueInstantiator.canCreateUsingDefault()
+            ;
+
+        // Any transformation we may need to apply?
+        JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+        _serializationShape = (format == null) ? null : format.getShape();
+
+        _needViewProcesing = hasViews;
+        _vanillaProcessing = !_nonStandardCreation
+                && (_injectables == null)
+                && !_needViewProcesing
+                // also, may need to reorder stuff if we expect Object Id:
+                && (_objectIdReader != null)
+                ;
+    }
+
+    protected BeanDeserializerBase(BeanDeserializerBase src)
+    {
+        this(src, src._ignoreAllUnknown);
+    }
+
+    protected BeanDeserializerBase(BeanDeserializerBase src, boolean ignoreAllUnknown)
+    {
+        super(src._beanType);
+        
+        _classAnnotations = src._classAnnotations;
+        _beanType = src._beanType;
+        
+        _valueInstantiator = src._valueInstantiator;
+        _delegateDeserializer = src._delegateDeserializer;
+        _propertyBasedCreator = src._propertyBasedCreator;
+        
+        _beanProperties = src._beanProperties;
+        _backRefs = src._backRefs;
+        _ignorableProps = src._ignorableProps;
+        _ignoreAllUnknown = ignoreAllUnknown;
+        _anySetter = src._anySetter;
+        _injectables = src._injectables;
+        _objectIdReader = src._objectIdReader;
+        
+        _nonStandardCreation = src._nonStandardCreation;
+        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
+        _needViewProcesing = src._needViewProcesing;
+        _serializationShape = src._serializationShape;
+
+        _vanillaProcessing = src._vanillaProcessing;
+    }
+ 
+    protected BeanDeserializerBase(BeanDeserializerBase src, NameTransformer unwrapper)
+    {
+        super(src._beanType);
+
+        _classAnnotations = src._classAnnotations;
+        _beanType = src._beanType;
+        
+        _valueInstantiator = src._valueInstantiator;
+        _delegateDeserializer = src._delegateDeserializer;
+        _propertyBasedCreator = src._propertyBasedCreator;
+
+        _backRefs = src._backRefs;
+        _ignorableProps = src._ignorableProps;
+        _ignoreAllUnknown = (unwrapper != null) || src._ignoreAllUnknown;
+        _anySetter = src._anySetter;
+        _injectables = src._injectables;
+        _objectIdReader = src._objectIdReader;
+
+        _nonStandardCreation = src._nonStandardCreation;
+        UnwrappedPropertyHandler uph = src._unwrappedPropertyHandler;
+
+        if (unwrapper != null) {
+            // delegate further unwraps, if any
+            if (uph != null) { // got handler, delegate
+                uph = uph.renameAll(unwrapper);
+            }
+            // and handle direct unwrapping as well:
+            _beanProperties = src._beanProperties.renameAll(unwrapper);
+        } else {
+            _beanProperties = src._beanProperties;
+        }
+        _unwrappedPropertyHandler = uph;
+        _needViewProcesing = src._needViewProcesing;
+        _serializationShape = src._serializationShape;
+
+        // probably adds a twist, so:
+        _vanillaProcessing = false;
+    }
+
+    public BeanDeserializerBase(BeanDeserializerBase src, ObjectIdReader oir)
+    {
+        super(src._beanType);
+        
+        _classAnnotations = src._classAnnotations;
+        _beanType = src._beanType;
+        
+        _valueInstantiator = src._valueInstantiator;
+        _delegateDeserializer = src._delegateDeserializer;
+        _propertyBasedCreator = src._propertyBasedCreator;
+        
+        _backRefs = src._backRefs;
+        _ignorableProps = src._ignorableProps;
+        _ignoreAllUnknown = src._ignoreAllUnknown;
+        _anySetter = src._anySetter;
+        _injectables = src._injectables;
+        
+        _nonStandardCreation = src._nonStandardCreation;
+        _unwrappedPropertyHandler = src._unwrappedPropertyHandler;
+        _needViewProcesing = src._needViewProcesing;
+        _serializationShape = src._serializationShape;
+
+        _vanillaProcessing = src._vanillaProcessing;
+
+        // then actual changes:
+        _objectIdReader = oir;
+
+        if (oir == null) {
+            _beanProperties = src._beanProperties;
+        } else {
+            /* 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.
+             */
+            ObjectIdValueProperty idProp = new ObjectIdValueProperty(oir, true);
+            _beanProperties = src._beanProperties.withProperty(idProp);
+        }
+    }
+
+    public BeanDeserializerBase(BeanDeserializerBase src, HashSet<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;
+        _serializationShape = src._serializationShape;
+
+        _vanillaProcessing = src._vanillaProcessing;
+        _objectIdReader = src._objectIdReader;
+        _beanProperties = src._beanProperties;
+    }
+    
+    @Override
+    public abstract JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper);
+
+    public abstract BeanDeserializerBase withObjectIdReader(ObjectIdReader oir);
+
+    public abstract BeanDeserializerBase withIgnorableProperties(HashSet<String> ignorableProps);
+
+    /**
+     * 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();
+
+    /*
+    /**********************************************************
+    /* Validation, post-processing
+    /**********************************************************
+     */
+
+    /**
+     * Method called to finalize setup of this deserializer,
+     * after deserializer itself has been registered.
+     * This is needed to handle recursive and transitive dependencies.
+     */
+    @Override
+    public void resolve(DeserializationContext ctxt)
+        throws JsonMappingException
+    {
+        ExternalTypeHandler.Builder extTypes = null;
+        // if ValueInstantiator can use "creator" approach, need to resolve it here...
+        if (_valueInstantiator.canCreateFromObjectWith()) {
+            SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+            // also: need to try to resolve 'external' type ids...
+            for (SettableBeanProperty prop : _propertyBasedCreator.properties()) {
+                if (prop.hasValueTypeDeserializer()) {
+                    TypeDeserializer typeDeser = prop.getValueTypeDeserializer();
+                    if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
+                        if (extTypes == null) {
+                            extTypes = new ExternalTypeHandler.Builder();
+                        }
+                        extTypes.addExternal(prop, typeDeser);
+                    }
+                }
+            }
+        }
+
+        UnwrappedPropertyHandler unwrapped = null;
+
+        for (SettableBeanProperty origProp : _beanProperties) {
+            SettableBeanProperty prop = origProp;
+            // May already have deserializer from annotations, if so, skip:
+            if (!prop.hasValueDeserializer()) {
+                // [Issue#125]: allow use of converters
+                JsonDeserializer<?> deser = findConvertingDeserializer(ctxt, prop);
+                if (deser == null) {
+                    deser = findDeserializer(ctxt, prop.getType(), prop);
+                }
+                prop = prop.withValueDeserializer(deser);
+            } else { // may need contextual version
+                JsonDeserializer<Object> deser = prop.getValueDeserializer();
+                if (deser instanceof ContextualDeserializer) {
+                    JsonDeserializer<?> cd = ((ContextualDeserializer) deser).createContextual(ctxt, prop);
+                    if (cd != deser) {
+                        prop = prop.withValueDeserializer(cd);
+                    }
+                }
+            }
+            // [JACKSON-235]: need to link managed references with matching back references
+            prop = _resolveManagedReferenceProperty(ctxt, prop);
+            // [JACKSON-132]: support unwrapped values (via @JsonUnwrapped)
+            SettableBeanProperty u = _resolveUnwrappedProperty(ctxt, prop);
+            if (u != null) {
+                prop = u;
+                if (unwrapped == null) {
+                    unwrapped = new UnwrappedPropertyHandler();
+                }
+                unwrapped.addProperty(prop);
+                continue;
+            }
+            // [JACKSON-594]: non-static inner classes too:
+            prop = _resolveInnerClassValuedProperty(ctxt, prop);
+            if (prop != origProp) {
+                _beanProperties.replace(prop);
+            }
+            
+            /* one more thing: if this property uses "external property" type inclusion
+             * (see [JACKSON-453]), it needs different handling altogether
+             */
+            if (prop.hasValueTypeDeserializer()) {
+                TypeDeserializer typeDeser = prop.getValueTypeDeserializer();
+                if (typeDeser.getTypeInclusion() == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
+                    if (extTypes == null) {
+                        extTypes = new ExternalTypeHandler.Builder();
+                    }
+                    extTypes.addExternal(prop, typeDeser);
+                    // In fact, remove from list of known properties to simplify later handling
+                    _beanProperties.remove(prop);
+                    continue;
+                }
+            }
+        }
+
+        // "any setter" may also need to be resolved now
+        if (_anySetter != null && !_anySetter.hasValueDeserializer()) {
+            _anySetter = _anySetter.withValueDeserializer(findDeserializer(ctxt,
+                    _anySetter.getType(), _anySetter.getProperty()));
+        }
+
+        // as well as delegate-based constructor:
+        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()'");
+            }
+            AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator();
+            // Need to create a temporary property to allow contextual deserializers:
+            BeanProperty.Std property = new BeanProperty.Std(null,
+                    delegateType, null, _classAnnotations, delegateCreator, false);
+            _delegateDeserializer = findDeserializer(ctxt, delegateType, property);
+        }
+        
+        if (extTypes != null) {
+            _externalTypeIdHandler = extTypes.build();
+            // we consider this non-standard, to offline handling
+            _nonStandardCreation = true;
+        }
+        
+        _unwrappedPropertyHandler = unwrapped;
+        if (unwrapped != null) { // we consider this non-standard, to offline handling
+            _nonStandardCreation = true;
+        }
+
+        // may need to disable vanilla processing, if unwrapped handling was enabled...
+        _vanillaProcessing = _vanillaProcessing && !_nonStandardCreation;
+    }
+
+    /**
+     * 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,
+     * it is container type itself, not key or content type).
+     * 
+     * @since 2.2
+     */
+    protected JsonDeserializer<Object> findConvertingDeserializer(DeserializationContext ctxt,
+            SettableBeanProperty prop)
+        throws JsonMappingException
+    {
+        final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        if (intr != null) {
+            Object convDef = intr.findDeserializationConverter(prop.getMember());
+            if (convDef != null) {
+                Converter<Object,Object> conv = ctxt.converterInstance(prop.getMember(), convDef);
+                JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
+                JsonDeserializer<?> ser = ctxt.findContextualValueDeserializer(delegateType, prop);
+                return new StdDelegatingDeserializer<Object>(conv, delegateType, ser);
+            }
+        }
+        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
+     * to support per-property ObjectIds.
+     * We will also consider Shape transformations (read from Array) at this
+     * point, since it may come from either Class definition or property.
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        ObjectIdReader oir = _objectIdReader;
+        String[] ignorals = null;
+        
+        // First: may have an override for Object Id:
+        final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        final AnnotatedMember accessor = (property == null || intr == null)
+                ? null : property.getMember();
+        if (property != null && intr != null) {
+            ignorals = intr.findPropertiesToIgnore(accessor);
+            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();
+                // Property-based generator is trickier
+                JavaType idType;
+                SettableBeanProperty idProp;
+                ObjectIdGenerator<?> idGen;
+                if (implClass == ObjectIdGenerators.PropertyGenerator.class) {
+                    String propName = objectIdInfo.getPropertyName();
+                    idProp = findProperty(propName);
+                    if (idProp == null) {
+                        throw new IllegalArgumentException("Invalid Object Id definition for "
+                                +getBeanClass().getName()+": can not find property with name '"+propName+"'");
+                    }
+                    idType = idProp.getType();
+                    idGen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
+                } else { // other types need to be simpler
+                    JavaType type = ctxt.constructType(implClass);
+                    idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
+                    idProp = null;
+                    idGen = ctxt.objectIdGeneratorInstance(accessor, objectIdInfo);
+                }
+                JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
+                oir = ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(),
+                		idGen, deser, idProp);
+            }
+        }
+        // either way, need to resolve serializer:
+        BeanDeserializerBase contextual = this;
+        if (oir != null && oir != _objectIdReader) {
+            contextual = contextual.withObjectIdReader(oir);
+        }
+        // And possibly add more properties to ignore
+        if (ignorals != null && ignorals.length != 0) {
+            HashSet<String> newIgnored = ArrayBuilders.setAndArray(contextual._ignorableProps, ignorals);
+            contextual = contextual.withIgnorableProperties(newIgnored);
+        }
+
+        // One more thing: are we asked to serialize POJO as array?
+        JsonFormat.Shape shape = null;
+        if (accessor != null) {
+            JsonFormat.Value format = intr.findFormat((Annotated) accessor);
+
+            if (format != null) {
+                shape = format.getShape();
+            }
+        }
+        if (shape == null) {
+            shape = _serializationShape;
+        }
+        if (shape == JsonFormat.Shape.ARRAY) {
+            contextual = contextual.asArrayDeserializer();
+        }
+        return contextual;
+    }
+
+    
+    /**
+     * Helper method called to see if given property is part of 'managed' property
+     * pair (managed + back reference), and if so, handle resolution details.
+     */
+    protected SettableBeanProperty _resolveManagedReferenceProperty(DeserializationContext ctxt,
+            SettableBeanProperty prop)
+    {
+        String refName = prop.getManagedReferenceName();
+        if (refName == null) {
+            return prop;
+        }
+        JsonDeserializer<?> valueDeser = prop.getValueDeserializer();
+        SettableBeanProperty backProp = null;
+        boolean isContainer = false;
+        if (valueDeser instanceof BeanDeserializerBase) {
+            backProp = ((BeanDeserializerBase) valueDeser).findBackReference(refName);
+        } else if (valueDeser instanceof ContainerDeserializerBase<?>) {
+            JsonDeserializer<?> contentDeser = ((ContainerDeserializerBase<?>) valueDeser).getContentDeserializer();
+            if (!(contentDeser instanceof BeanDeserializerBase)) {
+                String deserName = (contentDeser == null) ? "NULL" : contentDeser.getClass().getName();
+                throw new IllegalArgumentException("Can not handle managed/back reference '"+refName
+                        +"': value deserializer is of type ContainerDeserializerBase, but content type is not handled by a BeanDeserializer "
+                        +" (instead it's of type "+deserName+")");
+            }
+            backProp = ((BeanDeserializerBase) contentDeser).findBackReference(refName);
+            isContainer = true;
+        } else if (valueDeser instanceof AbstractDeserializer) {
+            backProp = ((AbstractDeserializer) valueDeser).findBackReference(refName);
+        } else {
+            throw new IllegalArgumentException("Can not handle managed/back reference '"+refName
+                    +"': type for value deserializer is not BeanDeserializer or ContainerDeserializerBase, but "
+                    +valueDeser.getClass().getName());
+        }
+        if (backProp == null) {
+            throw new IllegalArgumentException("Can not handle managed/back reference '"+refName+"': no back reference property found from type "
+                    +prop.getType());
+        }
+        // also: verify that type is compatible
+        JavaType referredType = _beanType;
+        JavaType backRefType = backProp.getType();
+        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()+")");
+        }
+        return new ManagedReferenceProperty(prop, refName, backProp,
+                _classAnnotations, isContainer);
+    }
+
+    /**
+     * Helper method called to see if given property might be so-called unwrapped
+     * property: these require special handling.
+     */
+    protected SettableBeanProperty _resolveUnwrappedProperty(DeserializationContext ctxt,
+            SettableBeanProperty prop)
+    {
+        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);
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Helper method that will handle gruesome details of dealing with properties
+     * that have non-static inner class as value...
+     */
+    protected SettableBeanProperty _resolveInnerClassValuedProperty(DeserializationContext ctxt,
+            SettableBeanProperty prop)
+    {            
+        /* Should we encounter a property that has non-static inner-class
+         * as value, we need to add some more magic to find the "hidden" constructor...
+         */
+        JsonDeserializer<Object> deser = prop.getValueDeserializer();
+        // ideally wouldn't rely on it being BeanDeserializerBase; but for now it'll have to do
+        if (deser instanceof BeanDeserializerBase) {
+            BeanDeserializerBase bd = (BeanDeserializerBase) deser;
+            ValueInstantiator vi = bd.getValueInstantiator();
+            if (!vi.canCreateUsingDefault()) { // no default constructor
+                Class<?> valueClass = prop.getType().getRawClass();
+                Class<?> enclosing = ClassUtil.getOuterClass(valueClass);
+                // and is inner class of the bean class...
+                if (enclosing != null && enclosing == _beanType.getRawClass()) {
+                    for (Constructor<?> ctor : valueClass.getConstructors()) {
+                        Class<?>[] paramTypes = ctor.getParameterTypes();
+                        if (paramTypes.length == 1 && paramTypes[0] == enclosing) {
+                            if (ctxt.getConfig().canOverrideAccessModifiers()) {
+                                ClassUtil.checkAndFixAccess(ctor);
+                            }
+                            return new InnerClassProperty(prop, ctor);
+                        }
+                    }
+                }
+            }
+        }
+        return prop;
+    }
+
+    /*
+    /**********************************************************
+    /* Public accessors
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isCachable() { return true; }
+
+    /**
+     * Overridden to return true for those instances that are
+     * handling value for which Object Identity handling is enabled
+     * (either via value type or referring property).
+     */
+    @Override
+    public ObjectIdReader getObjectIdReader() {
+        return _objectIdReader;
+    }
+    
+    public boolean hasProperty(String propertyName) {
+        return _beanProperties.find(propertyName) != null;
+    }
+
+    public boolean hasViews() {
+        return _needViewProcesing;
+    }
+    
+    /**
+     * Accessor for checking number of deserialized properties.
+     */
+    public int getPropertyCount() { 
+        return _beanProperties.size();
+    }
+
+    @Override
+    public Collection<Object> getKnownPropertyNames() {
+        ArrayList<Object> names = new ArrayList<Object>();
+        for (SettableBeanProperty prop : _beanProperties) {
+            names.add(prop.getName());
+        }
+        return names;
+    }
+    
+    public final Class<?> getBeanClass() { return _beanType.getRawClass(); }
+
+    @Override public JavaType getValueType() { return _beanType; }
+
+    /**
+     * Accessor for iterating over properties this deserializer uses; with
+     * the exception that properties passed via Creator methods
+     * (specifically, "property-based constructor") are not included,
+     * but can be accessed separate by calling
+     * {@link #creatorProperties}
+     */
+    public Iterator<SettableBeanProperty> properties()
+    {
+        if (_beanProperties == null) {
+            throw new IllegalStateException("Can only call after BeanDeserializer has been resolved");
+        }
+        return _beanProperties.iterator();
+    }
+
+    /**
+     * 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()
+    {
+        if (_propertyBasedCreator == null) {
+            return Collections.<SettableBeanProperty>emptyList().iterator();
+        }
+        return _propertyBasedCreator.properties().iterator();
+    }
+
+    /**
+     * 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)
+    {
+        SettableBeanProperty prop = (_beanProperties == null) ?
+                null : _beanProperties.find(propertyName);
+        if (prop == null && _propertyBasedCreator != null) {
+            prop = _propertyBasedCreator.findCreatorProperty(propertyName);
+        }
+        return prop;
+    }
+    
+    /**
+     * Method needed by {@link BeanDeserializerFactory} to properly link
+     * managed- and back-reference pairs.
+     */
+    public SettableBeanProperty findBackReference(String logicalName)
+    {
+        if (_backRefs == null) {
+            return null;
+        }
+        return _backRefs.get(logicalName);
+    }
+
+    public ValueInstantiator getValueInstantiator() {
+        return _valueInstantiator;
+    }
+
+    /*
+    /**********************************************************
+    /* Mutators
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to replace an existing property with
+     * a modified one.
+     *<p>
+     * NOTE: only ever use this method if you know what you are doing;
+     * incorrect usage can break deserializer.
+     *
+     * @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);
+    }
+
+    /*
+    /**********************************************************
+    /* Partial deserializer implementation
+    /**********************************************************
+     */
+
+    /**
+     * General version used when handling needs more advanced
+     * features.
+     */
+    public abstract Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    @Override
+    public final Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        /* 16-Feb-2012, tatu: ObjectId may be used as well... need to check
+         *    that first
+         */
+        if (_objectIdReader != null) {
+            JsonToken t = jp.getCurrentToken();
+            // should be good enough check; we only care about Strings, integral numbers:
+            if (t != null && t.isScalarValue()) {
+                return deserializeFromObjectId(jp, ctxt);
+            }
+        }
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+
+    // NOTE: currently only used by standard BeanDeserializer (not Builder-based)
+    /**
+     * Alternative deserialization method used when we expect to see Object Id;
+     * if so, we will need to ensure that the Id is seen before anything
+     * else, to ensure that it is available for solving references,
+     * even if JSON itself is not ordered that way. This may require
+     * buffering in some cases, but usually just a simple lookup to ensure
+     * that ordering is correct.
+     */
+    @SuppressWarnings("resource")
+    protected Object deserializeWithObjectId(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        final String idPropName = _objectIdReader.propertyName;
+        // First, the simple case: we point to the Object Id property
+        if (idPropName.equals(jp.getCurrentName())) {
+            return deserializeFromObject(jp, ctxt);
+        }
+        // otherwise need to reorder things
+        TokenBuffer tmpBuffer = new TokenBuffer(jp.getCodec());
+        TokenBuffer mergedBuffer = null;
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // when we match the id property, can start merging
+            if (mergedBuffer == null) {
+                if (idPropName.equals(propName)) {
+                    mergedBuffer = new TokenBuffer(jp.getCodec());
+                    mergedBuffer.writeFieldName(propName);
+                    jp.nextToken();
+                    mergedBuffer.copyCurrentStructure(jp);
+                    mergedBuffer.append(tmpBuffer);
+                    tmpBuffer = null;
+                } else {
+                    tmpBuffer.writeFieldName(propName);
+                    jp.nextToken();
+                    tmpBuffer.copyCurrentStructure(jp);
+                }
+            } else {
+                mergedBuffer.writeFieldName(propName);
+                jp.nextToken();
+                mergedBuffer.copyCurrentStructure(jp);
+            }
+        }
+        // note: we really should get merged buffer (and if not, that is likely error), but
+        // for now let's allow missing case as well. Will be caught be a later stage...
+        TokenBuffer buffer = (mergedBuffer == null) ? tmpBuffer : mergedBuffer;
+        buffer.writeEndObject();
+        // important: need to advance to point to first FIELD_NAME:
+        JsonParser mergedParser = buffer.asParser();
+        mergedParser.nextToken();
+        return deserializeFromObject(mergedParser, ctxt);
+    }
+    
+    /**
+     * Method called in cases where it looks like we got an Object Id
+     * to parse and use as a reference.
+     */
+    protected Object deserializeFromObjectId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        Object id = _objectIdReader.deserializer.deserialize(jp, ctxt);
+        ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
+        // do we have it resolved?
+        Object pojo = roid.item;
+        if (pojo == null) { // not yet; should wait...
+            throw new IllegalStateException("Could not resolve Object Id ["+id+"] (for "
+                    +_beanType+") -- unresolved forward-reference?");
+        }
+        return pojo;
+    }
+
+    protected Object deserializeFromObjectUsingNonDefault(JsonParser jp,
+            DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {        
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_propertyBasedCreator != null) {
+            return _deserializeUsingPropertyBased(jp, ctxt);
+        }
+        // should only occur for abstract types...
+        if (_beanType.isAbstract()) {
+            throw JsonMappingException.from(jp, "Can not instantiate abstract type "+_beanType
+                    +" (need to add/enable type information?)");
+        }
+        throw JsonMappingException.from(jp, "No suitable constructor found for type "
+                +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
+    }
+
+    protected abstract Object _deserializeUsingPropertyBased(final JsonParser jp,
+            final DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    @SuppressWarnings("incomplete-switch")
+    public Object deserializeFromNumber(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // First things first: id Object Id is used, most likely that's it
+        if (_objectIdReader != null) {
+            return deserializeFromObjectId(jp, ctxt);
+        }
+
+        switch (jp.getNumberType()) {
+        case INT:
+            if (_delegateDeserializer != null) {
+                if (!_valueInstantiator.canCreateFromInt()) {
+                    Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                    if (_injectables != null) {
+                        injectValues(ctxt, bean);
+                    }
+                    return bean;
+                }
+            }
+            return _valueInstantiator.createFromInt(ctxt, jp.getIntValue());
+        case LONG:
+            if (_delegateDeserializer != null) {
+                if (!_valueInstantiator.canCreateFromInt()) {
+                    Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                    if (_injectables != null) {
+                        injectValues(ctxt, bean);
+                    }
+                    return bean;
+                }
+            }
+            return _valueInstantiator.createFromLong(ctxt, jp.getLongValue());
+        }
+        // actually, could also be BigInteger, so:
+        if (_delegateDeserializer != null) {
+            Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+            if (_injectables != null) {
+                injectValues(ctxt, bean);
+            }
+            return bean;
+        }
+        throw ctxt.instantiationException(getBeanClass(), "no suitable creator method found to deserialize from JSON integer number");
+    }
+
+    public Object deserializeFromString(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // First things first: id Object Id is used, most likely that's it
+        if (_objectIdReader != null) {
+            return deserializeFromObjectId(jp, ctxt);
+        }
+        
+        /* Bit complicated if we have delegating creator; may need to use it,
+         * or might not...
+         */
+        if (_delegateDeserializer != null) {
+            if (!_valueInstantiator.canCreateFromString()) {
+                Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                if (_injectables != null) {
+                    injectValues(ctxt, bean);
+                }
+                return bean;
+            }
+        }
+        return _valueInstantiator.createFromString(ctxt, jp.getText());
+    }
+
+    /**
+     * Method called to deserialize POJO value from a JSON floating-point
+     * number.
+     */
+    @SuppressWarnings("incomplete-switch")
+    public Object deserializeFromDouble(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getNumberType()) {
+        case FLOAT: // no separate methods for taking float...
+        case DOUBLE:
+            if (_delegateDeserializer != null) {
+                if (!_valueInstantiator.canCreateFromDouble()) {
+                    Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                    if (_injectables != null) {
+                        injectValues(ctxt, bean);
+                    }
+                    return bean;
+                }
+            }
+            return _valueInstantiator.createFromDouble(ctxt, jp.getDoubleValue());
+        }
+        // actually, could also be BigDecimal, so:
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        throw ctxt.instantiationException(getBeanClass(), "no suitable creator method found to deserialize from JSON floating-point number");
+    }
+
+    /**
+     * Method called to deserialize POJO value from a JSON boolean value (true, false)
+     */
+    public Object deserializeFromBoolean(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            if (!_valueInstantiator.canCreateFromBoolean()) {
+                Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                if (_injectables != null) {
+                    injectValues(ctxt, bean);
+                }
+                return bean;
+            }
+        }
+        boolean value = (jp.getCurrentToken() == JsonToken.VALUE_TRUE);
+        return _valueInstantiator.createFromBoolean(ctxt, value);
+    }
+
+    public Object deserializeFromArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            try {
+                Object bean = _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+                if (_injectables != null) {
+                    injectValues(ctxt, bean);
+                }
+                return bean;
+            } catch (Exception e) {
+                wrapInstantiationProblem(e, ctxt);
+            }
+        }
+        throw ctxt.mappingException(getBeanClass());
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable helper methods
+    /**********************************************************
+     */
+
+    protected void injectValues(DeserializationContext ctxt, Object bean)
+            throws IOException, JsonProcessingException
+    {
+        for (ValueInjector injector : _injectables) {
+            injector.inject(ctxt, bean);
+        }
+    }
+    
+    /**
+     * Method called when a JSON property is encountered that has not matching
+     * setter, any-setter or field, and thus can not be assigned.
+     */
+    @Override
+    protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt,
+            Object beanOrClass, String propName)
+        throws IOException, JsonProcessingException
+    {
+        /* 22-Aug-2010, tatu: Caller now mostly checks for ignorable properties, so
+         *    following should not be necessary. However, "handleUnknownProperties()" seems
+         *    to still possibly need it so it is left for now.
+         */
+        // If registered as ignorable, skip
+        if (_ignoreAllUnknown ||
+            (_ignorableProps != null && _ignorableProps.contains(propName))) {
+            jp.skipChildren();
+            return;
+        }
+        /* Otherwise use default handling (call handler(s); if not
+         * handled, throw exception or skip depending on settings)
+         */
+        super.handleUnknownProperty(jp, ctxt, beanOrClass, propName);
+    }
+
+    /**
+     * Method called to handle set of one or more unknown properties,
+     * stored in their entirety in given {@link TokenBuffer}
+     * (as field entries, name and value).
+     */
+    protected Object handleUnknownProperties(DeserializationContext ctxt, Object bean, TokenBuffer unknownTokens)
+        throws IOException, JsonProcessingException
+    {
+        // First: add closing END_OBJECT as marker
+        unknownTokens.writeEndObject();
+        
+        // note: buffer does NOT have starting START_OBJECT
+        JsonParser bufferParser = unknownTokens.asParser();
+        while (bufferParser.nextToken() != JsonToken.END_OBJECT) {
+            String propName = bufferParser.getCurrentName();
+            // Unknown: let's call handler method
+            bufferParser.nextToken();
+            handleUnknownProperty(bufferParser, ctxt, bean, propName);
+        }
+        return bean;
+    }
+
+    /**
+     * Helper method called for an unknown property, when using "vanilla"
+     * processing.
+     */
+    protected void handleUnknownVanilla(JsonParser jp, DeserializationContext ctxt,
+            Object bean, String propName)
+        throws IOException, JsonProcessingException
+    {
+        if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+            jp.skipChildren();
+        } else if (_anySetter != null) {
+            try {
+               // should we consider return type of any setter?
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+            } catch (Exception e) {
+                wrapAndThrow(e, bean, propName, ctxt);
+            }
+        } else {
+            // Unknown: let's call handler method
+            handleUnknownProperty(jp, ctxt, bean, propName);         
+        }
+    }
+
+    /**
+     * Method called in cases where we may have polymorphic deserialization
+     * case: that is, type of Creator-constructed bean is not the type
+     * of deserializer itself. It should be a sub-class or implementation
+     * class; either way, we may have more specific deserializer to use
+     * for handling it.
+     *
+     * @param jp (optional) If not null, parser that has more properties to handle
+     *   (in addition to buffered properties); if null, all properties are passed
+     *   in buffer
+     */
+    protected Object handlePolymorphic(JsonParser jp, DeserializationContext ctxt,                                          
+            Object bean, TokenBuffer unknownTokens)
+        throws IOException, JsonProcessingException
+    {  
+        // First things first: maybe there is a more specific deserializer available?
+        JsonDeserializer<Object> subDeser = _findSubclassDeserializer(ctxt, bean, unknownTokens);
+        if (subDeser != null) {
+            if (unknownTokens != null) {
+                // need to add END_OBJECT marker first
+                unknownTokens.writeEndObject();
+                JsonParser p2 = unknownTokens.asParser();
+                p2.nextToken(); // to get to first data field
+                bean = subDeser.deserialize(p2, ctxt, bean);
+            }
+            // Original parser may also have some leftovers
+            if (jp != null) {
+                bean = subDeser.deserialize(jp, ctxt, bean);
+            }
+            return bean;
+        }
+        // nope; need to use this deserializer. Unknowns we've seen so far?
+        if (unknownTokens != null) {
+            bean = handleUnknownProperties(ctxt, bean, unknownTokens);
+        }
+        // and/or things left to process via main parser?
+        if (jp != null) {
+            bean = deserialize(jp, ctxt, bean);
+        }
+        return bean;
+    }
+    
+    /**
+     * Helper method called to (try to) locate deserializer for given sub-type of
+     * type that this deserializer handles.
+     */
+    protected JsonDeserializer<Object> _findSubclassDeserializer(DeserializationContext ctxt,
+            Object bean, TokenBuffer unknownTokens)
+        throws IOException, JsonProcessingException
+    {  
+        JsonDeserializer<Object> subDeser;
+
+        // First: maybe we have already created sub-type deserializer?
+        synchronized (this) {
+            subDeser = (_subDeserializers == null) ? null : _subDeserializers.get(new ClassKey(bean.getClass()));
+        }
+        if (subDeser != null) {
+            return subDeser;
+        }
+        // If not, maybe we can locate one. First, need provider
+        JavaType type = ctxt.constructType(bean.getClass());
+        /* 30-Jan-2012, tatu: Ideally we would be passing referring
+         *   property; which in theory we could keep track of via
+         *   ResolvableDeserializer (if we absolutely must...).
+         *   But for now, let's not bother.
+         */
+//        subDeser = ctxt.findValueDeserializer(type, _property);
+        subDeser = ctxt.findRootValueDeserializer(type);
+        // Also, need to cache it
+        if (subDeser != null) {
+            synchronized (this) {
+                if (_subDeserializers == null) {
+                    _subDeserializers = new HashMap<ClassKey,JsonDeserializer<Object>>();;
+                }
+                _subDeserializers.put(new ClassKey(bean.getClass()), subDeser);
+            }            
+        }
+        return subDeser;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods for error reporting
+    /**********************************************************
+     */
+
+    /**
+     * Method that will modify caught exception (passed in as argument)
+     * as necessary to include reference information, and to ensure it
+     * is a subtype of {@link IOException}, or an unchecked exception.
+     *<p>
+     * Rules for wrapping and unwrapping are bit complicated; essentially:
+     *<ul>
+     * <li>Errors are to be passed as is (if uncovered via unwrapping)
+     * <li>"Plain" IOExceptions (ones that are not of type
+     *   {@link JsonMappingException} are to be passed as is
+     *</ul>
+     */
+    public void wrapAndThrow(Throwable t, Object bean, String fieldName,
+            DeserializationContext ctxt)
+        throws IOException
+    {
+        // [JACKSON-55] Need to add reference information
+        throw JsonMappingException.wrapWithPath(throwOrReturnThrowable(t, ctxt), bean, fieldName);
+    }
+
+    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
+    {
+        /* 05-Mar-2009, tatu: But one nasty edge is when we get
+         *   StackOverflow: usually due to infinite loop. But that
+         *   often gets hidden within an InvocationTargetException...
+         */
+        while (t instanceof InvocationTargetException && t.getCause() != null) {
+            t = t.getCause();
+        }
+        // Errors to be passed as is
+        if (t instanceof Error) {
+            throw (Error) 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) { // [JACKSON-407] -- allow disabling wrapping for unchecked exceptions
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            }
+        }
+        return t;
+    }
+
+    protected void wrapInstantiationProblem(Throwable t, DeserializationContext ctxt)
+        throws IOException
+    {
+        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;
+        }
+        boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
+        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;
+            }
+        }
+        throw ctxt.instantiationException(_beanType.getRawClass(), 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
new file mode 100644
index 0000000..406c380
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerBuilder.java
@@ -0,0 +1,416 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap;
+import com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty;
+import com.fasterxml.jackson.databind.deser.impl.ObjectIdReader;
+import com.fasterxml.jackson.databind.deser.impl.ValueInjector;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * Builder class used for aggregating deserialization information about
+ * a POJO, in order to build a {@link JsonDeserializer} for deserializing
+ * instances.
+ */
+public class BeanDeserializerBuilder
+{
+    /*
+    /**********************************************************
+    /* General information about POJO
+    /**********************************************************
+     */
+
+    final protected BeanDescription _beanDesc;
+
+    final protected boolean _defaultViewInclusion;
+    
+    /*
+    /**********************************************************
+    /* 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)
+     */
+    protected HashMap<String, SettableBeanProperty> _backRefProperties;
+
+    /**
+     * Set of names of properties that are recognized but are to be ignored for deserialization
+     * purposes (meaning no exception is thrown, value is just skipped).
+     */
+    protected HashSet<String> _ignorableProps;
+    
+    /**
+     * Object that will handle value instantiation for the bean type.
+     */
+    protected ValueInstantiator _valueInstantiator;
+
+    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
+     * called once for each such property.
+     */
+    protected SettableAnyProperty _anySetter;
+
+    /**
+     * Flag that can be set to ignore and skip unknown properties.
+     * If set, will not throw an exception for unknown properties.
+     */
+    protected boolean _ignoreAllUnknown;
+
+    /**
+     * When creating Builder-based deserializers, this indicates
+     * method to call on builder to finalize value.
+     */
+    protected AnnotatedMethod _buildMethod;
+
+    /**
+     * In addition, Builder may have additional configuration
+     */
+    protected JsonPOJOBuilder.Value _builderConfig;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: construction
+    /**********************************************************
+     */
+    
+    public BeanDeserializerBuilder(BeanDescription beanDesc,
+            DeserializationConfig config)
+    { 
+        _beanDesc = beanDesc;
+        _defaultViewInclusion = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
+    }
+
+    /**
+     * Copy constructor for sub-classes to use, when constructing
+     * custom builder instances
+     */
+    protected BeanDeserializerBuilder(BeanDeserializerBuilder src)
+    {
+        _beanDesc = src._beanDesc;
+        _defaultViewInclusion = src._defaultViewInclusion;
+
+        _anySetter = src._anySetter;
+        _ignoreAllUnknown = src._ignoreAllUnknown;
+
+        // let's make copy of properties
+        _properties.putAll(src._properties);
+        _backRefProperties = _copy(src._backRefProperties);
+        // Hmmh. Should we create defensive copies here? For now, not yet
+        _ignorableProps = src._ignorableProps;        
+        _valueInstantiator = src._valueInstantiator;
+        _objectIdReader = src._objectIdReader;
+        
+        _buildMethod = src._buildMethod;
+        _builderConfig = src._builderConfig;
+    }
+
+    private static HashMap<String, SettableBeanProperty> _copy(HashMap<String, SettableBeanProperty> src)
+    {
+        if (src == null) {
+            return null;
+        }
+        return new HashMap<String, SettableBeanProperty>(src);
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: state modification (adders, setters)
+    /**********************************************************
+     */
+
+    /**
+     * Method for adding a new property or replacing a property.
+     */
+    public void addOrReplaceProperty(SettableBeanProperty prop, boolean allowOverride)
+    {
+        _properties.put(prop.getName(), prop);
+    }
+
+    /**
+     * Method to add a property setter. Will ensure that there is no
+     * unexpected override; if one is found will throw a
+     * {@link IllegalArgumentException}.
+     */
+    public void addProperty(SettableBeanProperty prop)
+    {
+        SettableBeanProperty old =  _properties.put(prop.getName(), prop);
+        if (old != null && old != prop) { // should never occur...
+            throw new IllegalArgumentException("Duplicate property '"+prop.getName()+"' for "+_beanDesc.getType());
+        }
+    }
+
+    /**
+     * Method called to add a property that represents so-called back reference;
+     * reference that "points back" to object that has forward reference to
+     * currently built bean.
+     */
+    public void  addBackReferenceProperty(String referenceName, SettableBeanProperty prop)
+    {
+        if (_backRefProperties == null) {
+            _backRefProperties = new HashMap<String, SettableBeanProperty>(4);
+        }
+        _backRefProperties.put(referenceName, prop);
+        // also: if we had property with same name, actually remove it
+        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(String propertyName, JavaType propertyType,
+            Annotations contextAnnotations, AnnotatedMember member,
+            Object valueId)
+    {
+        if (_injectables == null) {
+            _injectables = new ArrayList<ValueInjector>();
+        }
+        _injectables.add(new ValueInjector(propertyName, propertyType,
+                contextAnnotations, member, valueId));
+    }
+    
+    /**
+     * Method that will add property name as one of properties that can
+     * be ignored if not recognized.
+     */
+    public void addIgnorable(String propName)
+    {
+        if (_ignorableProps == null) {
+            _ignorableProps = new HashSet<String>();
+        }
+        _ignorableProps.add(propName);
+    }
+
+    /**
+     * Method called by deserializer factory, when a "creator property"
+     * (something that is passed via constructor- or factory method argument;
+     * instead of setter or field).
+     *<p>
+     * Default implementation does not do anything; we may need to revisit this
+     * decision if these properties need to be available through accessors.
+     * For now, however, we just have to ensure that we don't try to resolve
+     * types that masked setter/field has (see [JACKSON-700] for details).
+     */
+    public void addCreatorProperty(SettableBeanProperty prop)
+    {
+        addProperty(prop);
+    }
+
+    /**
+     * @deprecated since 2.1, override {@link #addCreatorProperty(SettableBeanProperty)} instead.
+     */
+    @Deprecated
+    public void addCreatorProperty(BeanPropertyDefinition propDef)
+    {
+        // do nothing
+    }
+    
+    public void setAnySetter(SettableAnyProperty s)
+    {
+        if (_anySetter != null && s != null) {
+            throw new IllegalStateException("_anySetter already set to non-null");
+        }
+        _anySetter = s;
+    }
+
+    public void setIgnoreUnknownProperties(boolean ignore) {
+        _ignoreAllUnknown = ignore;
+    }
+
+    public void setValueInstantiator(ValueInstantiator inst) {
+        _valueInstantiator = inst;
+    }
+
+    public void setObjectIdReader(ObjectIdReader r) {
+        _objectIdReader = r;
+    }
+
+    public void setPOJOBuilder(AnnotatedMethod buildMethod, JsonPOJOBuilder.Value config) {
+        _buildMethod = buildMethod;
+        _builderConfig = config;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public accessors
+    /**********************************************************
+     */
+    
+    /**
+     * Method that allows accessing all properties that this
+     * builder currently contains.
+     *<p>
+     * Note that properties are returned in order that properties
+     * are ordered (explictly, or by rule), which is the serialization
+     * order.
+     */
+    public Iterator<SettableBeanProperty> getProperties() {
+        return _properties.values().iterator();
+    }
+
+    public SettableBeanProperty findProperty(String propertyName) {
+    	return _properties.get(propertyName);
+    }
+    
+    public boolean hasProperty(String propertyName) {
+        return findProperty(propertyName) != null;
+    }
+    
+    public SettableBeanProperty removeProperty(String name) {
+        return _properties.remove(name);
+    }
+
+    public SettableAnyProperty getAnySetter() {
+        return _anySetter;
+    }
+    
+    public ValueInstantiator getValueInstantiator() {
+        return _valueInstantiator;
+    }
+
+    public List<ValueInjector> getInjectables() {
+        return _injectables;
+    }
+
+    public ObjectIdReader getObjectIdReader() {
+        return _objectIdReader;
+    }
+
+    public AnnotatedMethod getBuildMethod() {
+    	return _buildMethod;
+    }
+
+    public JsonPOJOBuilder.Value getBuilderConfig() {
+        return _builderConfig;
+    }
+    
+    /*
+    /**********************************************************
+    /* Build method(s)
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing a {@link BeanDeserializer}, given all
+     * information collected.
+     *<p>
+     * NOTE: Signature of this method did unfortunately change between Jackson 2.1
+     * and Jackson 2.2
+     */
+    public JsonDeserializer<?> build()
+    {
+        Collection<SettableBeanProperty> props = _properties.values();
+        BeanPropertyMap propertyMap = new BeanPropertyMap(props);
+        propertyMap.assignIndexes();
+
+        // view processing must be enabled if:
+        // (a) fields are not included by default (when deserializing with view), OR
+        // (b) one of properties has view(s) to included in defined
+        boolean anyViews = !_defaultViewInclusion;
+
+        if (!anyViews) {
+            for (SettableBeanProperty prop : props) {
+                if (prop.hasViews()) {
+                    anyViews = true;
+                    break;
+                }
+            }
+        }
+
+        // one more thing: may need to create virtual ObjectId property:
+        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.
+             */
+            ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, true);
+            propertyMap = propertyMap.withProperty(prop);
+        }
+        
+        return new BeanDeserializer(this,
+                _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
+                anyViews);
+    }
+
+    /**
+     * Alternate build method used when we must be using some form of
+     * abstract resolution, usually by using addition Type Id
+     * ("polymorphic deserialization")
+     * 
+     * @since 2.0
+     */
+    public AbstractDeserializer buildAbstract()
+    {
+        return new AbstractDeserializer(this, _beanDesc, _backRefProperties);
+    }
+    
+    /**
+     * Method for constructing a specialized deserializer that uses
+     * additional external Builder object during data binding.
+     */
+    public JsonDeserializer<?> buildBuilderBased(JavaType valueType,
+    		String expBuildMethodName)
+    {
+        // First: validation; must have build method that returns compatible type
+        if (_buildMethod == null) {
+            throw new IllegalArgumentException("Builder class "+_beanDesc.getBeanClass().getName()
+                    +" does not have build method '"+expBuildMethodName+"()'");
+        }
+        // also: type of the method must be compatible
+        Class<?> rawBuildType = _buildMethod.getRawReturnType();
+        if (!valueType.getRawClass().isAssignableFrom(rawBuildType)) {
+            throw new IllegalArgumentException("Build method '"+_buildMethod.getFullName()
+        			+" has bad return type ("+rawBuildType.getName()
+        			+"), not compatible with POJO type ("+valueType.getRawClass().getName()+")");
+        }
+        // And if so, we can try building the deserializer
+        Collection<SettableBeanProperty> props = _properties.values();
+        BeanPropertyMap propertyMap = new BeanPropertyMap(props);
+        propertyMap.assignIndexes();
+
+        boolean anyViews = !_defaultViewInclusion;
+
+        if (!anyViews) {
+            for (SettableBeanProperty prop : props) {
+                if (prop.hasViews()) {
+                    anyViews = true;
+                    break;
+                }
+            }
+        }
+
+        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.
+             */
+            ObjectIdValueProperty prop = new ObjectIdValueProperty(_objectIdReader, true);
+            propertyMap = propertyMap.withProperty(prop);
+        }
+        
+        return new BuilderBasedDeserializer(this,
+                _beanDesc, propertyMap, _backRefProperties, _ignorableProps, _ignoreAllUnknown,
+                anyViews);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
new file mode 100644
index 0000000..8058bb9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java
@@ -0,0 +1,852 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.cfg.DeserializerFactoryConfig;
+import com.fasterxml.jackson.databind.deser.impl.*;
+import com.fasterxml.jackson.databind.deser.std.JdkDeserializers;
+import com.fasterxml.jackson.databind.deser.std.ThrowableDeserializer;
+import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.SimpleBeanPropertyDefinition;
+
+/**
+ * Concrete deserializer factory class that adds full Bean deserializer
+ * construction logic using class introspection.
+ * Note that factories specifically do not implement any form of caching:
+ * aside from configuration they are stateless; caching is implemented
+ * by other components.
+ *<p>
+ * Instances of this class are fully immutable as all configuration is
+ * done by using "fluent factories" (methods that construct new factory
+ * instances with different configuration, instead of modifying instance).
+ */
+public class BeanDeserializerFactory
+    extends BasicDeserializerFactory
+    implements java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 1;
+
+    /**
+     * Signature of <b>Throwable.initCause</b> method.
+     */
+    private final static Class<?>[] INIT_CAUSE_PARAMS = new Class<?>[] { Throwable.class };
+
+    private final static Class<?>[] NO_VIEWS = new Class<?>[0];
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    /**
+     * Globally shareable thread-safe instance which has no additional custom deserializers
+     * registered
+     */
+    public final static BeanDeserializerFactory instance = new BeanDeserializerFactory(
+            new DeserializerFactoryConfig());
+
+    public BeanDeserializerFactory(DeserializerFactoryConfig config) {
+        super(config);
+    }
+    
+    /**
+     * Method used by module registration functionality, to construct a new bean
+     * deserializer factory
+     * with different configuration settings.
+     */
+    @Override
+    public DeserializerFactory withConfig(DeserializerFactoryConfig config)
+    {
+        if (_factoryConfig == config) {
+            return this;
+        }
+        /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor;
+         *    and we pretty much have to here either choose between losing subtype instance
+         *    when registering additional deserializers, or losing deserializers.
+         *    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");
+        }
+        return new BeanDeserializerFactory(config);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overrides for super-class methods used for finding
+    /* custom deserializers
+    /**********************************************************
+     */
+
+    // Note: NOT overriding, superclass has no matching method
+    @SuppressWarnings("unchecked")
+    protected JsonDeserializer<Object> _findCustomBeanDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        for (Deserializers d  : _factoryConfig.deserializers()) {
+            JsonDeserializer<?> deser = d.findBeanDeserializer(type, config, beanDesc);
+            if (deser != null) {
+                return (JsonDeserializer<Object>) deser;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* DeserializerFactory API implementation
+    /**********************************************************
+     */
+
+    /**
+     * Method that {@link DeserializerCache}s call to create a new
+     * deserializer for types other than Collections, Maps, arrays and
+     * enums.
+     */
+    @Override
+    public JsonDeserializer<Object> createBeanDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        // We may also have custom overrides:
+        JsonDeserializer<Object> custom = _findCustomBeanDeserializer(type, config, beanDesc);
+        if (custom != null) {
+            return custom;
+        }
+        /* One more thing to check: do we have an exception type
+         * (Throwable or its sub-classes)? If so, need slightly
+         * different handling.
+         */
+        if (type.isThrowable()) {
+            return buildThrowableDeserializer(ctxt, type, beanDesc);
+        }
+        /* Or, for abstract types, may have alternate means for resolution
+         * (defaulting, materialization)
+         */
+        if (type.isAbstract()) {
+            // [JACKSON-41] (v1.6): Let's make it possible to materialize abstract types.
+            JavaType concreteType = materializeAbstractType(ctxt, type, beanDesc);
+            if (concreteType != null) {
+                /* important: introspect actual implementation (abstract class or
+                 * interface doesn't have constructors, for one)
+                 */
+                beanDesc = config.introspect(concreteType);
+                return buildBeanDeserializer(ctxt, concreteType, beanDesc);
+            }
+        }
+
+        // Otherwise, may want to check handlers for standard types, from superclass:
+        @SuppressWarnings("unchecked")
+        JsonDeserializer<Object> deser = (JsonDeserializer<Object>) findStdDeserializer(ctxt, type, beanDesc);
+        if (deser != null) {
+            return deser;
+        }
+
+        // Otherwise: could the class be a Bean class? If not, bail out
+        if (!isPotentialBeanType(type.getRawClass())) {
+            return null;
+        }
+        // Use generic bean introspection to build deserializer
+        return buildBeanDeserializer(ctxt, type, beanDesc);
+    }
+
+    @Override
+    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.
+     */
+    protected JsonDeserializer<?> findStdDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        // note: we do NOT check for custom deserializers here, caller has already
+        // done that
+        JsonDeserializer<?> deser = findDefaultDeserializer(ctxt, type, beanDesc);
+        if (deser != null) {
+            return deser;
+        }
+        
+        Class<?> cls = type.getRawClass();
+        // [JACKSON-283]: AtomicReference is a rather special type...
+        if (AtomicReference.class.isAssignableFrom(cls)) {
+            // Must find parameterization
+            TypeFactory tf = ctxt.getTypeFactory();
+            JavaType[] params = tf.findTypeParameters(type, AtomicReference.class);
+            JavaType referencedType;
+            if (params == null || params.length < 1) { // untyped (raw)
+                referencedType = TypeFactory.unknownType();
+            } else {
+                referencedType = params[0];
+            }
+            return new JdkDeserializers.AtomicReferenceDeserializer(referencedType);
+        }
+        return findOptionalStdDeserializer(ctxt, type, beanDesc);
+    }
+
+    /**
+     * Overridable method called after checking all other types.
+     * 
+     * @since 2.2
+     */
+    protected JsonDeserializer<?> findOptionalStdDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        return OptionalHandlerFactory.instance.findDeserializer(type, ctxt.getConfig(), beanDesc);
+    }
+    
+    protected JavaType materializeAbstractType(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final JavaType abstractType = beanDesc.getType();
+        // [JACKSON-502]: Now it is possible to have multiple resolvers too,
+        //   as they are registered via module interface.
+        for (AbstractTypeResolver r : _factoryConfig.abstractTypeResolvers()) {
+            JavaType concrete = r.resolveAbstractType(ctxt.getConfig(), abstractType);
+            if (concrete != null) {
+                return concrete;
+            }
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public construction method beyond DeserializerFactory API:
+    /* can be called from outside as well as overridden by
+    /* sub-classes
+    /**********************************************************
+     */
+
+    /**
+     * Method that is to actually build a bean deserializer instance.
+     * All basic sanity checks have been done to know that what we have
+     * may be a valid bean type, and that there are no default simple
+     * deserializers.
+     */
+    @SuppressWarnings("unchecked")
+    public JsonDeserializer<Object> buildBeanDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        // First: check what creators we can use, if any
+        ValueInstantiator valueInstantiator = findValueInstantiator(ctxt, beanDesc);
+        BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);
+        builder.setValueInstantiator(valueInstantiator);
+         // And then setters for deserializing from JSON Object
+        addBeanProps(ctxt, beanDesc, builder);
+        addObjectIdReader(ctxt, beanDesc, builder);
+
+        // managed/back reference fields/setters need special handling... first part
+        addReferenceProperties(ctxt, beanDesc, builder);
+        addInjectables(ctxt, beanDesc, builder);
+        
+        final DeserializationConfig config = ctxt.getConfig();
+        // [JACKSON-440]: update builder now that all information is in?
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                builder = mod.updateBuilder(config, beanDesc, builder);
+            }
+        }
+        JsonDeserializer<?> deserializer;
+
+        /* 19-Mar-2012, tatu: This check used to be done earlier; but we have to defer
+         *   it a bit to collect information on ObjectIdReader, for example.
+         */
+        if (type.isAbstract() && !valueInstantiator.canInstantiate()) {
+            deserializer = builder.buildAbstract();
+        } else {
+            deserializer = builder.build();
+        }
+
+        // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built:
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deserializer = mod.modifyDeserializer(config, beanDesc, deserializer);
+            }
+        }
+        return (JsonDeserializer<Object>) deserializer;
+    }
+    
+    /**
+     * Method for constructing a bean deserializer that uses specified
+     * intermediate Builder for binding data, and construction of the
+     * value instance.
+     * Note that implementation is mostly copied from the regular
+     * BeanDeserializer build method.
+     */
+    @SuppressWarnings("unchecked")
+    protected JsonDeserializer<Object> buildBuilderBasedDeserializer(
+    		DeserializationContext ctxt, JavaType valueType, BeanDescription builderDesc)
+        throws JsonMappingException
+    {
+    	// Creators, anyone? (to create builder itself)
+        ValueInstantiator valueInstantiator = findValueInstantiator(ctxt, builderDesc);
+        final DeserializationConfig config = ctxt.getConfig();
+        BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, builderDesc);
+        builder.setValueInstantiator(valueInstantiator);
+         // And then "with methods" for deserializing from JSON Object
+        addBeanProps(ctxt, builderDesc, builder);
+        addObjectIdReader(ctxt, builderDesc, builder);
+        
+        // managed/back reference fields/setters need special handling... first part
+        addReferenceProperties(ctxt, builderDesc, builder);
+        addInjectables(ctxt, builderDesc, builder);
+
+        JsonPOJOBuilder.Value builderConfig = builderDesc.findPOJOBuilderConfig();
+        final String buildMethodName = (builderConfig == null) ?
+                "build" : builderConfig.buildMethodName;
+        
+        // and lastly, find build method to use:
+        AnnotatedMethod buildMethod = builderDesc.findMethod(buildMethodName, null);
+        if (buildMethod != null) { // note: can't yet throw error; may be given build method
+            if (config.canOverrideAccessModifiers()) {
+            	ClassUtil.checkAndFixAccess(buildMethod.getMember());
+            }
+        }
+        builder.setPOJOBuilder(buildMethod, builderConfig);
+        // this may give us more information...
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                builder = mod.updateBuilder(config, builderDesc, builder);
+            }
+        }
+        JsonDeserializer<?> deserializer = builder.buildBuilderBased(
+        		valueType, buildMethodName);
+
+        // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built:
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deserializer = mod.modifyDeserializer(config, builderDesc, deserializer);
+            }
+        }
+        return (JsonDeserializer<Object>) deserializer;
+    }
+    
+    protected void addObjectIdReader(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder)
+        throws JsonMappingException
+    {
+        ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo();
+        if (objectIdInfo == null) {
+            return;
+        }
+        Class<?> implClass = objectIdInfo.getGeneratorType();
+        JavaType idType;
+    	SettableBeanProperty idProp;
+        ObjectIdGenerator<?> gen;
+
+        // Just one special case: Property-based generator is trickier
+        if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
+            String propName = objectIdInfo.getPropertyName();
+            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+"'");
+            }
+            idType = idProp.getType();
+            gen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
+        } else {
+            JavaType type = ctxt.constructType(implClass);
+            idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
+            idProp = null;
+            gen = ctxt.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo);
+        }
+        // also: unlike with value deserializers, let's just resolve one we need here
+        JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
+        builder.setObjectIdReader(ObjectIdReader.construct(idType,
+                objectIdInfo.getPropertyName(), gen, deser, idProp));
+    }
+    
+    @SuppressWarnings("unchecked")
+    public JsonDeserializer<Object> buildThrowableDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        // first: construct like a regular bean deserializer...
+        BeanDeserializerBuilder builder = constructBeanDeserializerBuilder(ctxt, beanDesc);
+        builder.setValueInstantiator(findValueInstantiator(ctxt, beanDesc));
+
+        addBeanProps(ctxt, beanDesc, builder);
+        // (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).
+         */
+        AnnotatedMethod am = beanDesc.findMethod("initCause", INIT_CAUSE_PARAMS);
+        if (am != null) { // should never be null
+            SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(ctxt.getConfig(), am, "cause");
+            SettableBeanProperty prop = constructSettableProperty(ctxt, beanDesc, propDef,
+                    am.getGenericParameterType(0));
+            if (prop != null) {
+                /* 21-Aug-2011, tatus: We may actually have found 'cause' property
+                 *   to set (with new 1.9 code)... but let's replace it just in case,
+                 *   otherwise can end up with odd errors.
+                 */
+                builder.addOrReplaceProperty(prop, true);
+            }
+        }
+
+        // And also need to ignore "localizedMessage"
+        builder.addIgnorable("localizedMessage");
+        // [JACKSON-794]: JDK 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");
+
+        // [JACKSON-440]: update builder now that all information is in?
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                builder = mod.updateBuilder(config, beanDesc, builder);
+            }
+        }
+        JsonDeserializer<?> deserializer = builder.build();
+        
+        /* At this point it ought to be a BeanDeserializer; if not, must assume
+         * it's some other thing that can handle deserialization ok...
+         */
+        if (deserializer instanceof BeanDeserializer) {
+            deserializer = new ThrowableDeserializer((BeanDeserializer) deserializer);
+        }
+
+        // [JACKSON-440]: may have modifier(s) that wants to modify or replace serializer we just built:
+        if (_factoryConfig.hasDeserializerModifiers()) {
+            for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
+                deserializer = mod.modifyDeserializer(config, beanDesc, deserializer);
+            }
+        }
+        return (JsonDeserializer<Object>) deserializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for Bean deserializer construction,
+    /* overridable by sub-classes
+    /**********************************************************
+     */
+
+    /**
+     * Overridable method that constructs a {@link BeanDeserializerBuilder}
+     * which is used to accumulate information needed to create deserializer
+     * instance.
+     */
+    protected BeanDeserializerBuilder constructBeanDeserializerBuilder(DeserializationContext ctxt,
+            BeanDescription beanDesc) {
+        return new BeanDeserializerBuilder(beanDesc, ctxt.getConfig());
+    }
+    
+    /**
+     * Method called to figure out settable properties for the
+     * bean deserializer to use.
+     *<p>
+     * Note: designed to be overridable, and effort is made to keep interface
+     * similar between versions.
+     */
+    protected void addBeanProps(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder)
+        throws JsonMappingException
+    {
+        final SettableBeanProperty[] creatorProps =
+                builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig());
+        
+        // Things specified as "ok to ignore"? [JACKSON-77]
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        boolean ignoreAny = false;
+        {
+            Boolean B = intr.findIgnoreUnknownProperties(beanDesc.getClassInfo());
+            if (B != null) {
+                ignoreAny = B.booleanValue();
+                builder.setIgnoreUnknownProperties(ignoreAny);
+            }
+        }
+        // Or explicit/implicit definitions?
+        Set<String> ignored = ArrayBuilders.arrayToSet(intr.findPropertiesToIgnore(beanDesc.getClassInfo()));        
+        for (String propName : ignored) {
+            builder.addIgnorable(propName);
+        }
+        // Also, do we have a fallback "any" setter?
+        AnnotatedMethod anySetter = beanDesc.findAnySetter();
+        if (anySetter != null) {
+            builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
+        }
+        // NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any setter
+        // Implicit ones via @JsonIgnore and equivalent?
+        if (anySetter == null) {
+            Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();
+            if (ignored2 != null) {
+                for (String propName : ignored2) {
+                    // allow ignoral of similarly named JSON property, but do not force;
+                    // latter means NOT adding this to 'ignored':
+                    builder.addIgnorable(propName);
+                }
+            }
+        }
+        final boolean useGettersAsSetters = (ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)
+                && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
+
+        // 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;
+            if (propDef.hasConstructorParameter()) {
+                /* [JACKSON-700] If property is passed via constructor parameter, we must
+                 *   handle things in special way. Not sure what is the most optimal way...
+                 *   for now, let's just call a (new) method in builder, which does nothing.
+                 */
+                // but let's call a method just to allow custom builders to be aware...
+                final String name = propDef.getName();
+                if (creatorProps != null) {
+                    for (SettableBeanProperty cp : creatorProps) {
+                        if (name.equals(cp.getName())) {
+                            prop = cp;
+                            break;
+                        }
+                    }
+                }
+                if (prop == null) {
+                    throw ctxt.mappingException("Could not find creator property with name '"
+                    		+name+"' (in class "+beanDesc.getBeanClass().getName()+")");
+                }
+                builder.addCreatorProperty(prop);
+                continue;
+            }
+            if (propDef.hasSetter()) {
+                Type propertyType = propDef.getSetter().getGenericParameterType(0);
+                prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
+            } else if (propDef.hasField()) {
+                Type propertyType = propDef.getField().getGenericType();
+                prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
+            } else if (useGettersAsSetters && propDef.hasGetter()) {
+                /* As per [JACKSON-88], may also need to consider getters
+                 * for Map/Collection properties; but with lowest precedence
+                 */
+                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 (prop != null) {
+                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;
+                    }
+                }
+                // one more thing before adding to builder: copy any metadata
+                prop.setViews(views);
+                builder.addProperty(prop);
+            }
+        }
+    }
+    
+    /**
+     * Helper method called to filter out explicit ignored properties,
+     * as well as properties that have "ignorable types".
+     * Note that this will not remove properties that have no
+     * setters.
+     */
+    protected List<BeanPropertyDefinition> filterBeanProps(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder,
+            List<BeanPropertyDefinition> propDefsIn,
+            Set<String> ignored)
+        throws JsonMappingException
+    {
+        ArrayList<BeanPropertyDefinition> result = new ArrayList<BeanPropertyDefinition>(
+                Math.max(4, propDefsIn.size()));
+        HashMap<Class<?>,Boolean> ignoredTypes = new HashMap<Class<?>,Boolean>();
+        // These are all valid setters, but we do need to introspect bit more
+        for (BeanPropertyDefinition property : propDefsIn) {
+            String name = property.getName();
+            if (ignored.contains(name)) { // explicit ignoral using @JsonIgnoreProperties needs to block entries
+                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();
+                }
+
+                // [JACKSON-429] Some types are declared as ignorable as well
+                if ((rawPropertyType != null)
+                        && (isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes))) {
+                    // important: make ignorable, to avoid errors if value is actually seen
+                    builder.addIgnorable(name);
+                    continue;
+                }
+            }
+            result.add(property);
+        }
+        return result;
+    }
+    
+    /**
+     * 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.
+     */
+    protected void addReferenceProperties(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();
+                Type genericType;
+                if (m instanceof AnnotatedMethod) {
+                    genericType = ((AnnotatedMethod) m).getGenericParameterType(0);
+                } else {
+                    genericType = m.getRawType();
+                }
+                SimpleBeanPropertyDefinition propDef = SimpleBeanPropertyDefinition.construct(
+                		ctxt.getConfig(), m);
+                builder.addBackReferenceProperty(name, constructSettableProperty(
+                        ctxt, beanDesc, propDef, genericType));
+            }
+        }
+    }
+    
+    /**
+     * 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.
+     */
+    protected void addInjectables(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder)
+        throws JsonMappingException
+    {
+        Map<Object, AnnotatedMember> raw = beanDesc.findInjectables();
+        if (raw != null) {
+            boolean fixAccess = ctxt.canOverrideAccessModifiers();
+            for (Map.Entry<Object, AnnotatedMember> entry : raw.entrySet()) {
+                AnnotatedMember m = entry.getValue();
+                if (fixAccess) {
+                    m.fixAccess(); // to ensure we can call it
+                }
+                builder.addInjectable(m.getName(), beanDesc.resolveType(m.getGenericType()),
+                        beanDesc.getClassAnnotations(), m, entry.getKey());
+            }
+        }
+    }
+
+    /**
+     * Method called to construct fallback {@link SettableAnyProperty}
+     * for handling unknown bean properties, given a method that
+     * has been designated as such setter.
+     */
+    protected SettableAnyProperty constructAnySetter(DeserializationContext ctxt,
+            BeanDescription beanDesc, AnnotatedMethod setter)
+        throws JsonMappingException
+    {
+        if (ctxt.canOverrideAccessModifiers()) {
+            setter.fixAccess(); // to ensure we can call it
+        }
+        // we know it's a 2-arg method, second arg is the value
+        JavaType type = beanDesc.bindingsForBeanType().resolveType(setter.getGenericParameterType(1));
+        BeanProperty.Std property = new BeanProperty.Std(setter.getName(), type, null,
+                beanDesc.getClassAnnotations(), setter, false);
+        type = resolveType(ctxt, beanDesc, type, setter);
+
+        /* AnySetter can be annotated with @JsonClass (etc) just like a
+         * regular setter... so let's see if those are used.
+         * Returns null if no annotations, in which case binding will
+         * be done at a later point.
+         */
+        JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt, setter);
+        if (deser != null) {
+            return new SettableAnyProperty(property, setter, type, deser);
+        }
+        /* Otherwise, method may specify more specific (sub-)class for
+         * value (no need to check if explicit deser was specified):
+         */
+        type = modifyTypeByAnnotation(ctxt, setter, type);
+        return new SettableAnyProperty(property, setter, type, null);
+    }
+
+    /**
+     * Method that will construct a regular bean property setter using
+     * the given setter method.
+     *
+     * @return Property constructed, if any; or null to indicate that
+     *   there should be no property based on given definitions.
+     */
+    protected SettableBeanProperty constructSettableProperty(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanPropertyDefinition propDef,
+            Type jdkType)
+        throws JsonMappingException
+    {
+        // need to ensure method is callable (for non-public)
+        AnnotatedMember mutator = propDef.getMutator();
+        if (ctxt.canOverrideAccessModifiers()) {
+            mutator.fixAccess();
+        }
+        // note: this works since we know there's exactly one argument for methods
+        JavaType t0 = beanDesc.resolveType(jdkType);
+
+        BeanProperty.Std property = new BeanProperty.Std(propDef.getName(), t0, propDef.getWrapperName(),
+                beanDesc.getClassAnnotations(), mutator, propDef.isRequired());
+        JavaType type = resolveType(ctxt, beanDesc, t0, mutator);
+        // did type change?
+        if (type != t0) {
+            property = property.withType(type);
+        }
+        
+        /* First: does the Method specify the deserializer to use?
+         * If so, let's use it.
+         */
+        JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, mutator);
+        type = modifyTypeByAnnotation(ctxt, mutator, type);
+        TypeDeserializer typeDeser = type.getTypeHandler();
+        SettableBeanProperty prop;
+        if (mutator instanceof AnnotatedMethod) {
+            prop = new MethodProperty(propDef, type, typeDeser,
+                beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator);
+        } else {
+            prop = new FieldProperty(propDef, type, typeDeser,
+                    beanDesc.getClassAnnotations(), (AnnotatedField) mutator);
+        }
+        if (propDeser != null) {
+            prop = prop.withValueDeserializer(propDeser);
+        }
+        // [JACKSON-235]: need to retain name of managed forward references:
+        AnnotationIntrospector.ReferenceProperty ref = propDef.findReferenceType();
+        if (ref != null && ref.isManagedReference()) {
+            prop.setManagedReferenceName(ref.getName());
+        }
+        return prop;
+    }
+
+    /**
+     * Method that will construct a regular bean property setter using
+     * the given setter method.
+     */
+    protected SettableBeanProperty constructSetterlessProperty(DeserializationContext ctxt,
+            BeanDescription beanDesc, BeanPropertyDefinition propDef)
+        throws JsonMappingException
+    {
+        final AnnotatedMethod getter = propDef.getGetter();
+        // need to ensure it is callable now:
+        if (ctxt.canOverrideAccessModifiers()) {
+            getter.fixAccess();
+        }
+
+        /* 26-Jan-2012, tatu: Alas, this complication is still needed to handle
+         *   (or at least work around) local type declarations...
+         */
+        JavaType type = getter.getType(beanDesc.bindingsForBeanType());
+        /* First: does the Method specify the deserializer to use?
+         * If so, let's use it.
+         */
+        JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, getter);
+        type = modifyTypeByAnnotation(ctxt, getter, type);
+        TypeDeserializer typeDeser = type.getTypeHandler();
+        SettableBeanProperty prop = new SetterlessProperty(propDef, type, typeDeser,
+                beanDesc.getClassAnnotations(), getter);
+        if (propDeser != null) {
+            prop = prop.withValueDeserializer(propDeser);
+        }
+        return prop;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for Bean deserializer, other
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used to skip processing for types that we know
+     * can not 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
+     * types anyway; but better safe than sorry.
+     */
+    protected boolean isPotentialBeanType(Class<?> type)
+    {
+        String typeStr = ClassUtil.canBeABeanType(type);
+        if (typeStr != null) {
+            throw new IllegalArgumentException("Can not 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");
+        }
+        /* also: can't deserialize some local classes: static are ok; in-method not;
+         * and with [JACKSON-594], 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");
+        }
+    	return true;
+    }
+
+    /**
+     * 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,
+            Class<?> type, Map<Class<?>,Boolean> ignoredTypes)
+    {
+        Boolean status = ignoredTypes.get(type);
+        if (status == null) {
+            BeanDescription desc = config.introspectClassAnnotations(type);
+            status = config.getAnnotationIntrospector().isIgnorableType(desc.getClassInfo());
+            // We default to 'false', ie. not ignorable
+            if (status == null) {
+                status = Boolean.FALSE;
+            }
+        }
+        return status;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java
new file mode 100644
index 0000000..b308f48
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BeanDeserializerModifier.java
@@ -0,0 +1,171 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.BeanDeserializer;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerFactory;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.CollectionLikeType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.MapType;
+
+/**
+ * Abstract class that defines API for objects that can be registered
+ * to participate in constructing {@link JsonDeserializer} instances
+ * (via {@link DeserializerFactory}).
+ * This is typically done by modules that want alter some aspects of deserialization
+ * process; and is preferable to sub-classing of {@link BeanDeserializerFactory}.
+ *<p>
+ * Note that Jackson 2.2 adds more methods for customization; with earlier versions
+ * only {@link BeanDeserializer} instances could be modified, but with 2.2 all types
+ * of deserializers can be changed.
+ *<p>
+ * Sequence in which callback methods are called for {@link BeanDeserializer} is:
+ *  <li>{@link #updateProperties} is called once all property definitions are
+ *    collected, and initial filtering (by ignorable type and explicit ignoral-by-bean)
+ *    has been performed.
+ *  <li>{@link #updateBuilder} is called once all initial pieces for building deserializer
+ *    have been collected
+ *   </li>
+ *  <li>{@link #modifyDeserializer} is called after deserializer has been built
+ *    by {@link BeanDeserializerBuilder}
+ *    but before it is returned to be used
+ *   </li>
+ * </ol>
+ *<p>
+ * For other types of deserializers, methods called depend on type of values for
+ * which deserializer is being constructed; and only a single method is called
+ * since the process does not involve builders (unlike that of {@link BeanDeserializer}.
+ *<p>
+ * Default method implementations are "no-op"s, meaning that methods are implemented
+ * but have no effect; this is mostly so that new methods can be added in later
+ * versions.
+ */
+public abstract class BeanDeserializerModifier
+{
+    /**
+     * Method called by {@link BeanDeserializerFactory} when it has collected
+     * initial list of {@link BeanPropertyDefinition}s, and done basic by-name
+     * and by-type filtering, but before constructing builder or actual
+     * property handlers; or arranging order.
+     * 
+     * The most common changes to make at this point are to completely remove
+     * specified properties, or rename then: other modifications are easier
+     * to make at later points.
+     */
+    public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config,
+            BeanDescription beanDesc, List<BeanPropertyDefinition> propDefs) {
+        return propDefs;
+    }
+    
+    /**
+     * Method called by {@link BeanDeserializerFactory} when it has collected
+     * basic information such as tentative list of properties to deserialize.
+     *
+     * Implementations may choose to modify state of builder (to affect deserializer being
+     * built), or even completely replace it (if they want to build different kind of
+     * deserializer). Typically changes mostly concern set of properties to deserialize.
+     */
+    public BeanDeserializerBuilder updateBuilder(DeserializationConfig config,
+            BeanDescription beanDesc, BeanDeserializerBuilder builder) {
+        return builder;
+    }
+
+    /**
+     * Method called by {@link BeanDeserializerFactory} after constructing default
+     * bean deserializer instance with properties collected and ordered earlier.
+     * Implementations can modify or replace given deserializer and return deserializer
+     * to use. Note that although initial deserializer being passed is of type
+     * {@link BeanDeserializer}, modifiers may return deserializers of other types;
+     * and this is why implementations must check for type before casting.
+     */
+    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
+            BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Callback methods for other types (since 2.2)
+    /**********************************************************
+     */
+
+    /**
+     * Method called by {@link DeserializerFactory} after it has constructed the
+     * standard deserializer for given
+     * {@link ArrayType}
+     * to make it possible to either replace or augment this deserializer with
+     * additional functionality.
+     * 
+     * @param config Configuration in use
+     * @param valueType Type of the value deserializer is used for.
+     * @param beanDesc Description f
+     * @param deserializer Default deserializer that would be used.
+     * 
+     * @return Deserializer to use; either <code>deserializer</code> that was passed
+     *   in, or an instance method constructed.
+     * 
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyArrayDeserializer(DeserializationConfig config,
+            ArrayType valueType, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config,
+            CollectionType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyCollectionLikeDeserializer(DeserializationConfig config,
+            CollectionLikeType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig config,
+            MapType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyMapLikeDeserializer(DeserializationConfig config,
+            MapLikeType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config,
+            JavaType type, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+        return deserializer;
+    }
+    
+    /**
+     * Method called by {@link DeserializerFactory} after it has constructed the
+     * standard key deserializer for given key type.
+     * This make it possible to replace the default key deserializer, or augment
+     * it somehow (including optional use of default deserializer with occasional
+     * override).
+     * 
+     * @since 2.2
+     */
+    public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config,
+            JavaType type, KeyDeserializer deserializer) {
+        return deserializer;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
new file mode 100644
index 0000000..20c848c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/BuilderBasedDeserializer.java
@@ -0,0 +1,737 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Class that handles deserialization using a separate
+ * Builder class, which is used for data binding and
+ * produces actual deserialized value at the end
+ * of data binding.
+ *<p>
+ * Note on implementation: much of code has been copied from
+ * {@link BeanDeserializer}; there may be opportunities to
+ * refactor this in future.
+ */
+public class BuilderBasedDeserializer
+    extends BeanDeserializerBase
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final AnnotatedMethod _buildMethod;
+	
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, initialization
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by {@link BeanDeserializerBuilder}.
+     */
+    public BuilderBasedDeserializer(BeanDeserializerBuilder builder,
+            BeanDescription beanDesc,
+            BeanPropertyMap properties, Map<String, SettableBeanProperty> backRefs,
+            HashSet<String> ignorableProps, boolean ignoreAllUnknown,
+            boolean hasViews)
+    {
+        super(builder, beanDesc, properties, backRefs,
+                ignorableProps, ignoreAllUnknown, hasViews);
+        _buildMethod = builder.getBuildMethod();
+        // 05-Mar-2012, tatu: Can not 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 "
+                    +beanDesc.getType()+")");
+        }
+    }
+
+    /**
+     * Copy-constructor that can be used by sub-classes to allow
+     * copy-on-write styling copying of settings of an existing instance.
+     */
+    protected BuilderBasedDeserializer(BuilderBasedDeserializer src)
+    {
+        this(src, src._ignoreAllUnknown);
+    }
+
+    protected BuilderBasedDeserializer(BuilderBasedDeserializer src, boolean ignoreAllUnknown)
+    {
+        super(src, ignoreAllUnknown);
+        _buildMethod = src._buildMethod;
+    }
+    
+    protected BuilderBasedDeserializer(BuilderBasedDeserializer src, NameTransformer unwrapper) {
+        super(src, unwrapper);
+        _buildMethod = src._buildMethod;
+    }
+
+    public BuilderBasedDeserializer(BuilderBasedDeserializer src, ObjectIdReader oir) {
+        super(src, oir);
+        _buildMethod = src._buildMethod;
+    }
+
+    public BuilderBasedDeserializer(BuilderBasedDeserializer src, HashSet<String> ignorableProps) {
+        super(src, ignorableProps);
+        _buildMethod = src._buildMethod;
+    }
+    
+    @Override
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    {
+        /* 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 BuilderBasedDeserializer(this, unwrapper);
+    }
+
+    @Override
+    public BuilderBasedDeserializer withObjectIdReader(ObjectIdReader oir) {
+        return new BuilderBasedDeserializer(this, oir);
+    }
+
+    @Override
+    public BuilderBasedDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+        return new BuilderBasedDeserializer(this, ignorableProps);
+    }
+
+    @Override
+    protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
+        SettableBeanProperty[] props = _beanProperties.getPropertiesInInsertionOrder();
+        return new BeanAsArrayBuilderDeserializer(this, props, _buildMethod);
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer implementation
+    /**********************************************************
+     */
+    
+    protected final Object finishBuild(DeserializationContext ctxt, Object builder)
+            throws IOException
+    {
+        try {
+            return _buildMethod.getMember().invoke(builder);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            return null;
+        }
+    }
+    
+    /**
+     * Main deserialization method for bean-based objects (POJOs).
+     */
+    @Override
+    public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        
+        // common case first:
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+            if (_vanillaProcessing) {
+            	return finishBuild(ctxt, vanillaDeserialize(jp, ctxt, t));
+            }
+            Object builder = deserializeFromObject(jp, ctxt);
+            return finishBuild(ctxt, builder);
+        }
+        // and then others, generally requiring use of @JsonCreator
+        switch (t) {
+        case VALUE_STRING:
+            return finishBuild(ctxt, deserializeFromString(jp, ctxt));
+        case VALUE_NUMBER_INT:
+            return finishBuild(ctxt, deserializeFromNumber(jp, ctxt));
+        case VALUE_NUMBER_FLOAT:
+        	return finishBuild(ctxt, deserializeFromDouble(jp, ctxt));
+        case VALUE_EMBEDDED_OBJECT:
+            return jp.getEmbeddedObject();
+        case VALUE_TRUE:
+        case VALUE_FALSE:
+            return finishBuild(ctxt, deserializeFromBoolean(jp, ctxt));
+        case START_ARRAY:
+            // these only work if there's a (delegating) creator...
+            return finishBuild(ctxt, deserializeFromArray(jp, ctxt));
+        case FIELD_NAME:
+        case END_OBJECT: // added to resolve [JACKSON-319], possible related issues
+            return finishBuild(ctxt, deserializeFromObject(jp, ctxt));
+        default:
+            throw ctxt.mappingException(getBeanClass());
+        }
+    }
+
+    /**
+     * Secondary deserialization method, called in cases where POJO
+     * instance is created as part of deserialization, potentially
+     * after collecting some or all of the properties to set.
+     */
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt,
+    		Object builder)
+        throws IOException, JsonProcessingException
+    {
+        /* Important: we call separate method which does NOT call
+         * 'finishBuild()', to avoid problems with recursion
+         */
+        return finishBuild(ctxt, _deserialize(jp, ctxt, builder));
+    }
+    
+    /*
+    /**********************************************************
+    /* Concrete deserialization methods
+    /**********************************************************
+     */
+
+    protected final Object _deserialize(JsonParser jp,
+            DeserializationContext ctxt, Object builder)
+        throws IOException, JsonProcessingException
+    {        
+        if (_injectables != null) {
+            injectValues(ctxt, builder);
+        }
+        if (_unwrappedPropertyHandler != null) {
+            return deserializeWithUnwrapped(jp, ctxt, builder);
+        }
+        if (_externalTypeIdHandler != null) {
+            return deserializeWithExternalTypeId(jp, ctxt, builder);
+        }
+        if (_needViewProcesing) {
+            Class<?> view = ctxt.getActiveView();
+            if (view != null) {
+                return deserializeWithView(jp, ctxt, builder, view);
+            }
+        }
+        JsonToken t = jp.getCurrentToken();
+        // 23-Mar-2010, tatu: In some cases, we start with full JSON object too...
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            
+            if (prop != null) { // normal case
+                try {
+                    builder = prop.deserializeSetAndReturn(jp, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, propName, ctxt);
+                }
+                continue;
+            }
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                // should we try to get return value of any setter too?
+                _anySetter.deserializeAndSet(jp, ctxt, builder, propName);
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, builder, propName);
+            }
+        }
+        return builder;
+    }
+    
+    /**
+     * Streamlined version that is only used when no "special"
+     * features are enabled.
+     */
+    private final Object vanillaDeserialize(JsonParser jp,
+    		DeserializationContext ctxt, JsonToken t)
+        throws IOException, JsonProcessingException
+    {
+        Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+            } else {
+                handleUnknownVanilla(jp, ctxt, bean, propName);
+            }
+        }
+        return bean;
+    }
+
+    /**
+     * General version used when handling needs more advanced
+     * features.
+     */
+    @Override
+    public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_nonStandardCreation) {
+            if (_unwrappedPropertyHandler != null) {
+                return deserializeWithUnwrapped(jp, ctxt);
+            }
+            if (_externalTypeIdHandler != null) {
+                return deserializeWithExternalTypeId(jp, ctxt);
+            }
+            return deserializeFromObjectUsingNonDefault(jp, ctxt);
+        }
+        Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+        if (_needViewProcesing) {
+            Class<?> view = ctxt.getActiveView();
+            if (view != null) {
+                return deserializeWithView(jp, ctxt, bean, view);
+            }
+        }
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);         
+            }
+        }
+        return bean;
+    }
+
+    /**
+     * Method called to deserialize bean using "property-based creator":
+     * this means that a non-default constructor or factory method is
+     * called, and then possibly other setters. The trick is that
+     * values for creator method need to be buffered, first; and 
+     * due to non-guaranteed ordering possibly some other properties
+     * as well.
+     */
+    @Override
+    protected final Object _deserializeUsingPropertyBased(final JsonParser jp,
+            final DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    { 
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        // 04-Jan-2010, tatu: May need to collect unknown properties for polymorphic cases
+        TokenBuffer unknown = null;
+
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken(); // to point to value
+            // creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                    Object bean;
+                    try {
+                        bean = 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(jp, ctxt, bean, unknown);
+                    }
+                    if (unknown != null) { // nope, just extra unknown stuff...
+                        bean = handleUnknownProperties(ctxt, bean, unknown);
+                    }
+                    // or just clean?
+                    return _deserialize(jp, ctxt, bean);
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+                continue;
+            }
+            // As per [JACKSON-313], things marked as ignorable should not be
+            // passed to any setter
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // "any property"?
+            if (_anySetter != null) {
+                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt));
+                continue;
+            }
+            // Ok then, let's collect the whole field; name and value
+            if (unknown == null) {
+                unknown = new TokenBuffer(jp.getCodec());
+            }
+            unknown.writeFieldName(propName);
+            unknown.copyCurrentStructure(jp);
+        }
+
+        // We hit END_OBJECT, so:
+        Object bean;
+        try {
+            bean = creator.build(ctxt, buffer);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            return null; // never gets here
+        }
+        if (unknown != null) {
+            // polymorphic?
+            if (bean.getClass() != _beanType.getRawClass()) {
+                return handlePolymorphic(null, ctxt, bean, unknown);
+            }
+            // no, just some extra unknown properties
+            return handleUnknownProperties(ctxt, bean, unknown);
+        }
+        return bean;
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializing when we have to consider an active View
+    /**********************************************************
+     */
+    
+    protected final Object deserializeWithView(JsonParser jp, DeserializationContext ctxt,
+            Object bean, Class<?> activeView)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            // Skip field name:
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                if (!prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+            } else if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);
+            }
+        }
+        return bean;
+    }
+    
+    /*
+    /**********************************************************
+    /* Handling for cases where we have "unwrapped" values
+    /**********************************************************
+     */
+
+    /**
+     * Method called when there are declared "unwrapped" properties
+     * which need special handling
+     */
+    protected Object deserializeWithUnwrapped(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_propertyBasedCreator != null) {
+            return deserializeUsingPropertyBasedWithUnwrapped(jp, ctxt);
+        }
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+        Object bean = _valueInstantiator.createUsingDefault(ctxt);
+
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            // ignorable things should be ignored
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but... others should be passed to unwrapped property deserializers
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // how about any setter? We'll get copies but...
+            if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+        }
+        tokens.writeEndObject();
+        _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+        return bean;
+    }    
+
+    protected Object deserializeWithUnwrapped(JsonParser jp,
+    		DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            jp.nextToken();
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but... others should be passed to unwrapped property deserializers
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // how about any setter? We'll get copies but...
+            if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+            }
+        }
+        tokens.writeEndObject();
+        _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+        return bean;
+    }
+
+    @SuppressWarnings("resource")
+    protected Object deserializeUsingPropertyBasedWithUnwrapped(JsonParser jp,
+    		DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+        tokens.writeStartObject();
+
+        JsonToken t = jp.getCurrentToken();
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken(); // to point to value
+            // creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    t = jp.nextToken(); // to move to following FIELD_NAME/END_OBJECT
+                    Object bean;
+                    try {
+                        bean = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        continue; // never gets here
+                    }
+                    // if so, need to copy all remaining tokens into buffer
+                    while (t == JsonToken.FIELD_NAME) {
+                        jp.nextToken(); // to skip name
+                        tokens.copyCurrentStructure(jp);
+                        t = jp.nextToken();
+                    }
+                    tokens.writeEndObject();
+                    if (bean.getClass() != _beanType.getRawClass()) {
+                        // !!! 08-Jul-2011, tatu: Could probably support; but for now
+                        //   it's too complicated, so bail out
+                        throw ctxt.mappingException("Can not create polymorphic instances with unwrapped values");
+                    }
+                    return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) {
+                buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+                continue;
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            tokens.writeFieldName(propName);
+            tokens.copyCurrentStructure(jp);
+            // "any property"?
+            if (_anySetter != null) {
+                buffer.bufferAnyProperty(_anySetter, propName, _anySetter.deserialize(jp, ctxt));
+            }
+        }
+
+        // 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) {
+            wrapInstantiationProblem(e, ctxt);
+            return null; // never gets here
+        }
+        return _unwrappedPropertyHandler.processUnwrapped(jp, ctxt, bean, tokens);
+    }
+
+    /*
+    /**********************************************************
+    /* Handling for cases where we have property/-ies with
+    /* external type id
+    /**********************************************************
+     */
+    
+    protected Object deserializeWithExternalTypeId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_propertyBasedCreator != null) {
+            return deserializeUsingPropertyBasedWithExternalTypeId(jp, ctxt);
+        }
+        return deserializeWithExternalTypeId(jp, ctxt, _valueInstantiator.createUsingDefault(ctxt));
+    }
+
+    protected Object deserializeWithExternalTypeId(JsonParser jp,
+    		DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        final Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        final ExternalTypeHandler ext = _externalTypeIdHandler.start();
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            jp.nextToken();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            if (prop != null) { // normal case
+                if (activeView != null && !prop.visibleInView(activeView)) {
+                    jp.skipChildren();
+                    continue;
+                }
+                try {
+                    bean = prop.deserializeSetAndReturn(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            }
+            // ignorable things should be ignored
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // but others are likely to be part of external type id thingy...
+            if (ext.handlePropertyValue(jp, ctxt, propName, bean)) {
+                continue;
+            }
+            // if not, the usual fallback handling:
+            if (_anySetter != null) {
+                try {
+                    _anySetter.deserializeAndSet(jp, ctxt, bean, propName);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, propName, ctxt);
+                }
+                continue;
+            } else {
+                // Unknown: let's call handler method
+                handleUnknownProperty(jp, ctxt, bean, propName);         
+            }
+        }
+        // and when we get this far, let's try finalizing the deal:
+        return ext.complete(jp, ctxt, bean);
+    }        
+
+    protected Object deserializeUsingPropertyBasedWithExternalTypeId(JsonParser jp,
+    		DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // !!! 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");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java
new file mode 100644
index 0000000..11c972b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualDeserializer.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Add-on interface that {@link JsonDeserializer}s can implement to get a callback
+ * that can be used to create contextual (context-dependent) instances of
+ * deserializer to use for  handling properties of supported type.
+ * This can be useful
+ * for deserializers that can be configured by annotations, or should otherwise
+ * have differing behavior depending on what kind of property is being deserialized.
+ *<p>
+ * Note that in cases where deserializer needs both contextualization and
+ * resolution -- that is, implements both this interface and {@link ResolvableDeserializer}
+ * -- resolution via {@link ResolvableDeserializer} occurs first, and contextual
+ * resolution (via this interface) later on.
+ */
+public interface ContextualDeserializer
+{
+    /**
+     * Method called to see if a different (or differently configured) deserializer
+     * is needed to deserialize values of specified property.
+     * Note that instance that this method is called on is typically shared one and
+     * as a result method should <b>NOT</b> modify this instance but rather construct
+     * and return a new instance. This instance should only be returned as-is, in case
+     * it is already suitable for use.
+     * 
+     * @param ctxt Deserialization context to access configuration, additional 
+     *    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
+     *   null is passed instead (in which case impls usually pass 'this' deserializer as is)
+     * 
+     * @return Deserializer to use for deserializing values of specified property;
+     *   may be this instance or a new instance.
+     * 
+     * @throws JsonMappingException
+     */
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java
new file mode 100644
index 0000000..897dc79
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ContextualKeyDeserializer.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Add-on interface that {@link KeyDeserializer}s can implement to get a callback
+ * that can be used to create contextual instances of key deserializer to use for
+ * handling Map keys of supported type. This can be useful
+ * for key deserializers that can be configured by annotations, or should otherwise
+ * have differing behavior depending on what kind of Map property keys are being deserialized.
+ */
+public interface ContextualKeyDeserializer
+{
+    /**
+     * Method called to see if a different (or differently configured) key deserializer
+     * is needed to deserialize keys of specified Map property.
+     * Note that instance that this method is called on is typically shared one and
+     * as a result method should <b>NOT</b> modify this instance but rather construct
+     * and return a new instance. This instance should only be returned as-is, in case
+     * it is already suitable for use.
+     * 
+     * @param ctxt Deserialization context to access configuration, additional 
+     *    deserializers that may be needed by this deserializer
+     * @param property Method, field or constructor parameter that declared Map for which
+     *   contextual instance will be used. Will not be available when deserializing root-level
+     *   Map value; otherwise should not be null.
+     * 
+     * @return Key deserializer to use for deserializing keys specified Map property,
+     *   may be this instance or a new instance.
+     */
+    public KeyDeserializer createContextual(DeserializationContext ctxt,
+            BeanProperty property)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
new file mode 100644
index 0000000..858dd3b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/CreatorProperty.java
@@ -0,0 +1,198 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.PropertyName;
+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;
+
+/**
+ * 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.
+ *<p>
+ * Note on injectable values: unlike with other mutators, where
+ * deserializer and injecting are separate, here we deal the two as related
+ * things. This is necessary to add proper priority, as well as to simplify
+ * coordination.
+ */
+public class CreatorProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Placeholder that represents constructor parameter, when it is created
+     * from actual constructor.
+     * May be null when a synthetic instance is created.
+     */
+    protected final AnnotatedParameter _annotated;
+
+    /**
+     * Id of value to inject, if value injection should be used for this parameter
+     * (in addition to, or instead of, regular deserialization).
+     */
+    protected final Object _injectableValueId;
+
+    /**
+     * @since 2.1
+     */
+    protected final int _creatorIndex;
+
+    /**
+     * @deprecated Since 2.2: use the method that takes <code>isRequired</code> property
+     */
+    @Deprecated
+    public CreatorProperty(String name, JavaType type, TypeDeserializer typeDeser,
+            Annotations contextAnnotations, AnnotatedParameter param,
+            int index, Object injectableValueId)
+    {
+        this(name, type, null, typeDeser, contextAnnotations, param, index,
+                injectableValueId, true);
+    }
+    
+    /**
+     * @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
+     *    information, if one is needed
+     * @param contextAnnotations Contextual annotations (usually by class that
+     *    declares creator [constructor, factory method] that includes
+     *    this property)
+     * @param param Representation of property, constructor or factory
+     *    method parameter; used for accessing annotations of the property
+     * @param index Index of this property within creator invocatino
+     */
+    public CreatorProperty(String name, JavaType type, PropertyName wrapperName,
+            TypeDeserializer typeDeser,
+            Annotations contextAnnotations, AnnotatedParameter param,
+            int index, Object injectableValueId,
+            boolean isRequired)
+    {
+        super(name, type, wrapperName, typeDeser, contextAnnotations, isRequired);
+        _annotated = param;
+        _creatorIndex = index;
+        _injectableValueId = injectableValueId;
+    }
+
+    protected CreatorProperty(CreatorProperty src, String newName) {
+        super(src, newName);
+        _annotated = src._annotated;
+        _creatorIndex = src._creatorIndex;
+        _injectableValueId = src._injectableValueId;
+    }
+    
+    protected CreatorProperty(CreatorProperty src, JsonDeserializer<?> deser) {
+        super(src, deser);
+        _annotated = src._annotated;
+        _creatorIndex = src._creatorIndex;
+        _injectableValueId = src._injectableValueId;
+    }
+
+    @Override
+    public CreatorProperty withName(String newName) {
+        return new CreatorProperty(this, newName);
+    }
+    
+    @Override
+    public CreatorProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new CreatorProperty(this, deser);
+    }
+
+    /**
+     * 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)
+    {
+        if (_injectableValueId == null) {
+            throw new IllegalStateException("Property '"+getName()
+                    +"' (type "+getClass().getName()+") has no injectable value id configured");
+        }
+        return context.findInjectableValue(_injectableValueId, this, beanInstance);
+    }
+    
+    /**
+     * Method to find value to inject, and inject it to this property.
+     */
+    public void inject(DeserializationContext context, Object beanInstance)
+        throws IOException
+    {
+        set(beanInstance, findInjectableValue(context, beanInstance));
+    }
+    
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        if (_annotated == null) {
+            return null;
+        }
+        return _annotated.getAnnotation(acls);
+    }
+
+    @Override public AnnotatedMember getMember() {  return _annotated; }
+
+    @Override public int getCreatorIndex() {
+        return _creatorIndex;
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+                                  Object instance)
+        throws IOException, JsonProcessingException
+    {
+        set(instance, deserialize(jp, ctxt));
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        return setAndReturn(instance, deserialize(jp, 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.
+         */
+        throw new IllegalStateException("Method should never be called on a "+getClass().getName());
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value) throws IOException
+    {
+        return instance;
+    }
+    
+    @Override
+    public Object getInjectableValueId() {
+        return _injectableValueId;
+    }
+
+    @Override
+    public String toString() { return "[creator property, name '"+getName()+"'; inject id '"+_injectableValueId+"']"; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java b/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java
new file mode 100644
index 0000000..1cf00fe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DataFormatReaders.java
@@ -0,0 +1,388 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.format.*;
+import com.fasterxml.jackson.core.io.MergedStream;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Alternative to {@link DataFormatDetector} that needs to be used when
+ * using data-binding.
+ * 
+ * @since 2.1
+ */
+public class DataFormatReaders
+{
+    /**
+     * By default we will look ahead at most 64 bytes; in most cases,
+     * much less (4 bytes or so) is needed, but we will allow bit more
+     * leniency to support data formats that need more complex heuristics.
+     */
+    public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64;
+    
+    /**
+     * Ordered list of readers which both represent data formats to
+     * detect (in precedence order, starting with highest) and contain
+     * factories used for actual detection.
+     */
+    protected final ObjectReader[] _readers;
+
+    /**
+     * Strength of match we consider to be good enough to be used
+     * without checking any other formats.
+     * Default value is {@link MatchStrength#SOLID_MATCH}, 
+     */
+    protected final MatchStrength _optimalMatch;
+
+    /**
+     * Strength of minimal match we accept as the answer, unless
+     * better matches are found. 
+     * Default value is {@link MatchStrength#WEAK_MATCH}, 
+     */
+    protected final MatchStrength _minimalMatch;
+
+    /**
+     * Maximum number of leading bytes of the input that we can read
+     * to determine data format.
+     *<p>
+     * Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}.
+     */
+    protected final int _maxInputLookahead;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+    
+    public DataFormatReaders(ObjectReader... detectors) {
+        this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH,
+            DEFAULT_MAX_INPUT_LOOKAHEAD);
+    }
+
+    public DataFormatReaders(Collection<ObjectReader> detectors) {
+        this(detectors.toArray(new ObjectReader[detectors.size()]));
+    }
+
+    private DataFormatReaders(ObjectReader[] detectors,
+            MatchStrength optMatch, MatchStrength minMatch,
+            int maxInputLookahead)
+    {
+        _readers = detectors;
+        _optimalMatch = optMatch;
+        _minimalMatch = minMatch;
+        _maxInputLookahead = maxInputLookahead;
+    }
+    
+    /*
+    /**********************************************************
+    /* Fluent factories for changing match settings
+    /**********************************************************
+     */
+    
+    public DataFormatReaders withOptimalMatch(MatchStrength optMatch) {
+        if (optMatch == _optimalMatch) {
+            return this;
+        }
+        return new DataFormatReaders(_readers, optMatch, _minimalMatch, _maxInputLookahead);
+    }
+
+    public DataFormatReaders withMinimalMatch(MatchStrength minMatch) {
+        if (minMatch == _minimalMatch) {
+            return this;
+        }
+        return new DataFormatReaders(_readers, _optimalMatch, minMatch, _maxInputLookahead);
+    }
+
+    public DataFormatReaders with(ObjectReader[] readers) {
+        return new DataFormatReaders(readers, _optimalMatch, _minimalMatch, _maxInputLookahead);
+    }
+    
+    public DataFormatReaders withMaxInputLookahead(int lookaheadBytes)
+    {
+        if (lookaheadBytes == _maxInputLookahead) {
+            return this;
+        }
+        return new DataFormatReaders(_readers, _optimalMatch, _minimalMatch, lookaheadBytes);
+    }
+
+    /*
+    /**********************************************************
+    /* Fluent factories for changing underlying readers
+    /**********************************************************
+     */
+
+    public DataFormatReaders with(DeserializationConfig config)
+    {
+        final int len = _readers.length;
+        ObjectReader[] r = new ObjectReader[len];
+        for (int i = 0; i < len; ++i) {
+            r[i] = _readers[i].with(config);
+        }
+        return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
+    }
+
+    public DataFormatReaders withType(JavaType type)
+    {
+        final int len = _readers.length;
+        ObjectReader[] r = new ObjectReader[len];
+        for (int i = 0; i < len; ++i) {
+            r[i] = _readers[i].withType(type);
+        }
+        return new DataFormatReaders(r, _optimalMatch, _minimalMatch, _maxInputLookahead);
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method to call to find format that content (accessible via given
+     * {@link InputStream}) given has, as per configuration of this detector
+     * instance.
+     * 
+     * @return Matcher object which contains result; never null, even in cases
+     *    where no match (with specified minimal match strength) is found.
+     */
+    public Match findFormat(InputStream in) throws IOException
+    {
+        return _findFormat(new AccessorForReader(in, new byte[_maxInputLookahead]));
+    }
+
+    /**
+     * Method to call to find format that given content (full document)
+     * has, as per configuration of this detector instance.
+     * 
+     * @return Matcher object which contains result; never null, even in cases
+     *    where no match (with specified minimal match strength) is found.
+     */
+    public Match findFormat(byte[] fullInputData) throws IOException
+    {
+        return _findFormat(new AccessorForReader(fullInputData));
+    }
+
+    /**
+     * Method to call to find format that given content (full document)
+     * has, as per configuration of this detector instance.
+     * 
+     * @return Matcher object which contains result; never null, even in cases
+     *    where no match (with specified minimal match strength) is found.
+     * 
+     * @since 2.1
+     */
+    public Match findFormat(byte[] fullInputData, int offset, int len) throws IOException
+    {
+        return _findFormat(new AccessorForReader(fullInputData, offset, len));
+    }
+    
+    /*
+    /**********************************************************
+    /* Overrides
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        final int len = _readers.length;
+        if (len > 0) {
+            sb.append(_readers[0].getFactory().getFormatName());
+            for (int i = 1; i < len; ++i) {
+                sb.append(", ");
+                sb.append(_readers[i].getFactory().getFormatName());
+            }
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private Match _findFormat(AccessorForReader acc) throws IOException
+    {
+        ObjectReader bestMatch = null;
+        MatchStrength bestMatchStrength = null;
+        for (ObjectReader f : _readers) {
+            acc.reset();
+            MatchStrength strength = f.getFactory().hasFormat(acc);
+            // if not better than what we have so far (including minimal level limit), skip
+            if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) {
+                continue;
+            }
+            // also, needs to better match than before
+            if (bestMatch != null) {
+                if (bestMatchStrength.ordinal() >= strength.ordinal()) {
+                    continue;
+                }
+            }
+            // finally: if it's good enough match, we are done
+            bestMatch = f;
+            bestMatchStrength = strength;
+            if (strength.ordinal() >= _optimalMatch.ordinal()) {
+                break;
+            }
+        }
+        return acc.createMatcher(bestMatch, bestMatchStrength);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * We need sub-class here as well, to be able to access efficiently.
+     */
+    protected class AccessorForReader extends InputAccessor.Std
+    {
+        public AccessorForReader(InputStream in, byte[] buffer) {
+            super(in, buffer);
+        }
+        public AccessorForReader(byte[] inputDocument) {
+            super(inputDocument);
+        }
+        public AccessorForReader(byte[] inputDocument, int start, int len) {
+            super(inputDocument, start, len);
+        }
+
+        public Match createMatcher(ObjectReader match, MatchStrength matchStrength)
+        {
+            return new Match(_in, _buffer, _bufferedStart, (_bufferedEnd - _bufferedStart),
+                    match, matchStrength);
+        }
+    }
+    
+    /**
+     * Result class, similar to {@link DataFormatMatcher}
+     */
+    public static class Match
+    {
+        protected final InputStream _originalStream;
+
+        /**
+         * Content read during format matching process
+         */
+        protected final byte[] _bufferedData;
+
+        /**
+         * Pointer to the first byte in buffer available for reading
+         */
+        protected final int _bufferedStart;
+        
+        /**
+         * Number of bytes available in buffer.
+         */
+        protected final int _bufferedLength;
+
+        /**
+         * Factory that produced sufficient match (if any)
+         */
+        protected final ObjectReader _match;
+
+        /**
+         * Strength of match with {@link #_match}
+         */
+        protected final MatchStrength _matchStrength;
+        
+        protected Match(InputStream in, byte[] buffered,
+                int bufferedStart, int bufferedLength,
+                ObjectReader match, MatchStrength strength)
+        {
+            _originalStream = in;
+            _bufferedData = buffered;
+            _bufferedStart = bufferedStart;
+            _bufferedLength = bufferedLength;
+            _match = match;
+            _matchStrength = strength;
+        }
+
+        /*
+        /**********************************************************
+        /* Public API, simple accessors
+        /**********************************************************
+         */
+
+        /**
+         * Accessor to use to see if any formats matched well enough with
+         * the input data.
+         */
+        public boolean hasMatch() { return _match != null; }
+
+        /**
+         * Method for accessing strength of the match, if any; if no match,
+         * will return {@link MatchStrength#INCONCLUSIVE}.
+         */
+        public MatchStrength getMatchStrength() {
+            return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength;
+        }
+
+        /**
+         * Accessor for {@link JsonFactory} that represents format that data matched.
+         */
+        public ObjectReader getReader() { return _match; }
+
+        /**
+         * Accessor for getting brief textual name of matched format if any (null
+         * if none). Equivalent to:
+         *<pre>
+         *   return hasMatch() ? getMatch().getFormatName() : null;
+         *</pre>
+         */
+        public String getMatchedFormatName() {
+            return _match.getFactory().getFormatName();
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, factory methods
+        /**********************************************************
+         */
+        
+        /**
+         * Convenience method for trying to construct a {@link JsonParser} for
+         * parsing content which is assumed to be in detected data format.
+         * If no match was found, returns null.
+         */
+        public JsonParser createParserWithMatch() throws IOException
+        {
+            if (_match == null) {
+                return null;
+            }
+            JsonFactory jf = _match.getFactory();
+            if (_originalStream == null) {
+                return jf.createParser(_bufferedData, _bufferedStart, _bufferedLength);
+            }
+            return jf.createParser(getDataStream());
+        }
+        
+        /**
+         * Method to use for accessing input for which format detection has been done.
+         * This <b>must</b> be used instead of using stream passed to detector
+         * unless given stream itself can do buffering.
+         * Stream will return all content that was read during matching process, as well
+         * as remaining contents of the underlying stream.
+         */
+        public InputStream getDataStream() {
+            if (_originalStream == null) {
+                return new ByteArrayInputStream(_bufferedData, _bufferedStart, _bufferedLength);
+            }
+            return new MergedStream(null, _originalStream, _bufferedData, _bufferedStart, _bufferedLength);
+        }        
+    }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
new file mode 100644
index 0000000..fabf977
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DefaultDeserializationContext.java
@@ -0,0 +1,226 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.LinkedHashMap;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.deser.impl.ReadableObjectId;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Complete {@link DeserializationContext} implementation that adds
+ * extended API for {@link ObjectMapper} (and {@link ObjectReader})
+ * to call, as well as implements certain parts that base class
+ * has left abstract.
+ * The remaining abstract methods ({@link #createInstance}, {@link #with})
+ * are left so that custom implementations will properly implement them
+ * to return intended subtype.
+ */
+public abstract class DefaultDeserializationContext
+    extends DeserializationContext
+    implements java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 1L;
+
+    protected transient LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId> _objectIds;
+    
+    /**
+     * 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
+     */
+    protected DefaultDeserializationContext(DeserializerFactory df, DeserializerCache cache) {
+        super(df, cache);
+    }
+    
+    protected DefaultDeserializationContext(DefaultDeserializationContext src,
+            DeserializationConfig config, JsonParser jp, InjectableValues values) {
+        super(src, config, jp, values);
+    }
+
+    protected DefaultDeserializationContext(DefaultDeserializationContext src,
+            DeserializerFactory factory) {
+        super(src, factory);
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract methods impls, Object Id
+    /**********************************************************
+     */
+
+    @Override
+    public ReadableObjectId findObjectId(Object id,
+            ObjectIdGenerator<?> generator)
+    {
+        final ObjectIdGenerator.IdKey key = generator.key(id);
+        if (_objectIds == null) {
+            _objectIds = new LinkedHashMap<ObjectIdGenerator.IdKey, ReadableObjectId>();
+        } else {
+            ReadableObjectId entry = _objectIds.get(key);
+            if (entry != null) {
+                return entry;
+            }
+        }
+        ReadableObjectId entry = new ReadableObjectId(id);
+        _objectIds.put(key, entry);
+        return entry;
+    }
+    
+    /*
+    /**********************************************************
+    /* Abstract methods impls, other factory methods
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public JsonDeserializer<Object> deserializerInstance(Annotated annotated,
+            Object deserDef)
+        throws JsonMappingException
+    {
+        if (deserDef == null) {
+            return null;
+        }
+        JsonDeserializer<?> deser;
+        
+        if (deserDef instanceof JsonDeserializer) {
+            deser = (JsonDeserializer<?>) deserDef;
+        } else {
+            /* Alas, there's no way to force return type of "either class
+             * X or Y" -- need to throw an exception after the fact
+             */
+            if (!(deserDef instanceof Class)) {
+                throw new IllegalStateException("AnnotationIntrospector returned deserializer definition of type "+deserDef.getClass().getName()+"; expected type JsonDeserializer or Class<JsonDeserializer> instead");
+            }
+            Class<?> deserClass = (Class<?>)deserDef;
+            // there are some known "no class" markers to consider too:
+            if (deserClass == JsonDeserializer.None.class || deserClass == NoClass.class) {
+                return null;
+            }
+            if (!JsonDeserializer.class.isAssignableFrom(deserClass)) {
+                throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()+"; expected Class<JsonDeserializer>");
+            }
+            HandlerInstantiator hi = _config.getHandlerInstantiator();
+            deser = (hi == null) ? null : hi.deserializerInstance(_config, annotated, deserClass);
+            if (deser == null) {
+                deser = (JsonDeserializer<?>) ClassUtil.createInstance(deserClass,
+                        _config.canOverrideAccessModifiers());
+            }
+        }
+        // First: need to resolve
+        if (deser instanceof ResolvableDeserializer) {
+            ((ResolvableDeserializer) deser).resolve(this);
+        }
+        return (JsonDeserializer<Object>) deser;
+    }
+
+    @Override
+    public final KeyDeserializer keyDeserializerInstance(Annotated annotated,
+            Object deserDef)
+        throws JsonMappingException
+    {
+        if (deserDef == null) {
+            return null;
+        }
+
+        KeyDeserializer deser;
+        
+        if (deserDef instanceof KeyDeserializer) {
+            deser = (KeyDeserializer) deserDef;
+        } else {
+            if (!(deserDef instanceof Class)) {
+                throw new IllegalStateException("AnnotationIntrospector returned key deserializer definition of type "
+                        +deserDef.getClass().getName()
+                        +"; expected type KeyDeserializer or Class<KeyDeserializer> instead");
+            }
+            Class<?> deserClass = (Class<?>)deserDef;
+            // there are some known "no class" markers to consider too:
+            if (deserClass == KeyDeserializer.None.class || deserClass == NoClass.class) {
+                return null;
+            }
+            if (!KeyDeserializer.class.isAssignableFrom(deserClass)) {
+                throw new IllegalStateException("AnnotationIntrospector returned Class "+deserClass.getName()
+                        +"; expected Class<KeyDeserializer>");
+            }
+            HandlerInstantiator hi = _config.getHandlerInstantiator();
+            deser = (hi == null) ? null : hi.keyDeserializerInstance(_config, annotated, deserClass);
+            if (deser == null) {
+                deser = (KeyDeserializer) ClassUtil.createInstance(deserClass,
+                        _config.canOverrideAccessModifiers());
+            }
+        }
+        // First: need to resolve
+        if (deser instanceof ResolvableDeserializer) {
+            ((ResolvableDeserializer) deser).resolve(this);
+        }
+        return deser;
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Fluent factory method used for constructing a blueprint instance
+     * with different factory
+     */
+    public abstract DefaultDeserializationContext with(DeserializerFactory factory);
+    
+    /**
+     * Method called to create actual usable per-deserialization
+     * context instance.
+     */
+    public abstract DefaultDeserializationContext createInstance(
+            DeserializationConfig config, JsonParser jp, InjectableValues values);
+    
+    /*
+    /**********************************************************
+    /* And then the concrete implementation class
+    /**********************************************************
+     */
+
+    /**
+     * Actual full concrete implementation
+     */
+    public final static class Impl extends DefaultDeserializationContext
+    {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Default constructor for a blueprint object, which will use the standard
+         * {@link DeserializerCache}, given factory.
+         */
+        public Impl(DeserializerFactory df) {
+            super(df, null);
+        }
+
+        protected Impl(Impl src,
+                DeserializationConfig config, JsonParser jp, InjectableValues values) {
+            super(src, config, jp, values);
+        }
+
+        protected Impl(Impl src, DeserializerFactory factory) {
+            super(src, factory);
+        }
+        
+        @Override
+        public DefaultDeserializationContext createInstance(DeserializationConfig config,
+                JsonParser jp, InjectableValues values) {
+            return new Impl(this, config, jp, values);
+        }
+
+        @Override
+        public DefaultDeserializationContext with(DeserializerFactory factory) {
+            return new Impl(this, factory);
+        }        
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
new file mode 100644
index 0000000..c04974d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializationProblemHandler.java
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * This is the class that can be registered (via
+ * {@link DeserializationConfig} object owner by
+ * {@link ObjectMapper}) to get calledn when a potentially
+ * recoverable problem is encountered during deserialization
+ * process. Handlers can try to resolve the problem, throw
+ * an exception or do nothing.
+ *<p>
+ * Default implementations for all methods implemented minimal
+ * "do nothing" functionality, which is roughly equivalent to
+ * not having a registered listener at all. This allows for
+ * only implemented handler methods one is interested in, without
+ * handling other cases.
+ */
+public abstract class DeserializationProblemHandler
+{
+    /**
+     * Method called when a JSON Map ("Object") entry with an unrecognized
+     * name is encountered.
+     * Content (supposedly) matching the property are accessible via
+     * parser that can be obtained from passed deserialization context.
+     * Handler can also choose to skip the content; if so, it MUST return
+     * true to indicate it did handle property succesfully.
+     * Skipping is usually done like so:
+     *<pre>
+     *  ctxt.getParser().skipChildren();
+     *</pre>
+     *<p>
+     * Note: version 1.2 added new deserialization feature
+     * (<code>DeserializationConfig.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES</code>).
+     * It will only have effect <b>after</b> handler is called, and only
+     * if handler did <b>not</b> handle the problem.
+     *
+     * @param beanOrClass Either bean instance being deserialized (if one
+     *   has been instantiated so far); or Class that indicates type that
+     *   will be instantiated (if no instantiation done yet: for example
+     *   when bean uses non-default constructors)
+     * @param jp Parser to use for handling problematic content
+     * 
+     * @return True if the problem is resolved (and content available used or skipped);
+     *  false if the handler did not anything and the problem is unresolved. Note that in
+     *  latter case caller will either throw an exception or explicitly skip the content,
+     *  depending on configuration.
+     */
+    public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser jp,
+            JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName)
+        throws IOException, JsonProcessingException
+    {
+        return false;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
new file mode 100644
index 0000000..72dd082
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerCache.java
@@ -0,0 +1,583 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Class that defines caching layer between callers (like
+ * {@link ObjectMapper},
+ * {@link com.fasterxml.jackson.databind.DeserializationContext})
+ * and classes that construct deserializers
+ * ({@link com.fasterxml.jackson.databind.deser.DeserializerFactory}).
+ */
+public final class DeserializerCache
+    implements java.io.Serializable // since 2.1 -- needs to be careful tho
+{
+    private static final long serialVersionUID = 1L;
+
+    /*
+    /**********************************************************
+    /* Caching
+    /**********************************************************
+     */
+
+    /**
+     * We will also cache some dynamically constructed deserializers;
+     * specifically, ones that are expensive to construct.
+     * This currently means bean and Enum deserializers; array, List and Map
+     * deserializers will not be cached.
+     *<p>
+     * Given that we don't expect much concurrency for additions
+     * (should very quickly converge to zero after startup), let's
+     * explicitly define a low concurrency setting.
+     */
+    final protected ConcurrentHashMap<JavaType, JsonDeserializer<Object>> _cachedDeserializers
+        = new ConcurrentHashMap<JavaType, JsonDeserializer<Object>>(64, 0.75f, 2);
+
+    /**
+     * During deserializer construction process we may need to keep track of partially
+     * completed deserializers, to resolve cyclic dependencies. This is the
+     * map used for storing deserializers before they are fully complete.
+     */
+    final protected HashMap<JavaType, JsonDeserializer<Object>> _incompleteDeserializers
+        = new HashMap<JavaType, JsonDeserializer<Object>>(8);
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public DeserializerCache() { }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object writeReplace() {
+        // instead of making this transient, just clear it:
+        _incompleteDeserializers.clear();
+        // TODO: clear out "cheap" cached deserializers?
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Access to caching aspects
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to determine how many deserializers this
+     * provider is caching currently 
+     * (if it does caching: default implementation does)
+     * Exact count depends on what kind of deserializers get cached;
+     * default implementation caches only dynamically constructed deserializers,
+     * but not eagerly constructed standard deserializers (which is different
+     * from how serializer provider works).
+     *<p>
+     * The main use case for this method is to allow conditional flushing of
+     * deserializer cache, if certain number of entries is reached.
+     */
+    public int cachedDeserializersCount() {
+        return _cachedDeserializers.size();
+    }
+
+    /**
+     * Method that will drop all dynamically constructed deserializers (ones that
+     * are counted as result value for {@link #cachedDeserializersCount}).
+     * This can be used to remove memory usage (in case some deserializers are
+     * only used once or so), or to force re-construction of deserializers after
+     * configuration changes for mapper than owns the provider.
+     */
+    public void flushCachedDeserializers() {
+        _cachedDeserializers.clear();       
+    }
+
+    /*
+    /**********************************************************
+    /* General deserializer locating method
+    /**********************************************************
+     */
+
+    /**
+     * Method called to get hold of a deserializer for a value of given type;
+     * or if no such deserializer can be found, a default handler (which
+     * may do a best-effort generic serialization or just simply
+     * throw an exception when invoked).
+     *<p>
+     * Note: this method is only called for value types; not for keys.
+     * Key deserializers can be accessed using {@link #findKeyDeserializer}.
+     *<p>
+     * Note also that deserializer returned is guaranteed to be resolved
+     * (if it is of type {@link ResolvableDeserializer}), but
+     * not contextualized (wrt {@link ContextualDeserializer}): caller
+     * has to handle latter if necessary.
+     *
+     * @param ctxt Deserialization context
+     * @param propertyType Declared type of the value to deserializer (obtained using
+     *   'setter' method signature and/or type annotations
+     *
+     * @throws JsonMappingException if there are fatal problems with
+     *   accessing suitable deserializer; including that of not
+     *   finding any serializer
+     */
+    public JsonDeserializer<Object> findValueDeserializer(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType propertyType)
+        throws JsonMappingException
+    {
+        JsonDeserializer<Object> deser = _findCachedDeserializer(propertyType);
+        if (deser != null) {
+            return deser;
+        }
+        // If not, need to request factory to construct (or recycle)
+        deser = _createAndCacheValueDeserializer(ctxt, factory, propertyType);
+        if (deser == null) {
+            /* Should we let caller handle it? Let's have a helper method
+             * decide it; can throw an exception, or return a valid
+             * deserializer
+             */
+            deser = _handleUnknownValueDeserializer(propertyType);
+        }
+        return deser;
+    }
+
+    /**
+     * Method called to get hold of a deserializer to use for deserializing
+     * keys for {@link java.util.Map}.
+     *
+     * @throws JsonMappingException if there are fatal problems with
+     *   accessing suitable key deserializer; including that of not
+     *   finding any serializer
+     */
+    public KeyDeserializer findKeyDeserializer(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type)
+        throws JsonMappingException
+    {
+        KeyDeserializer kd = factory.createKeyDeserializer(ctxt, type);
+        if (kd == null) { // if none found, need to use a placeholder that'll fail
+            return _handleUnknownKeyDeserializer(type);
+        }
+        // First: need to resolve?
+        if (kd instanceof ResolvableDeserializer) {
+            ((ResolvableDeserializer) kd).resolve(ctxt);
+        }
+        return kd;
+    }
+
+    /**
+     * Method called to find out whether provider would be able to find
+     * a deserializer for given type, using a root reference (i.e. not
+     * through fields or membership in an array or collection)
+     */
+    public boolean hasValueDeserializerFor(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type)
+    {
+        /* Note: mostly copied from findValueDeserializer, except for
+         * handling of unknown types
+         */
+        JsonDeserializer<Object> deser = _findCachedDeserializer(type);
+        if (deser == null) {
+            try {
+                deser = _createAndCacheValueDeserializer(ctxt, factory, type);
+            } catch (Exception e) {
+                return false;
+            }
+        }
+        return (deser != null);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods that handle cache lookups
+    /**********************************************************
+     */
+
+    protected JsonDeserializer<Object> _findCachedDeserializer(JavaType type)
+    {
+        if (type == null) {
+            throw new IllegalArgumentException("Null JavaType passed");
+        }
+        return _cachedDeserializers.get(type);
+    }
+
+    /**
+     * Method that will try to create a deserializer for given type,
+     * and resolve and cache it if necessary
+     * 
+     * @param ctxt Currently active deserialization context
+     * @param type Type of property to deserialize
+     */
+    protected JsonDeserializer<Object>_createAndCacheValueDeserializer(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type)
+        throws JsonMappingException
+    {
+        /* Only one thread to construct deserializers at any given point in time;
+         * limitations necessary to ensure that only completely initialized ones
+         * are visible and used.
+         */
+        synchronized (_incompleteDeserializers) {
+            // Ok, then: could it be that due to a race condition, deserializer can now be found?
+            JsonDeserializer<Object> deser = _findCachedDeserializer(type);
+            if (deser != null) {
+                return deser;
+            }
+            int count = _incompleteDeserializers.size();
+            // Or perhaps being resolved right now?
+            if (count > 0) {
+                deser = _incompleteDeserializers.get(type);
+                if (deser != null) {
+                    return deser;
+                }
+            }
+            // Nope: need to create and possibly cache
+            try {
+                return _createAndCache2(ctxt, factory, type);
+            } finally {
+                // also: any deserializers that have been created are complete by now
+                if (count == 0 && _incompleteDeserializers.size() > 0) {
+                    _incompleteDeserializers.clear();
+                }
+            }
+        }
+    }
+
+    /**
+     * Method that handles actual construction (via factory) and caching (both
+     * intermediate and eventual)
+     */
+    protected JsonDeserializer<Object> _createAndCache2(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type)
+        throws JsonMappingException
+    {
+        JsonDeserializer<Object> deser;
+        try {
+            deser = _createDeserializer(ctxt, factory, type);
+        } catch (IllegalArgumentException iae) {
+            /* We better only expose checked exceptions, since those
+             * are what caller is expected to handle
+             */
+            throw new JsonMappingException(iae.getMessage(), null, iae);
+        }
+        if (deser == null) {
+            return null;
+        }
+        /* cache resulting deserializer? always true for "plain" BeanDeserializer
+         * (but can be re-defined for sub-classes by using @JsonCachable!)
+         */
+        // 08-Jun-2010, tatu: Related to [JACKSON-296], need to avoid caching MapSerializers... so:
+        boolean isResolvable = (deser instanceof ResolvableDeserializer);
+        boolean addToCache = deser.isCachable();
+
+        /* we will temporarily hold on to all created deserializers (to
+         * handle cyclic references, and possibly reuse non-cached
+         * deserializers (list, map))
+         */
+        /* 07-Jun-2010, tatu: Danger: [JACKSON-296] was caused by accidental
+         *   resolution of a reference -- couple of ways to prevent this;
+         *   either not add Lists or Maps, or clear references eagerly.
+         *   Let's actually do both; since both seem reasonable.
+         */
+        /* Need to resolve? Mostly done for bean deserializers; required for
+         * resolving cyclic references.
+         */
+        if (isResolvable) {
+            _incompleteDeserializers.put(type, deser);
+            ((ResolvableDeserializer)deser).resolve(ctxt);
+            _incompleteDeserializers.remove(type);
+        }
+        if (addToCache) {
+            _cachedDeserializers.put(type, deser);
+        }
+        return deser;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for actual construction of deserializers
+    /**********************************************************
+     */
+    
+    /**
+     * Method that does the heavy lifting of checking for per-type annotations,
+     * find out full type, and figure out which actual factory method
+     * to call.
+     */
+    @SuppressWarnings("unchecked")
+    protected JsonDeserializer<Object> _createDeserializer(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+
+        // First things first: do we need to use abstract type mapping?
+        if (type.isAbstract() || type.isMapLikeType() || type.isCollectionLikeType()) {
+            type = factory.mapAbstractType(config, type);
+        }
+        BeanDescription beanDesc = config.introspect(type);
+        // Then: does type define explicit deserializer to use, with annotation(s)?
+        JsonDeserializer<Object> deser = findDeserializerFromAnnotation(ctxt,
+                beanDesc.getClassInfo());
+        if (deser != null) {
+            return deser;
+        }
+
+        // If not, may have further type-modification annotations to check:
+        JavaType newType = modifyTypeByAnnotation(ctxt, beanDesc.getClassInfo(), type);
+        if (newType != type) {
+            type = newType;
+            beanDesc = config.introspect(newType);
+        }
+
+        // We may also have a Builder type to consider...
+        Class<?> builder = beanDesc.findPOJOBuilder();
+        if (builder != null) {
+            return (JsonDeserializer<Object>) factory.createBuilderBasedDeserializer(
+            		ctxt, type, beanDesc, builder);
+        }
+
+        // Or perhaps a Converter?
+        Converter<Object,Object> conv = beanDesc.findDeserializationConverter();
+        if (conv == null) { // nope, just construct in normal way
+            return (JsonDeserializer<Object>) _createDeserializer2(ctxt, factory, type, beanDesc);
+        }
+        // otherwise need to do bit of introspection
+        JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
+        return new StdDelegatingDeserializer<Object>(conv, delegateType,
+                _createDeserializer2(ctxt, factory, delegateType, beanDesc));
+    }
+
+    protected JsonDeserializer<?> _createDeserializer2(DeserializationContext ctxt,
+            DeserializerFactory factory, JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        final DeserializationConfig config = ctxt.getConfig();
+        // If not, let's see which factory method to use:
+        if (type.isEnumType()) {
+            return factory.createEnumDeserializer(ctxt, type, beanDesc);
+        }
+        if (type.isContainerType()) {
+            if (type.isArrayType()) {
+                return factory.createArrayDeserializer(ctxt, (ArrayType) type, beanDesc);
+            }
+            if (type.isMapLikeType()) {
+                MapLikeType mlt = (MapLikeType) type;
+                if (mlt.isTrueMapType()) {
+                    return factory.createMapDeserializer(ctxt,(MapType) mlt, beanDesc);
+                }
+                return factory.createMapLikeDeserializer(ctxt, mlt, beanDesc);
+            }
+            if (type.isCollectionLikeType()) {
+                /* 03-Aug-2012, tatu: As per [Issue#40], one exception is if shape
+                 *   is to be Shape.OBJECT. 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.
+                 */
+                JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+                if (format == null || format.getShape() != JsonFormat.Shape.OBJECT) {
+                    CollectionLikeType clt = (CollectionLikeType) type;
+                    if (clt.isTrueCollectionType()) {
+                        return factory.createCollectionDeserializer(ctxt, (CollectionType) clt, beanDesc);
+                    }
+                    return factory.createCollectionLikeDeserializer(ctxt, clt, beanDesc);
+                }
+            }
+        }
+        if (JsonNode.class.isAssignableFrom(type.getRawClass())) {
+            return factory.createTreeDeserializer(config, type, beanDesc);
+        }
+        return factory.createBeanDeserializer(ctxt, type, beanDesc);
+    }
+
+    /**
+     * Helper method called to check if a class or method
+     * has annotation that tells which class to use for deserialization.
+     * Returns null if no such annotation found.
+     */
+    protected JsonDeserializer<Object> findDeserializerFromAnnotation(DeserializationContext ctxt,
+            Annotated ann)
+        throws JsonMappingException
+    {
+        Object deserDef = ctxt.getAnnotationIntrospector().findDeserializer(ann);
+        if (deserDef == null) {
+            return null;
+        }
+        JsonDeserializer<Object> deser = ctxt.deserializerInstance(ann, deserDef);
+        // One more thing however: may need to also apply a converter:
+        return findConvertingDeserializer(ctxt, ann, deser);
+    }
+
+    /**
+     * Helper method that will check whether given annotated entity (usually class,
+     * but may also be a property accessor) indicates that a {@link Converter} is to
+     * be used; and if so, to construct and return suitable serializer for it.
+     * If not, will simply return given serializer as is.
+     */
+    protected JsonDeserializer<Object> findConvertingDeserializer(DeserializationContext ctxt,
+            Annotated a, JsonDeserializer<Object> deser)
+        throws JsonMappingException
+    {
+        Converter<Object,Object> conv = findConverter(ctxt, a);
+        if (conv == null) {
+            return deser;
+        }
+        JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
+        return (JsonDeserializer<Object>) new StdDelegatingDeserializer<Object>(conv, delegateType, deser);
+    }
+
+    protected Converter<Object,Object> findConverter(DeserializationContext ctxt,
+            Annotated a)
+        throws JsonMappingException
+    {
+        Object convDef = ctxt.getAnnotationIntrospector().findDeserializationConverter(a);
+        if (convDef == null) {
+            return null;
+        }
+        return ctxt.converterInstance(a, convDef);
+    }    
+    /**
+     * Method called to see if given method has annotations that indicate
+     * a more specific type than what the argument specifies.
+     * If annotations are present, they must specify compatible Class;
+     * instance of which can be assigned using the method. This means
+     * that the Class has to be raw class of type, or its sub-class
+     * (or, implementing class if original Class instance is an interface).
+     *
+     * @param a Method or field that the type is associated with
+     * @param type Type derived from the setter argument
+     *
+     * @return Original type if no annotations are present; or a more
+     *   specific type derived from it if type annotation(s) was found
+     *
+     * @throws JsonMappingException if invalid annotation is found
+     */
+    private JavaType modifyTypeByAnnotation(DeserializationContext ctxt,
+            Annotated a, JavaType type)
+        throws JsonMappingException
+    {
+        // first: let's check class for the instance itself:
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        Class<?> subclass = intr.findDeserializationType(a, type);
+        if (subclass != null) {
+            try {
+                type = type.narrowBy(subclass);
+            } catch (IllegalArgumentException iae) {
+                throw new JsonMappingException("Failed to narrow type "+type+" with concrete-type annotation (value "+subclass.getName()+"), method '"+a.getName()+"': "+iae.getMessage(), null, iae);
+            }
+        }
+
+        // then key class
+        if (type.isContainerType()) {
+            Class<?> keyClass = intr.findDeserializationKeyType(a, type.getKeyType());
+            if (keyClass != null) {
+                // illegal to use on non-Maps
+                if (!(type instanceof MapLikeType)) {
+                    throw new JsonMappingException("Illegal key-type annotation: type "+type+" is not a Map(-like) type");
+                }
+                try {
+                    type = ((MapLikeType) type).narrowKey(keyClass);
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException("Failed to narrow key type "+type+" with key-type annotation ("+keyClass.getName()+"): "+iae.getMessage(), null, iae);
+                }
+            }
+            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);
+                if (kdDef != null) {
+                    KeyDeserializer kd = ctxt.keyDeserializerInstance(a, kdDef);
+                    if (kd != null) {
+                        type = ((MapLikeType) type).withKeyValueHandler(kd);
+                        keyType = type.getKeyType(); // just in case it's used below
+                    }
+                }
+            }            
+            
+            // and finally content class; only applicable to structured types
+            Class<?> cc = intr.findDeserializationContentType(a, type.getContentType());
+            if (cc != null) {
+                try {
+                    type = type.narrowContentsBy(cc);
+                } catch (IllegalArgumentException iae) {
+                    throw new JsonMappingException("Failed to narrow content type "+type+" with content-type annotation ("+cc.getName()+"): "+iae.getMessage(), null, iae);
+                }
+            }
+            // ... as well as deserializer for contents:
+            JavaType contentType = type.getContentType();
+            if (contentType.getValueHandler() == null) { // as with above, avoid resetting (which would trigger exception)
+                Object cdDef = intr.findContentDeserializer(a);
+                if (cdDef != null) {
+                    JsonDeserializer<?> cd = null;
+                    if (cdDef instanceof JsonDeserializer<?>) {
+                        cdDef = (JsonDeserializer<?>) cdDef;
+                    } else {
+                        Class<?> cdClass = _verifyAsClass(cdDef, "findContentDeserializer", JsonDeserializer.None.class);
+                        if (cdClass != null) {
+                            cd = ctxt.deserializerInstance(a, cdClass);
+                        }
+                    }
+                    if (cd != null) {
+                        type = type.withContentValueHandler(cd);
+                    }
+                }
+            }
+        }
+        return type;
+    }
+
+    private Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
+    {
+        if (src == null) {
+            return null;
+        }
+        if (!(src instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class<JsonSerializer> instead");
+        }
+        Class<?> cls = (Class<?>) src;
+        if (cls == noneClass || cls == NoClass.class) {
+            return null;
+        }
+        return cls;
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable error reporting methods
+    /**********************************************************
+     */
+
+    protected JsonDeserializer<Object> _handleUnknownValueDeserializer(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)) {
+            throw new JsonMappingException("Can not find a Value deserializer for abstract type "+type);
+        }
+        throw new JsonMappingException("Can not find a Value deserializer for type "+type);
+    }
+
+    protected KeyDeserializer _handleUnknownKeyDeserializer(JavaType type)
+        throws JsonMappingException
+    {
+        throw new JsonMappingException("Can not 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
new file mode 100644
index 0000000..ae2f364
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/DeserializerFactory.java
@@ -0,0 +1,198 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.*;
+
+/**
+ * Abstract class that defines API used by {@link DeserializationContext}
+ * to construct actual
+ * {@link JsonDeserializer} instances (which are then cached by
+ * context and/or dedicated cache).
+ *<p>
+ * Since there are multiple broad categories of deserializers, there are 
+ * multiple factory methods:
+ *<ul>
+ * <li>For JSON "Array" type, we need 2 methods: one to deal with expected
+ *   Java arrays ({@link #createArrayDeserializer})
+ *   and the other for other Java containers like {@link java.util.List}s
+ *   and {@link java.util.Set}s ({@link #createCollectionDeserializer}).
+ *   Actually there is also a third method for "Collection-like" types;
+ *   things like Scala collections that act like JDK collections but do not
+ *   implement same interfaces.
+ *  </li>
+ * <li>For JSON "Object" type, we need 2 methods: one to deal with
+ *   expected Java {@link java.util.Map}s
+ *   ({@link #createMapDeserializer}), and another for POJOs
+ *   ({@link #createBeanDeserializer}.
+ *   As an additional twist there is also a callback for "Map-like" types,
+ *   mostly to make it possible to support Scala Maps (which are NOT JDK
+ *   Map compatible).
+ *  </li>
+ * <li>For Tree Model ({@link com.fasterxml.jackson.databind.JsonNode}) properties there is
+ *    {@link #createTreeDeserializer}
+ * <li>For enumerated types ({@link java.lang.Enum}) there is
+ *    {@link #createEnumDeserializer}
+ *  </li>
+ * <li>For all other types, {@link #createBeanDeserializer} is used.
+ * </ul>
+ *<p>
+ */
+public abstract class DeserializerFactory
+{
+    protected final static Deserializers[] NO_DESERIALIZERS = new Deserializers[0];
+
+    /*
+    /********************************************************
+    /* Configuration handling
+    /********************************************************
+     */
+
+    /**
+     * Convenience method for creating a new factory instance with additional deserializer
+     * provider.
+     */
+    public abstract DeserializerFactory withAdditionalDeserializers(Deserializers additional);
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link KeyDeserializers}.
+     */
+    public abstract DeserializerFactory withAdditionalKeyDeserializers(KeyDeserializers additional);
+    
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link BeanDeserializerModifier}.
+     */
+    public abstract DeserializerFactory withDeserializerModifier(BeanDeserializerModifier modifier);
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link AbstractTypeResolver}.
+     */
+    public abstract DeserializerFactory withAbstractTypeResolver(AbstractTypeResolver resolver);
+
+    /**
+     * Convenience method for creating a new factory instance with additional
+     * {@link ValueInstantiators}.
+     */
+    public abstract DeserializerFactory withValueInstantiators(ValueInstantiators instantiators);
+    
+    /*
+    /**********************************************************
+    /* Basic DeserializerFactory API:
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to try to resolve an abstract type
+     * (interface, abstract class) into a concrete type, or at least
+     * something "more concrete" (abstract class instead of interface).
+     * Will either return passed type, or a more specific type.
+     */
+    public abstract JavaType mapAbstractType(DeserializationConfig config, JavaType type)
+        throws JsonMappingException;
+
+    /**
+     * Method that is to find all creators (constructors, factory methods)
+     * for the bean type to deserialize.
+     */
+    public abstract ValueInstantiator findValueInstantiator(DeserializationContext ctxt,
+            BeanDescription beanDesc)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to create (or, for completely immutable deserializers,
+     * reuse) a deserializer that can convert JSON content into values of
+     * specified Java "bean" (POJO) type.
+     * At this point it is known that the type is not otherwise recognized
+     * as one of structured types (array, Collection, Map) or a well-known
+     * JDK type (enum, primitives/wrappers, String); this method only
+     * gets called if other options are exhausted. This also means that
+     * this method can be overridden to add support for custom types.
+     *
+     * @param type Type to be deserialized
+     */
+    public abstract JsonDeserializer<Object> createBeanDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    /**
+     * Method called to create a deserializer that will use specified Builder
+     * class for building value instances.
+     * 
+     * @since 2.0
+     */
+    public abstract JsonDeserializer<Object> createBuilderBasedDeserializer(
+    		DeserializationContext ctxt, JavaType type, BeanDescription beanDesc,
+    		Class<?> builderClass)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to create (or, for completely immutable deserializers,
+     * reuse) a deserializer that can convert JSON content into values of
+     * specified Java type.
+     *
+     * @param type Type to be deserialized
+     */
+    public abstract JsonDeserializer<?> createArrayDeserializer(DeserializationContext ctxt,
+            ArrayType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    public abstract JsonDeserializer<?> createCollectionDeserializer(DeserializationContext ctxt,
+            CollectionType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    public abstract JsonDeserializer<?> createCollectionLikeDeserializer(DeserializationContext ctxt,
+            CollectionLikeType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+    
+    public abstract JsonDeserializer<?> createEnumDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    public abstract JsonDeserializer<?> createMapDeserializer(DeserializationContext ctxt,
+            MapType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    public abstract JsonDeserializer<?> createMapLikeDeserializer(DeserializationContext ctxt,
+            MapLikeType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    /**
+     * Method called to create and return a deserializer that can construct
+     * JsonNode(s) from JSON content.
+     */
+    public abstract JsonDeserializer<?> createTreeDeserializer(DeserializationConfig config,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    /**
+     * Method called to find if factory knows how to create a key deserializer
+     * for specified type; currently this means checking if a module has registered
+     * possible deserializers.
+     * 
+     * @return Key deserializer to use for specified type, if one found; null if not
+     *   (and default key deserializer should be used)
+     */
+    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),
+     * should return null.
+     *<p>
+     * Note that this method is usually only directly called for values of container (Collection,
+     * array, Map) types and root values, but not for bean property values.
+     *
+     * @param baseType Declared base type of the value to deserializer (actual
+     *    deserializer type will be this type or its subtype)
+     * 
+     * @return Type deserializer to use for given base type, if one is needed; null if not.
+     */
+    public abstract TypeDeserializer findTypeDeserializer(DeserializationConfig config,
+            JavaType baseType)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java
new file mode 100644
index 0000000..3ae086f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/Deserializers.java
@@ -0,0 +1,299 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.*;
+
+/**
+ * Interface that defines API for simple extensions that can provide additional deserializers
+ * for various types. Access is by a single callback method; instance is to either return
+ * a configured {@link JsonDeserializer} for specified type, or null to indicate that it
+ * does not support handling of the type. In latter case, further calls can be made
+ * for other providers; in former case returned deserializer is used for handling of
+ * instances of specified type.
+ *<p>
+ * Unlike with {@link com.fasterxml.jackson.databind.ser.Serializers},
+ * multiple different methods are used since different kinds of types typically
+ * require different kinds of inputs.
+ */
+public interface Deserializers
+{
+    /**
+     * Method called to locate serializer for specified array type.
+     *<p>
+     * Deserializer for element type may be passed, if configured explicitly at higher level (by
+     * annotations, typically), but usually are not.
+     * Type deserializer for element is passed if one is needed based on contextual information
+     * (annotations on declared element class; or on field or method type is associated with).
+     * 
+     * @param type Type of array instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * @param elementTypeDeserializer If element type needs polymorphic type handling, this is
+     *    the type information deserializer to use; should usually be used as is when constructing
+     *    array deserializer.
+     * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using
+     *    annotations, for exmple). May be null, in which case it should be resolved here (or using
+     *    {@link ResolvableDeserializer} callback)
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findArrayDeserializer(ArrayType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException;
+
+    /**
+     * Method called to locate serializer for specified {@link java.util.Collection} (List, Set etc) type.
+     *<p>
+     * Deserializer for element type may be passed, if configured explicitly at higher level (by
+     * annotations, typically), but usually are not.
+     * Type deserializer for element is passed if one is needed based on contextual information
+     * (annotations on declared element class; or on field or method type is associated with).
+     * 
+     * @param type Type of collection instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * @param elementTypeDeserializer If element type needs polymorphic type handling, this is
+     *    the type information deserializer to use; should usually be used as is when constructing
+     *    array deserializer.
+     * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using
+     *    annotations, for exmple). May be null, in which case it should be resolved here (or using
+     *    {@link ResolvableDeserializer} callback)
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findCollectionDeserializer(CollectionType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException;
+
+    /**
+     * Method called to locate serializer for specified
+     * "Collection-like" type (one that acts
+     * like {@link java.util.Collection} but does not implement it).
+     *<p>
+     * Deserializer for element type may be passed, if configured explicitly at higher level (by
+     * annotations, typically), but usually are not.
+     * Type deserializer for element is passed if one is needed based on contextual information
+     * (annotations on declared element class; or on field or method type is associated with).
+     * 
+     * @param type Type of instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * @param elementTypeDeserializer If element type needs polymorphic type handling, this is
+     *    the type information deserializer to use; should usually be used as is when constructing
+     *    array deserializer.
+     * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using
+     *    annotations, for exmple). May be null, in which case it should be resolved here (or using
+     *    {@link ResolvableDeserializer} callback)
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findCollectionLikeDeserializer(CollectionLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to locate deserializer for specified {@link java.lang.Enum} type.
+     * 
+     * @param type Type of {@link java.lang.Enum} instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findEnumDeserializer(Class<?> type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    /**
+     * Method called to locate deserializer for specified {@link java.util.Map} type.
+     *<p>
+     * Deserializer for element type may be passed, if configured explicitly at higher level (by
+     * annotations, typically), but usually are not.
+     * Type deserializer for element is passed if one is needed based on contextual information
+     * (annotations on declared element class; or on field or method type is associated with).
+     *<p>
+     * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is
+     * a specific configuration override (annotations) to indicate instance to use.
+     * Otherwise null is passed, and key deserializer needs to be obtained later during
+     * resolution (using {@link ResolvableDeserializer#resolve}).
+     * 
+     * @param type Type of {@link java.util.Map} instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * @param keyDeserializer Key deserializer use, if it is defined via annotations or other configuration;
+     *    null if default key deserializer for key type can be used.
+     * @param elementTypeDeserializer If element type needs polymorphic type handling, this is
+     *    the type information deserializer to use; should usually be used as is when constructing
+     *    array deserializer.
+     * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using
+     *    annotations, for exmple). May be null, in which case it should be resolved here (or using
+     *    {@link ResolvableDeserializer} callback)
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findMapDeserializer(MapType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException;
+
+    /**
+     * Method called to locate serializer for specified
+     * "Map-like" type (one that acts
+     * like {@link java.util.Map} but does not implement it).
+     *<p>
+     * Deserializer for element type may be passed, if configured explicitly at higher level (by
+     * annotations, typically), but usually are not.
+     * Type deserializer for element is passed if one is needed based on contextual information
+     * (annotations on declared element class; or on field or method type is associated with).
+     *<p>
+     * Similarly, a {@link KeyDeserializer} may be passed, but this is only done if there is
+     * a specific configuration override (annotations) to indicate instance to use.
+     * Otherwise null is passed, and key deserializer needs to be obtained later during
+     * resolution (using {@link ResolvableDeserializer#resolve}).
+     * 
+     * @param type Type of {@link java.util.Map} instances to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * @param keyDeserializer Key deserializer use, if it is defined via annotations or other configuration;
+     *    null if default key deserializer for key type can be used.
+     * @param elementTypeDeserializer If element type needs polymorphic type handling, this is
+     *    the type information deserializer to use; should usually be used as is when constructing
+     *    array deserializer.
+     * @param elementDeserializer Deserializer to use for elements, if explicitly defined (by using
+     *    annotations, for exmple). May be null, in which case it should be resolved here (or using
+     *    {@link ResolvableDeserializer} callback)
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to locate deserializer for specified JSON tree node type.
+     * 
+     * @param nodeType Specific type of JSON tree nodes to deserialize
+     *  (subtype of {@link com.fasterxml.jackson.databind.JsonNode})
+     * @param config Configuration in effect
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findTreeNodeDeserializer(Class<? extends JsonNode> nodeType,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to locate deserializer for specified value type which does not belong to any other
+     * category (not an Enum, Collection, Map, Array or tree node)
+     * 
+     * @param type Bean type to deserialize
+     * @param config Configuration in effect
+     * @param beanDesc Definition of the enumeration type that contains class annotations and
+     *    other information typically needed for building deserializers
+     * 
+     * @return Deserializer to use for the type; or null if this provider does not know how to construct it
+     */
+    public JsonDeserializer<?> findBeanDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException;
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Basic {@link Deserializers} implementation that implements all methods but provides
+     * no deserializers. Its main purpose is to serve as a base class so that
+     * sub-classes only need to override methods they need, as most of the time some
+     * of methods are not needed (especially enumeration and array deserializers are
+     * very rarely overridden).
+     */
+    public static class Base implements Deserializers
+    {
+        @Override
+        public JsonDeserializer<?> findArrayDeserializer(ArrayType type,
+                DeserializationConfig config, BeanDescription beanDesc,
+                TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findCollectionDeserializer(CollectionType type,
+                DeserializationConfig config, BeanDescription beanDesc,
+                TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findCollectionLikeDeserializer(CollectionLikeType type,
+                DeserializationConfig config, BeanDescription beanDesc,
+                TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findMapDeserializer(MapType type,
+                DeserializationConfig config, BeanDescription beanDesc,
+                KeyDeserializer keyDeserializer,
+                TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
+                DeserializationConfig config, BeanDescription beanDesc,
+                KeyDeserializer keyDeserializer,
+                TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findEnumDeserializer(Class<?> type,
+                DeserializationConfig config, BeanDescription beanDesc)
+            throws JsonMappingException
+        {
+            return null;
+        }
+        
+        @Override
+        public JsonDeserializer<?> findTreeNodeDeserializer(Class<? extends JsonNode> nodeType,
+                DeserializationConfig config, BeanDescription beanDesc)
+            throws JsonMappingException
+        {
+            return null;
+        }
+
+        @Override
+        public JsonDeserializer<?> findBeanDeserializer(JavaType type,
+                DeserializationConfig config, BeanDescription beanDesc)
+            throws JsonMappingException
+        {
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/KeyDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/KeyDeserializers.java
new file mode 100644
index 0000000..652170b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/KeyDeserializers.java
@@ -0,0 +1,19 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Interface that defines API for simple extensions that can provide additional deserializers
+ * for deserializer Map keys of various types, from JSON property names.
+ * Access is by a single callback method; instance is to either return
+ * a configured {@link KeyDeserializer} for specified type, or null to indicate that it
+ * does not support handling of the type. In latter case, further calls can be made
+ * for other providers; in former case returned key deserializer is used for handling of
+ * key instances of specified type.
+ */
+public interface KeyDeserializers
+{
+    public KeyDeserializer findKeyDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java
new file mode 100644
index 0000000..0d97b2d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ResolvableDeserializer.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Interface used to indicate deserializers that want to do post-processing
+ * after construction but before being returned to caller (and possibly cached)
+ * and used.
+ * This is typically used to resolve references
+ * to other contained types; for example, bean deserializers use this callback
+ * to locate deserializers for contained field types.
+ * Main reason for using a callback (instead of trying to resolve dependencies
+ * immediately) is to make it possible to cleanly handle self-references;
+ * otherwise it would be easy to get into infinite recursion.
+ *<p>
+ * Note that {@link #resolve} method does NOT allow returning anything 
+ * (specifically, a new deserializer instance): reason for this is that
+ * allowing this would not work with proper handling of cyclic dependencies,
+ * which are resolved by two-phase processing, where initially constructed
+ * deserializer is added as known deserializer, and only after this
+ * resolution is done. Resolution is the part that results in lookups for
+ * dependant deserializers, which may include handling references to
+ * deserializer itself.
+ *<p>
+ * Note that in cases where deserializer needs both contextualization and
+ * resolution -- that is, implements both this interface and {@link ContextualDeserializer}
+ * -- resolution via this interface occurs first, and contextual
+ * resolution (using {@link ContextualDeserializer}) later on.
+ */
+public interface ResolvableDeserializer
+{
+    /**
+     * Method called after deserializer instance has been constructed
+     * (and registered as necessary by provider objects),
+     * but before it has returned it to the caller.
+     * Called object can then resolve its dependencies to other types,
+     * including self-references (direct or indirect).
+     *
+     * @param ctxt Context to use for accessing configuration, resolving
+     *    secondary deserializers
+     */
+    public abstract void resolve(DeserializationContext ctxt)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
new file mode 100644
index 0000000..9d90d42
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableAnyProperty.java
@@ -0,0 +1,155 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.lang.reflect.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.BeanProperty;
+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.introspect.AnnotatedMethod;
+
+/**
+ * Class that represents a "wildcard" set method which can be used
+ * to generically set values of otherwise unmapped (aka "unknown")
+ * properties read from Json content.
+ *<p>
+ * !!! Note: might make sense to refactor to share some code
+ * with {@link SettableBeanProperty}?
+ */
+public final class SettableAnyProperty
+{
+    /**
+     * Method used for setting "any" properties, along with annotation
+     * information. Retained to allow contextualization of any properties.
+     */
+    final protected BeanProperty _property;
+    
+    /**
+     * Physical JDK object used for assigning properties.
+     */
+    final protected Method _setter;
+
+    final protected JavaType _type;
+
+    protected JsonDeserializer<Object> _valueDeserializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public SettableAnyProperty(BeanProperty property, AnnotatedMethod setter, JavaType type,
+            JsonDeserializer<Object> valueDeser) {
+        this(property, setter.getAnnotated(), type, valueDeser);
+    }
+
+    public SettableAnyProperty(BeanProperty property, Method rawSetter, JavaType type,
+            JsonDeserializer<Object> valueDeser) {
+        _property = property;
+        _type = type;
+        _setter = rawSetter;
+        _valueDeserializer = valueDeser;
+    }
+
+    public SettableAnyProperty withValueDeserializer(JsonDeserializer<Object> deser) {
+        return new SettableAnyProperty(_property, _setter, _type, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, accessors
+    /**********************************************************
+     */
+
+    public BeanProperty getProperty() { return _property; }
+    
+    public boolean hasValueDeserializer() { return (_valueDeserializer != null); }
+
+    public JavaType getType() { return _type; }
+
+    /*
+    /**********************************************************
+    /* Public API, deserialization
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to deserialize appropriate value, given parser (and
+     * context), and set it using appropriate method (a setter method).
+     */
+    public final void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+                                        Object instance, String propName)
+        throws IOException, JsonProcessingException
+    {
+        set(instance, propName, deserialize(jp, ctxt));
+    }
+
+    public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NULL) {
+            return null;
+        }
+        return _valueDeserializer.deserialize(jp, ctxt);
+    }
+
+    public final void set(Object instance, String propName, Object value)
+        throws IOException
+    {
+        try {
+            _setter.invoke(instance, propName, value);
+        } catch (Exception e) {
+            _throwAsIOE(e, propName, value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * @param e Exception to re-throw or wrap
+     * @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)
+        throws IOException
+    {
+        if (e instanceof IllegalArgumentException) {
+            String actType = (value == null) ? "[NULL]" : value.getClass().getName();
+            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(")");
+            String origMsg = e.getMessage();
+            if (origMsg != null) {
+                msg.append(", problem: ").append(origMsg);
+            } else {
+                msg.append(" (no error message provided)");
+            }
+            throw new JsonMappingException(msg.toString(), null, e);
+        }
+        if (e instanceof IOException) {
+            throw (IOException) e;
+        }
+        if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        }
+        // let's wrap the innermost problem
+        Throwable t = e;
+        while (t.getCause() != null) {
+            t = t.getCause();
+        }
+        throw new JsonMappingException(t.getMessage(), null, t);
+    }
+
+    private String getClassName() { return _setter.getDeclaringClass().getName(); }
+
+    @Override public String toString() { return "[any property on class "+getClassName()+"]"; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
new file mode 100644
index 0000000..892a8f1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/SettableBeanProperty.java
@@ -0,0 +1,514 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.util.InternCache;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.impl.NullProvider;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.impl.FailingDeserializer;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ViewMatcher;
+
+/**
+ * Base class for deserilizable properties of a bean: contains
+ * both type and name definitions, and reflection-based set functionality.
+ * Concrete sub-classes implement details, so that field- and
+ * setter-backed properties, as well as a few more esoteric variations,
+ * can be handled.
+ */
+public abstract class SettableBeanProperty
+    implements BeanProperty,
+        java.io.Serializable
+{
+    private static final long serialVersionUID = -1026580169193933453L;
+
+    /**
+     * To avoid nasty NPEs, let's use a placeholder for _valueDeserializer,
+     * if real deserializer is not (yet) available.
+     * 
+     * @since 2.2
+     */
+    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)
+     */
+    protected final String _propName;
+
+    /**
+     * Base type for property; may be a supertype of actual value.
+     */
+    protected final JavaType _type;
+
+    /**
+     * @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.
+     */
+    protected JsonDeserializer<Object> _valueDeserializer;
+
+    /**
+     * If value will contain type information (to support
+     * polymorphic handling), this is the type deserializer
+     * used to handle type resolution.
+     */
+    protected final TypeDeserializer _valueTypeDeserializer;
+    
+    /**
+     * Object used to figure out value to be used when 'null' literal is encountered in JSON.
+     * For most types simply Java null, but for primitive types must
+     * be a non-null value (like Integer.valueOf(0) for int).
+     */
+    protected final NullProvider _nullProvider;
+
+    /**
+     * Whether value of this property has been marked as required.
+     * Retained since it will be needed when traversing type hierarchy
+     * for producing schemas (and other similar tasks); currently not
+     * used for serialization.
+     * 
+     * @since 2.2
+     */
+    protected final boolean _isRequired;
+
+    /*
+    /**********************************************************
+    /* Configuration that is not yet immutable; generally assigned
+    /* during initialization process but can not be passed to
+    /* constructor.
+    /**********************************************************
+     */
+
+    /**
+     * If property represents a managed (forward) reference
+     * (see [JACKSON-235]), we will need name of reference for
+     * later linking.
+     *<p>
+     * TODO: should try to make immutable.
+     */
+    protected String _managedReferenceName;
+
+    /**
+     * Helper object used for checking whether this property is to
+     * be included in the active view, if property is view-specific;
+     * null otherwise.
+     *<p>
+     * 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
+     * is arbitrary, but once indexes are assigned they are not
+     * changed.
+     *<p>
+     * TODO: should try to make immutable if at all possible
+     */
+    protected int _propertyIndex = -1;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle (construct & configure)
+    /**********************************************************
+     */
+
+    protected SettableBeanProperty(BeanPropertyDefinition propDef,
+            JavaType type, TypeDeserializer typeDeser, Annotations contextAnnotations)
+    {
+        this(propDef.getName(), type, propDef.getWrapperName(), typeDeser, contextAnnotations,
+                propDef.isRequired());
+    }
+
+    @Deprecated // since 2.2
+    protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper,
+            TypeDeserializer typeDeser, Annotations contextAnnotations)
+    {
+        this(propName, type, wrapper, typeDeser, contextAnnotations, false);
+    }
+    
+    protected SettableBeanProperty(String propName, JavaType type, PropertyName wrapper,
+            TypeDeserializer typeDeser, Annotations contextAnnotations,
+            boolean isRequired)
+    {
+        /* 09-Jan-2009, tatu: Intern()ing makes sense since Jackson parsed
+         *   field names are (usually) interned too, hence lookups will be faster.
+         */
+        // 23-Oct-2009, tatu: should this be disabled wrt [JACKSON-180]?
+        /*   Probably need not, given that namespace of field/method names
+         *   is not unbounded, unlike potential JSON names.
+         */
+        if (propName == null || propName.length() == 0) {
+            _propName = "";
+        } else {
+            _propName = InternCache.instance.intern(propName);
+        }
+        _type = type;
+        _wrapperName = wrapper;
+        _isRequired = isRequired;
+        _contextAnnotations = contextAnnotations;
+        _viewMatcher = null;
+        _nullProvider = null;
+
+        // 30-Jan-2012, tatu: Important: contextualize TypeDeserializer now...
+        if (typeDeser != null) {
+            typeDeser = typeDeser.forProperty(this);
+        }
+        _valueTypeDeserializer = typeDeser;
+        _valueDeserializer = MISSING_VALUE_DESERIALIZER;
+    }
+
+    /**
+     * Basic copy-constructor for sub-classes to use.
+     */
+    protected SettableBeanProperty(SettableBeanProperty src)
+    {
+        _propName = src._propName;
+        _type = src._type;
+        _wrapperName = src._wrapperName;
+        _isRequired = src._isRequired;
+        _contextAnnotations = src._contextAnnotations;
+        _valueDeserializer = src._valueDeserializer;
+        _valueTypeDeserializer = src._valueTypeDeserializer;
+        _nullProvider = src._nullProvider;
+        _managedReferenceName = src._managedReferenceName;
+        _propertyIndex = src._propertyIndex;
+        _viewMatcher = src._viewMatcher;
+    }
+
+    /**
+     * Copy-with-deserializer-change constructor for sub-classes to use.
+     */
+    @SuppressWarnings("unchecked")
+    protected SettableBeanProperty(SettableBeanProperty src, JsonDeserializer<?> deser)
+    {
+        _propName = src._propName;
+        _type = src._type;
+        _wrapperName = src._wrapperName;
+        _isRequired = src._isRequired;
+        _contextAnnotations = src._contextAnnotations;
+        _valueTypeDeserializer = src._valueTypeDeserializer;
+        _managedReferenceName = src._managedReferenceName;
+        _propertyIndex = src._propertyIndex;
+
+        if (deser == null) {
+            _nullProvider = null;
+            _valueDeserializer = MISSING_VALUE_DESERIALIZER;
+        } else {
+            Object nvl = deser.getNullValue();
+            _nullProvider = (nvl == null) ? null : new NullProvider(_type, nvl);
+            _valueDeserializer = (JsonDeserializer<Object>) deser;
+        }
+        _viewMatcher = src._viewMatcher;
+    }
+
+    /**
+     * Copy-with-deserializer-change constructor for sub-classes to use.
+     */
+    protected SettableBeanProperty(SettableBeanProperty src, String newName)
+    {
+        _propName = newName;
+        _type = src._type;
+        _wrapperName = src._wrapperName;
+        _isRequired = src._isRequired;
+        _contextAnnotations = src._contextAnnotations;
+        _valueDeserializer = src._valueDeserializer;
+        _valueTypeDeserializer = src._valueTypeDeserializer;
+        _nullProvider = src._nullProvider;
+        _managedReferenceName = src._managedReferenceName;
+        _propertyIndex = src._propertyIndex;
+        _viewMatcher = src._viewMatcher;
+    }
+
+    /**
+     * Fluent factory method for constructing and returning a new instance
+     * with specified value deserializer.
+     * Note that this method should NOT change configuration of this instance.
+     * 
+     * @param deser Deserializer to assign to the new property instance
+     * 
+     * @return Newly constructed instance, if value deserializer differs from the
+     *   one used for this instance; or 'this' if not.
+     */
+    public abstract SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser);
+
+    /**
+     * Fluent factory method for constructing and returning a new instance
+     * with specified property name.
+     * Note that this method should NOT change configuration of this instance.
+     * 
+     * @param newName Name to use for the new instance.
+     * 
+     * @return Newly constructed instance, if property name differs from the
+     *   one used for this instance; or 'this' if not.
+     */
+    public abstract SettableBeanProperty withName(String newName);
+    
+    public void setManagedReferenceName(String n) {
+        _managedReferenceName = n;
+    }
+    
+    public void setViews(Class<?>[] views) {
+        if (views == null) {
+            _viewMatcher = null;
+        } else {
+            _viewMatcher = ViewMatcher.construct(views);
+        }
+    }
+    
+    /**
+     * Method used to assign index for property.
+     */
+    public void assignIndex(int index) {
+        if (_propertyIndex != -1) {
+            throw new IllegalStateException("Property '"+getName()+"' already had index ("+_propertyIndex+"), trying to assign "+index);
+        }
+        _propertyIndex = index;
+    }
+    
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public final String getName() { return _propName; }
+
+    @Override
+    public boolean isRequired() { return _isRequired; }
+    
+    @Override
+    public JavaType getType() { return _type; }
+
+    @Override
+    public PropertyName getWrapperName() {
+        return _wrapperName;
+    }
+    
+    @Override
+    public abstract <A extends Annotation> A getAnnotation(Class<A> acls);
+
+    @Override
+    public abstract AnnotatedMember getMember();
+
+    @Override
+    public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
+        return _contextAnnotations.get(acls);
+    }
+
+    @Override
+    public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor)
+        throws JsonMappingException
+    {
+        if (isRequired()) {
+            objectVisitor.property(this); 
+        } else {
+            objectVisitor.optionalProperty(this);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    protected final Class<?> getDeclaringClass() {
+        return getMember().getDeclaringClass();
+    }
+
+    public String getManagedReferenceName() { return _managedReferenceName; }
+
+    public boolean hasValueDeserializer() {
+        return (_valueDeserializer != null) && (_valueDeserializer != MISSING_VALUE_DESERIALIZER);
+    }
+
+    public boolean hasValueTypeDeserializer() { return (_valueTypeDeserializer != null); }
+    
+    public JsonDeserializer<Object> getValueDeserializer() {
+        JsonDeserializer<Object> deser = _valueDeserializer;
+        if (deser == MISSING_VALUE_DESERIALIZER) {
+            return null;
+        }
+        return deser;
+    }
+
+    public TypeDeserializer getValueTypeDeserializer() { return _valueTypeDeserializer; }
+
+    public boolean visibleInView(Class<?> activeView) {
+        return (_viewMatcher == null) || _viewMatcher.isVisibleForView(activeView);
+    }
+    
+    public boolean hasViews() { return _viewMatcher != null; }
+    
+    /**
+     * Method for accessing unique index of this property; indexes are
+     * assigned once all properties of a {@link BeanDeserializer} have
+     * been collected.
+     * 
+     * @return Index of this property
+     */
+    public int getPropertyIndex() { return _propertyIndex; }
+
+    /**
+     * Method for accessing index of the creator property: for other
+     * types of properties will simply return -1.
+     * 
+     * @since 2.1
+     */
+    public int getCreatorIndex() { return -1; }
+    
+    /**
+     * Accessor for id of injectable value, if this bean property supports
+     * value injection.
+     */
+    public Object getInjectableValueId() { return null; }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method called to deserialize appropriate value, given parser (and
+     * context), and set it using appropriate mechanism.
+     * Pre-condition is that passed parser must point to the first token
+     * that should be consumed to produce the value (the only value for
+     * scalars, multiple for Objects and Arrays).
+     */
+    public abstract void deserializeAndSet(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException;
+
+	/**
+	 * Alternative to {@link #deserializeAndSet} that returns
+	 * either return value of setter method called (if one is),
+	 * or null to indicate that no return value is available.
+	 * Mostly used to support Builder style deserialization.
+	 *
+	 * @since 2.0
+	 */
+    public abstract Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method called to assign given value to this property, on
+     * specified Object.
+     *<p>
+     * Note: this is an optional operation, not supported by all
+     * implementations, creator-backed properties for example do not
+     * support this method.
+     */
+    public abstract void set(Object instance, Object value)
+        throws IOException;
+
+    /**
+     * Method called to assign given value to this property, on
+     * specified Object, and return whatever delegating accessor
+     * returned (if anything)
+     *<p>
+     * Note: this is an optional operation, not supported by all
+     * implementations, creator-backed properties for example do not
+     * support this method.
+     * 
+     * @since 2.0
+     */
+    public abstract Object setAndReturn(Object instance, Object value)
+            throws IOException;
+    
+    /**
+     * This method is needed by some specialized bean deserializers,
+     * and also called by some {@link #deserializeAndSet} implementations.
+     *<p>
+     * Pre-condition is that passed parser must point to the first token
+     * that should be consumed to produce the value (the only value for
+     * scalars, multiple for Objects and Arrays).
+     *<p> 
+     * Note that this method is final for performance reasons: to override
+     * functionality you must override other methods that call this method;
+     * this method should also not be called directly unless you really know
+     * what you are doing (and probably not even then).
+     */
+    public final Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        
+        if (t == JsonToken.VALUE_NULL) {
+            return (_nullProvider == null) ? null : _nullProvider.nullValue(ctxt);
+        }
+        if (_valueTypeDeserializer != null) {
+            return _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
+        }
+        return _valueDeserializer.deserialize(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that takes in exception of any type, and casts or wraps it
+     * to an IOException or its subclass.
+     */
+    protected void _throwAsIOE(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 origMsg = e.getMessage();
+            if (origMsg != null) {
+                msg.append(", problem: ").append(origMsg);
+            } else {
+                msg.append(" (no error message provided)");
+            }
+            throw new JsonMappingException(msg.toString(), null, e);
+        }
+        _throwAsIOE(e);
+    }
+
+    protected IOException _throwAsIOE(Exception e)
+        throws IOException
+    {
+        if (e instanceof IOException) {
+            throw (IOException) e;
+        }
+        if (e instanceof RuntimeException) {
+            throw (RuntimeException) e;
+        }
+        // let's wrap the innermost problem
+        Throwable th = e;
+        while (th.getCause() != null) {
+            th = th.getCause();
+        }
+        throw new JsonMappingException(th.getMessage(), null, th);
+    }
+
+    @Override public String toString() { return "[property '"+getName()+"']"; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
new file mode 100644
index 0000000..0ca1719
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiator.java
@@ -0,0 +1,292 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+
+/**
+ * Class that defines simple API implemented by objects that create value
+ * instances.  Some or all of properties of value instances may 
+ * be initialized by instantiator, rest being populated by deserializer,
+ * to which value instance is passed.
+ * Since different kinds of JSON values (structured and scalar)
+ * may be bound to Java values, in some cases instantiator
+ * fully defines resulting value; this is the case when JSON value
+ * 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
+ * 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.
+ *<p>
+ * Actual implementations are strongly recommended to be based on
+ * {@link com.fasterxml.jackson.databind.deser.std.StdValueInstantiator}
+ * which implements all methods, and as such will be compatible
+ * across versions even if new methods were added to this interface.
+ */
+public abstract class ValueInstantiator
+{
+    /*
+    /**********************************************************
+    /* Metadata accessors
+    /**********************************************************
+     */
+
+    /**
+     * Method that returns description of the value type this instantiator
+     * handles. Used for error messages, diagnostics.
+     */
+    public abstract String getValueTypeDesc();
+
+    /**
+     * Method that will return true if any of <code>canCreateXxx</code> method
+     * returns true: that is, if there is any way that an instance could
+     * be created.
+     */
+    public boolean canInstantiate() {
+        return
+             canCreateUsingDefault()
+             || canCreateUsingDelegate()
+             || canCreateFromObjectWith()
+             || canCreateFromString()
+             || canCreateFromInt()
+             || canCreateFromLong()
+             || canCreateFromDouble()
+             || canCreateFromBoolean()
+             ;
+    }    
+    
+    /**
+     * Method that can be called to check whether a String-based creator
+     * is available for this instantiator
+     */
+    public boolean canCreateFromString() {
+        return false;
+    }
+
+    /**
+     * Method that can be called to check whether an integer (int, Integer) based
+     * creator is available to use (to call {@link #createFromInt}).
+     */
+    public boolean canCreateFromInt() {
+        return false;
+    }
+
+    /**
+     * Method that can be called to check whether a long (long, Long) based
+     * creator is available to use (to call {@link #createFromLong}).
+     */
+    public boolean canCreateFromLong() {
+        return false;
+    }
+
+    /**
+     * Method that can be called to check whether a double (double / Double) based
+     * creator is available to use (to call {@link #createFromDouble}).
+     */
+    public boolean canCreateFromDouble() {
+        return false;
+    }
+
+    /**
+     * Method that can be called to check whether a double (boolean / Boolean) based
+     * creator is available to use (to call {@link #createFromDouble}).
+     */
+    public boolean canCreateFromBoolean() {
+        return false;
+    }
+    
+    /**
+     * Method that can be called to check whether a default creator (constructor,
+     * or no-arg static factory method)
+     * is available for this instantiator
+     */
+    public boolean canCreateUsingDefault() {
+        return getDefaultCreator() != null;
+    }
+
+    /**
+     * Method that can be called to check whether a delegate-based creator (single-arg
+     * constructor or factory method)
+     * is available for this instantiator
+     */
+    public boolean canCreateUsingDelegate() {
+        return false;
+    }
+
+    /**
+     * Method that can be called to check whether a property-based creator
+     * (argument-taking constructor or factory method)
+     * is available to instantiate values from JSON Object
+     */
+    public boolean canCreateFromObjectWith() {
+        return false;
+    }
+
+    /**
+     * Method called to determine types of instantiation arguments
+     * to use when creating instances with creator arguments
+     * (when {@link #canCreateFromObjectWith()} returns  true).
+     * These arguments are bound from JSON, using specified
+     * property types to locate deserializers.
+     *<p>
+     * NOTE: all properties will be of type
+     * {@link com.fasterxml.jackson.databind.deser.CreatorProperty}.
+     */
+    public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) {
+        return null;
+    }
+
+    /**
+     * Method that can be used to determine what is the type of delegate
+     * type to use, if any; if no delegates are used, will return null.
+     * If non-null type is returned, deserializer will bind JSON into
+     * specified type (using standard deserializer for that type), and
+     * pass that to instantiator.
+     */
+    public JavaType getDelegateType(DeserializationConfig config) {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Instantiation methods for JSON Object
+    /**********************************************************
+     */
+
+    /**
+     * Method called to create value instance from a JSON value when
+     * no data needs to passed to creator (constructor, factory method);
+     * typically this will call the default constructor of the value object.
+     * It will only be used if more specific creator methods are not
+     * applicable; hence "default".
+     *<p>
+     * This method is called if {@link #getFromObjectArguments} returns
+     * null or empty List.
+     */
+    public Object createUsingDefault(DeserializationContext ctxt)
+        throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+"; no default creator found");
+    }
+
+    /**
+     * Method called to create value instance from JSON Object when
+     * instantiation arguments are passed; this is done, for example when passing information
+     * specified with "Creator" annotations.
+     *<p>
+     * This method is called if {@link #getFromObjectArguments} returns
+     * a non-empty List of arguments.
+     */
+    public Object createFromObjectWith(DeserializationContext ctxt, Object[] args)
+        throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" with arguments");
+    }
+
+    /**
+     * Method to called to create value instance from JSON Object using
+     * an intermediate "delegate" value to pass to createor method
+     */
+    public Object createUsingDelegate(DeserializationContext ctxt, Object delegate)
+        throws IOException, JsonProcessingException
+    {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" using delegate");
+    }
+    
+    /*
+    /**********************************************************
+    /* Instantiation methods for JSON scalar types
+    /* (String, Number, Boolean)
+    /**********************************************************
+     */
+    
+    public Object createFromString(DeserializationContext ctxt, String value)
+            throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" from String value");
+    }
+    
+    public Object createFromInt(DeserializationContext ctxt, int value)
+            throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" from Integer number (int)");
+    }
+
+    public Object createFromLong(DeserializationContext ctxt, long value)
+            throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" from Integer number (long)");
+    }
+
+    public Object createFromDouble(DeserializationContext ctxt, double value)
+            throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" from Floating-point number (double)");
+    }
+    
+    public Object createFromBoolean(DeserializationContext ctxt, boolean value)
+            throws IOException, JsonProcessingException {
+        throw new JsonMappingException("Can not instantiate value of type "
+                +getValueTypeDesc()+" from Boolean value");
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors for underlying creator objects (optional)
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to try to access member (constructor,
+     * static factory method) that is used as the "default creator"
+     * (creator that is called without arguments; typically default
+     * [zero-argument] constructor of the type).
+     * Note that implementations not required to return actual object
+     * they use (or, they may use some other instantiation) method.
+     * That is, even if {@link #canCreateUsingDefault()} returns true,
+     * this method may return null .
+     */
+    public AnnotatedWithParams getDefaultCreator() {
+        return null;
+    }
+    
+    /**
+     * Method that can be called to try to access member (constructor,
+     * static factory method) that is used as the "delegate creator".
+     * Note that implementations not required to return actual object
+     * they use (or, they may use some other instantiation) method.
+     * That is, even if {@link #canCreateUsingDelegate()} returns true,
+     * this method may return null .
+     */
+    public AnnotatedWithParams getDelegateCreator() {
+        return null;
+    }
+
+    /**
+     * Method that can be called to try to access member (constructor,
+     * static factory method) that is used as the "non-default creator"
+     * (constructor or factory method that takes one or more arguments).
+     * Note that implementations not required to return actual object
+     * they use (or, they may use some other instantiation) method.
+     * That is, even if {@link #canCreateFromObjectWith()} returns true,
+     * this method may return null .
+     */
+    public AnnotatedWithParams getWithArgsCreator() {
+        return null;
+    }
+
+    /**
+     * If an incomplete creator was found, this is the first parameter that
+     * needs further annotation to help make the creator complete.
+     */
+    public AnnotatedParameter getIncompleteParameter() {
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java
new file mode 100644
index 0000000..59e5eac
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/ValueInstantiators.java
@@ -0,0 +1,47 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Interface for providers of {@link ValueInstantiator} instances.
+ * Implemented when an object wants to provide custom value instantiators,
+ * usually to support custom value types with alternate constructors, or
+ * which need specified post-processing after construction but before
+ * binding data.
+ */
+public interface ValueInstantiators
+{
+    /**
+     * Method called to find the {@link ValueInstantiator} to use for creating
+     * instances of specified type during deserialization.
+     * Note that a default value instantiator is always created first and passed;
+     * if an implementation does not want to modify or replace it, it has to return
+     * passed instance as is (returning null is an error)
+     * 
+     * @param config Deserialization configuration in use
+     * @param beanDesc Additional information about POJO type to be instantiated
+     * @param defaultInstantiator Instantiator that will be used if no changes are made;
+     *   passed to allow custom instances to use annotation-provided information
+     *   (note, however, that earlier {@link ValueInstantiators} may have changed it to
+     *   a custom instantiator already)
+     *   
+     * @return Instantiator to use; either <code>defaultInstantiator</code> that was passed,
+     *   or a custom variant; can not be null.
+     */
+    public ValueInstantiator findValueInstantiator(DeserializationConfig config,
+            BeanDescription beanDesc, ValueInstantiator defaultInstantiator);
+
+    /**
+     * Basic "NOP" implementation that can be used as the base class for custom implementations.
+     * Safer to extend (instead of implementing {@link ValueInstantiators}) in case later
+     * Jackson versions add new methods in base interface.
+     */
+    public static class Base implements ValueInstantiators
+    {
+        @Override
+        public ValueInstantiator findValueInstantiator(DeserializationConfig config,
+                BeanDescription beanDesc, ValueInstantiator defaultInstantiator) {
+            return 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
new file mode 100644
index 0000000..81dd220
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayBuilderDeserializer.java
@@ -0,0 +1,368 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.util.HashSet;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+public class BeanAsArrayBuilderDeserializer
+    extends BeanDeserializerBase
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Deserializer we delegate operations that we can not handle.
+     */
+    protected final BeanDeserializerBase _delegate;
+
+    /**
+     * Properties in order expected to be found in JSON array.
+     */
+    protected final SettableBeanProperty[] _orderedProperties;
+
+    protected final AnnotatedMethod _buildMethod;
+        
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, initialization
+    /**********************************************************
+     */
+    
+    /**
+     * Main constructor used both for creating new instances (by
+     * {@link BeanDeserializer#asArrayDeserializer}) and for
+     * creating copies with different delegate.
+     */
+    public BeanAsArrayBuilderDeserializer(BeanDeserializerBase delegate,
+            SettableBeanProperty[] ordered,
+            AnnotatedMethod buildMethod)
+    {
+        super(delegate);
+        _delegate = delegate;
+        _orderedProperties = ordered;
+        _buildMethod = buildMethod;
+    }
+    
+    @Override
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    {
+        /* We can't do much about this; could either replace _delegate
+         * with unwrapping instance, or just replace this one. Latter seems
+         * more sensible.
+         */
+        return _delegate.unwrappingDeserializer(unwrapper);
+    }
+
+    @Override
+    public BeanAsArrayBuilderDeserializer withObjectIdReader(ObjectIdReader oir) {
+        return new BeanAsArrayBuilderDeserializer(_delegate.withObjectIdReader(oir),
+                _orderedProperties, _buildMethod);
+    }
+
+    @Override
+    public BeanAsArrayBuilderDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+        return new BeanAsArrayBuilderDeserializer(_delegate.withIgnorableProperties(ignorableProps),
+                _orderedProperties, _buildMethod);
+    }
+
+    @Override
+    protected BeanAsArrayBuilderDeserializer asArrayDeserializer() {
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializer implementation
+    /**********************************************************
+     */
+
+    protected final Object finishBuild(DeserializationContext ctxt, Object builder)
+            throws IOException
+    {
+        try {
+            return _buildMethod.getMember().invoke(builder);
+        } catch (Exception e) {
+            wrapInstantiationProblem(e, ctxt);
+            return null;
+        }
+    }
+    
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Let's delegate just in case we got a JSON Object (could error out, alternatively?)
+        if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
+            return finishBuild(ctxt, _deserializeFromNonArray(jp, ctxt));
+        }
+        if (!_vanillaProcessing) {
+            return finishBuild(ctxt, _deserializeNonVanilla(jp, ctxt));
+        }
+        Object builder = _valueInstantiator.createUsingDefault(ctxt);
+        final SettableBeanProperty[] props = _orderedProperties;
+        int i = 0;
+        final int propCount = props.length;
+        while (true) {
+            if (jp.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(jp, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, prop.getName(), ctxt);
+                }
+            } else { // just skip?
+                jp.skipChildren();
+            }
+            ++i;
+        }
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return finishBuild(ctxt, builder);
+    }
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt, Object builder)
+        throws IOException, JsonProcessingException
+    {
+        /* 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 (jp.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(jp, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, prop.getName(), ctxt);
+                }
+            } else { // just skip?
+                jp.skipChildren();
+            }
+            ++i;
+        }
+        
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return finishBuild(ctxt, builder);
+    }
+
+    // needed since 2.1
+    @Override
+    public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        return _deserializeFromNonArray(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods, non-standard creation
+    /**********************************************************
+     */
+
+    /**
+     * Alternate deserialization method that has to check many more configuration
+     * aspects than the "vanilla" processing.
+     * Note: should NOT resolve builder; caller will do that
+     * 
+     * @return Builder object in use.
+     */
+    protected Object _deserializeNonVanilla(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_nonStandardCreation) {
+            return _deserializeWithCreator(jp, ctxt);
+        }
+        Object builder = _valueInstantiator.createUsingDefault(ctxt);
+        if (_injectables != null) {
+            injectValues(ctxt, builder);
+        }
+        Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        final SettableBeanProperty[] props = _orderedProperties;
+        int i = 0;
+        final int propCount = props.length;
+        while (true) {
+            if (jp.nextToken() == JsonToken.END_ARRAY) {
+                return builder;
+            }
+            if (i == propCount) {
+                break;
+            }
+            SettableBeanProperty prop = props[i];
+            ++i;
+            if (prop != null) { // normal case
+                if (activeView == null || prop.visibleInView(activeView)) {
+                    try {
+                        prop.deserializeSetAndReturn(jp, ctxt, builder);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, builder, prop.getName(), ctxt);
+                    }
+                    continue;
+                }
+            }
+            // otherwise, skip it (view-filtered, no prop etc)
+            jp.skipChildren();
+        }
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return builder;
+    }
+    
+    protected Object _deserializeWithCreator(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {        
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_propertyBasedCreator != null) {
+            return _deserializeUsingPropertyBased(jp, ctxt);
+        }
+        // should only occur for abstract types...
+        if (_beanType.isAbstract()) {
+            throw JsonMappingException.from(jp, "Can not instantiate abstract type "+_beanType
+                    +" (need to add/enable type information?)");
+        }
+        throw JsonMappingException.from(jp, "No suitable constructor found for type "
+                +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
+    }
+
+    /**
+     * Method called to deserialize bean using "property-based creator":
+     * this means that a non-default constructor or factory method is
+     * called, and then possibly other setters. The trick is that
+     * values for creator method need to be buffered, first; and 
+     * due to non-guaranteed ordering possibly some other properties
+     * as well.
+     */
+    @Override
+    protected final Object _deserializeUsingPropertyBased(final JsonParser jp,
+            final DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        final SettableBeanProperty[] props = _orderedProperties;
+        final int propCount = props.length;
+        int i = 0;
+        Object builder = null;
+        
+        for (; jp.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?
+                jp.skipChildren();
+                continue;
+            }
+            // if we have already constructed POJO, things are simple:
+            if (builder != null) {
+                try {
+                    builder = prop.deserializeSetAndReturn(jp, ctxt, builder);
+                } catch (Exception e) {
+                    wrapAndThrow(e, builder, prop.getName(), ctxt);
+                }
+                continue;
+            }
+            final String propName = prop.getName();
+            // if not yet, maybe we got a creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    try {
+                        builder = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        continue; // never gets here
+                    }
+                    //  polymorphic?
+                    if (builder.getClass() != _beanType.getRawClass()) {
+                        /* 23-Jul-2012, tatu: Not sure if these could ever be properly
+                         *   supported (since ordering of elements may not be guaranteed);
+                         *   but make explicitly non-supported for now.
+                         */
+                        throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+                                +"nominal type "+_beanType.getRawClass().getName()+", actual type "+builder.getClass().getName());
+                    }
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+        }
+
+        // In case we didn't quite get all the creator properties, we may have to do this:
+        if (builder == null) {
+            try {
+                builder = creator.build(ctxt, buffer);
+            } catch (Exception e) {
+                wrapInstantiationProblem(e, ctxt);
+                return null; // never gets here
+            }
+        }
+        return builder;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, error reporting
+    /**********************************************************
+     */
+
+    protected Object _deserializeFromNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // Let's start with failure
+        throw ctxt.mappingException("Can not deserialize a POJO (of type "+_beanType.getRawClass().getName()
+                +") from non-Array representation (token: "+jp.getCurrentToken()
+                +"): type/property designed to be serialized as JSON Array");
+        // in future, may allow use of "standard" POJO serialization as well; if so, do:
+        //return _delegate.deserialize(jp, ctxt);
+    }
+}
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
new file mode 100644
index 0000000..b8540e3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanAsArrayDeserializer.java
@@ -0,0 +1,357 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.util.HashSet;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Variant of {@link BeanDeserializer} used for handling deserialization
+ * of POJOs when serialized as JSON Arrays, instead of JSON Objects.
+ * 
+ * @since 2.1
+ */
+public class BeanAsArrayDeserializer
+    extends BeanDeserializerBase
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Deserializer we delegate operations that we can not handle.
+     */
+    protected final BeanDeserializerBase _delegate;
+
+    /**
+     * Properties in order expected to be found in JSON array.
+     */
+    protected final SettableBeanProperty[] _orderedProperties;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, initialization
+    /**********************************************************
+     */
+
+    /**
+     * Main constructor used both for creating new instances (by
+     * {@link BeanDeserializer#asArrayDeserializer}) and for
+     * creating copies with different delegate.
+     */
+    public BeanAsArrayDeserializer(BeanDeserializerBase delegate,
+            SettableBeanProperty[] ordered)
+    {
+        super(delegate);
+        _delegate = delegate;
+        _orderedProperties = ordered;
+    }
+    
+    @Override
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    {
+        /* We can't do much about this; could either replace _delegate
+         * with unwrapping instance, or just replace this one. Latter seems
+         * more sensible.
+         */
+        return _delegate.unwrappingDeserializer(unwrapper);
+    }
+
+    @Override
+    public BeanAsArrayDeserializer withObjectIdReader(ObjectIdReader oir) {
+        return new BeanAsArrayDeserializer(_delegate.withObjectIdReader(oir),
+                _orderedProperties);
+    }
+
+    @Override
+    public BeanAsArrayDeserializer withIgnorableProperties(HashSet<String> ignorableProps) {
+        return new BeanAsArrayDeserializer(_delegate.withIgnorableProperties(ignorableProps),
+                _orderedProperties);
+    }
+
+    @Override
+    protected BeanDeserializerBase asArrayDeserializer() {
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializer implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Let's delegate just in case we got a JSON Object (could error out, alternatively?)
+        if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
+            return _deserializeFromNonArray(jp, ctxt);
+        }
+        if (!_vanillaProcessing) {
+            return _deserializeNonVanilla(jp, ctxt);
+        }
+        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        final SettableBeanProperty[] props = _orderedProperties;
+        int i = 0;
+        final int propCount = props.length;
+        while (true) {
+            if (jp.nextToken() == JsonToken.END_ARRAY) {
+                return bean;
+            }
+            if (i == propCount) {
+                break;
+            }
+            SettableBeanProperty prop = props[i];
+            if (prop != null) { // normal case
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, prop.getName(), ctxt);
+                }
+            } else { // just skip?
+                jp.skipChildren();
+            }
+            ++i;
+        }
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return bean;
+    }
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        /* 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, bean);
+        }
+        final SettableBeanProperty[] props = _orderedProperties;
+        int i = 0;
+        final int propCount = props.length;
+        while (true) {
+            if (jp.nextToken() == JsonToken.END_ARRAY) {
+                return bean;
+            }
+            if (i == propCount) {
+                break;
+            }
+            SettableBeanProperty prop = props[i];
+            if (prop != null) { // normal case
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, prop.getName(), ctxt);
+                }
+            } else { // just skip?
+                jp.skipChildren();
+            }
+            ++i;
+        }
+        
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return bean;
+    }
+
+
+    // needed since 2.1
+    @Override
+    public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        return _deserializeFromNonArray(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods, non-standard creation
+    /**********************************************************
+     */
+
+    /**
+     * Alternate deserialization method that has to check many more configuration
+     * aspects than the "vanilla" processing.
+     */
+    protected Object _deserializeNonVanilla(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_nonStandardCreation) {
+            return _deserializeWithCreator(jp, ctxt);
+        }
+        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
+        if (_injectables != null) {
+            injectValues(ctxt, bean);
+        }
+        Class<?> activeView = _needViewProcesing ? ctxt.getActiveView() : null;
+        final SettableBeanProperty[] props = _orderedProperties;
+        int i = 0;
+        final int propCount = props.length;
+        while (true) {
+            if (jp.nextToken() == JsonToken.END_ARRAY) {
+                return bean;
+            }
+            if (i == propCount) {
+                break;
+            }
+            SettableBeanProperty prop = props[i];
+            ++i;
+            if (prop != null) { // normal case
+                if (activeView == null || prop.visibleInView(activeView)) {
+                    try {
+                        prop.deserializeAndSet(jp, ctxt, bean);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, bean, prop.getName(), ctxt);
+                    }
+                    continue;
+                }
+            }
+            // otherwise, skip it (view-filtered, no prop etc)
+            jp.skipChildren();
+        }
+        // Ok; extra fields? Let's fail, unless ignoring extra props is fine
+        if (!_ignoreAllUnknown) {
+            throw ctxt.mappingException("Unexpected JSON values; expected at most "+propCount+" properties (in JSON Array)");
+        }
+        // otherwise, skip until end
+        while (jp.nextToken() != JsonToken.END_ARRAY) {
+            jp.skipChildren();
+        }
+        return bean;
+    }
+    
+    protected Object _deserializeWithCreator(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {        
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt, _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_propertyBasedCreator != null) {
+            return _deserializeUsingPropertyBased(jp, ctxt);
+        }
+        // should only occur for abstract types...
+        if (_beanType.isAbstract()) {
+            throw JsonMappingException.from(jp, "Can not instantiate abstract type "+_beanType
+                    +" (need to add/enable type information?)");
+        }
+        throw JsonMappingException.from(jp, "No suitable constructor found for type "
+                +_beanType+": can not instantiate from JSON object (need to add/enable type information?)");
+    }
+
+    /**
+     * Method called to deserialize bean using "property-based creator":
+     * this means that a non-default constructor or factory method is
+     * called, and then possibly other setters. The trick is that
+     * values for creator method need to be buffered, first; and 
+     * due to non-guaranteed ordering possibly some other properties
+     * as well.
+     */
+    @Override
+    protected final Object _deserializeUsingPropertyBased(final JsonParser jp, final DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, _objectIdReader);
+
+        final SettableBeanProperty[] props = _orderedProperties;
+        final int propCount = props.length;
+        int i = 0;
+        Object bean = null;
+        
+        for (; jp.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?
+                jp.skipChildren();
+                continue;
+            }
+            // if we have already constructed POJO, things are simple:
+            if (bean != null) {
+                try {
+                    prop.deserializeAndSet(jp, ctxt, bean);
+                } catch (Exception e) {
+                    wrapAndThrow(e, bean, prop.getName(), ctxt);
+                }
+                continue;
+            }
+            final String propName = prop.getName();
+            // if not yet, maybe we got a creator property?
+            SettableBeanProperty creatorProp = creator.findCreatorProperty(propName);
+            if (creatorProp != null) {
+                // Last creator property to set?
+                Object value = creatorProp.deserialize(jp, ctxt);
+                if (buffer.assignParameter(creatorProp.getCreatorIndex(), value)) {
+                    try {
+                        bean = creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _beanType.getRawClass(), propName, ctxt);
+                        continue; // never gets here
+                    }
+                    //  polymorphic?
+                    if (bean.getClass() != _beanType.getRawClass()) {
+                        /* 23-Jul-2012, tatu: Not sure if these could ever be properly
+                         *   supported (since ordering of elements may not be guaranteed);
+                         *   but make explicitly non-supported for now.
+                         */
+                        throw ctxt.mappingException("Can not support implicit polymorphic deserialization for POJOs-as-Arrays style: "
+                                +"nominal type "+_beanType.getRawClass().getName()+", actual type "+bean.getClass().getName());
+                    }
+                }
+                continue;
+            }
+            // Object Id property?
+            if (buffer.readIdProperty(propName)) {
+                continue;
+            }
+            // regular property? needs buffering
+            buffer.bufferProperty(prop, prop.deserialize(jp, ctxt));
+        }
+
+        // In case we didn't quite get all the creator properties, we may have to do this:
+        if (bean == null) {
+            try {
+                bean = creator.build(ctxt, buffer);
+            } catch (Exception e) {
+                wrapInstantiationProblem(e, ctxt);
+                return null; // never gets here
+            }
+        }
+        return bean;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, error reporting
+    /**********************************************************
+     */
+
+    protected Object _deserializeFromNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // Let's start with failure
+        throw ctxt.mappingException("Can not deserialize a POJO (of type "+_beanType.getRawClass().getName()
+                +") from non-Array representation (token: "+jp.getCurrentToken()
+                +"): type/property designed to be serialized as JSON Array");
+        // in future, may allow use of "standard" POJO serialization as well; if so, do:
+        //return _delegate.deserialize(jp, ctxt);
+    }
+}
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
new file mode 100644
index 0000000..4c0e532
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/BeanPropertyMap.java
@@ -0,0 +1,400 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Helper class used for storing mapping from property name to
+ * {@link SettableBeanProperty} instances.
+ *<p>
+ * Note that this class is used instead of generic {@link java.util.HashMap}
+ * for bit of performance gain (and some memory savings): although default
+ * implementation is very good for generic use cases, it can be streamlined
+ * a bit for specific use case we have. Even relatively small improvements
+ * matter since this is directly on the critical path during deserialization,
+ * as it is done for each and every POJO property deserialized.
+ */
+public final class BeanPropertyMap
+    implements Iterable<SettableBeanProperty>,
+        java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Bucket[] _buckets;
+    
+    private final int _hashMask;
+
+    private final int _size;
+
+    /**
+     * Counter we use to keep track of insertion order of properties
+     * (to be able to recreate insertion order when needed).
+     *<p>
+     * Note: is kept up-to-date with additions, but can NOT handle
+     * removals (i.e. "holes" may be left)
+     */
+    private int _nextBucketIndex = 0;
+
+    public BeanPropertyMap(Collection<SettableBeanProperty> properties)
+    {
+        _size = properties.size();
+        int bucketCount = findSize(_size);
+        _hashMask = bucketCount-1;
+        Bucket[] buckets = new Bucket[bucketCount];
+        for (SettableBeanProperty property : properties) {
+            String key = property.getName();
+            int index = key.hashCode() & _hashMask;
+            buckets[index] = new Bucket(buckets[index], key, property, _nextBucketIndex++);
+        }
+        _buckets = buckets;
+    }
+
+    private BeanPropertyMap(Bucket[] buckets, int size, int index)
+    {
+        _buckets = buckets;
+        _size = size;
+        _hashMask = buckets.length-1;
+        _nextBucketIndex = index;
+    }
+    
+    /**
+     * Fluent copy method that creates a new instance that is a copy
+     * of this instance except for one additional property that is
+     * passed as the argument.
+     * Note that method does not modify this instance but constructs
+     * and returns a new one.
+     * 
+     * @since 2.0
+     */
+    public BeanPropertyMap withProperty(SettableBeanProperty newProperty)
+    {
+        // first things first: can just copy hash area:
+        final int bcount = _buckets.length;
+        Bucket[] newBuckets = new Bucket[bcount];
+        System.arraycopy(_buckets, 0, newBuckets, 0, bcount);
+        final String propName = newProperty.getName();
+        // and then see if it's add or replace:
+        SettableBeanProperty oldProp = find(newProperty.getName());
+        if (oldProp == null) { // add
+            // first things first: add or replace?
+    	        // can do a straight copy, since all additions are at the front
+    	        // and then insert the new property:
+    	        int index = propName.hashCode() & _hashMask;
+    	        newBuckets[index] = new Bucket(newBuckets[index],
+    	                propName, newProperty, _nextBucketIndex++);
+    	        return new BeanPropertyMap(newBuckets, _size+1, _nextBucketIndex);
+        }
+        // replace: easy, close + replace
+        BeanPropertyMap newMap = new BeanPropertyMap(newBuckets, bcount, _nextBucketIndex);
+        newMap.replace(newProperty);
+        return newMap;
+    }
+
+    /**
+     * Factory method for constructing a map where all entries use given
+     * prefix
+     */
+    public BeanPropertyMap renameAll(NameTransformer transformer)
+    {
+        if (transformer == null || (transformer == NameTransformer.NOP)) {
+            return this;
+        }
+        Iterator<SettableBeanProperty> it = iterator();
+        ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>();
+        while (it.hasNext()) {
+            SettableBeanProperty prop = it.next();
+            String newName = transformer.transform(prop.getName());
+            prop = prop.withName(newName);
+            JsonDeserializer<?> deser = prop.getValueDeserializer();
+            if (deser != null) {
+                @SuppressWarnings("unchecked")
+                JsonDeserializer<Object> newDeser = (JsonDeserializer<Object>)
+                    deser.unwrappingDeserializer(transformer);
+                if (newDeser != deser) {
+                    prop = prop.withValueDeserializer(newDeser);
+                }
+            }
+            newProps.add(prop);
+        }
+        // should we try to re-index? Ordering probably changed but called probably doesn't want changes...
+        return new BeanPropertyMap(newProps);
+    }
+    
+    public BeanPropertyMap assignIndexes()
+    {
+        // order is arbitrary, but stable:
+        int index = 0;
+        for (Bucket bucket : _buckets) {
+            while (bucket != null) {
+                bucket.value.assignIndex(index++);
+                bucket = bucket.next;
+            }
+        }
+        return this;
+    }
+    
+    private final static int findSize(int size)
+    {
+        // For small enough results (32 or less), we'll require <= 50% fill rate; otherwise 80%
+        int needed = (size <= 32) ? (size + size) : (size + (size >> 2));
+        int result = 2;
+        while (result < needed) {
+            result += result;
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Iterable, for convenient iterating over all properties
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Properties=[");
+        int count = 0;
+        for (SettableBeanProperty prop : getPropertiesInInsertionOrder()) {
+            if (prop == null) {
+                continue;
+            }
+            if (count++ > 0) {
+                sb.append(", ");
+            }
+            sb.append(prop.getName());
+            sb.append('(');
+            sb.append(prop.getType());
+            sb.append(')');
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+    
+    /**
+     * Accessor for traversing over all contained properties.
+     */
+    @Override
+    public Iterator<SettableBeanProperty> iterator() {
+        return new IteratorImpl(_buckets);
+    }
+    
+    /**
+     * 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()
+    {
+        int len = _nextBucketIndex;
+        SettableBeanProperty[] result = new SettableBeanProperty[len];
+        for (Bucket root : _buckets) {
+            for (Bucket bucket = root; bucket != null; bucket = bucket.next) {
+                result[bucket.index] = bucket.value;
+            }
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    public int size() { return _size; }
+    
+    public SettableBeanProperty find(String key)
+    {
+        int index = key.hashCode() & _hashMask;
+        Bucket bucket = _buckets[index];
+        // Let's unroll first lookup since that is null or match in 90+% cases
+        if (bucket == null) {
+            return null;
+        }
+        // Primarily we do just identity comparison as keys should be interned
+        if (bucket.key == key) {
+            return bucket.value;
+        }
+        while ((bucket = bucket.next) != null) {
+            if (bucket.key == key) {
+                return bucket.value;
+            }
+        }
+        // Do we need fallback for non-interned Strings?
+        return _findWithEquals(key, index);
+    }
+
+    /**
+     * Specialized method that can be used to replace an existing entry
+     * (note: entry MUST exist; otherwise exception is thrown) with
+     * specified replacement.
+     */
+    public void replace(SettableBeanProperty property)
+    {
+        String name = property.getName();
+        int index = name.hashCode() & (_buckets.length-1);
+
+        /* This is bit tricky just because buckets themselves
+         * are immutable, so we need to recreate the chain. Fine.
+         */
+        Bucket tail = null;
+        int foundIndex = -1;
+        
+        for (Bucket bucket = _buckets[index]; bucket != null; bucket = bucket.next) {
+            // match to remove?
+            if (foundIndex < 0 && bucket.key.equals(name)) {
+                foundIndex = bucket.index;
+            } else {
+                tail = new Bucket(tail, bucket.key, bucket.value, bucket.index);
+            }
+        }
+        // Not finding specified entry is error, so:
+        if (foundIndex < 0) {
+            throw new NoSuchElementException("No entry '"+property+"' found, can't replace");
+        }
+        /* So let's attach replacement in front: useful also because
+         * it allows replacement even when iterating over entries
+         */
+        _buckets[index] = new Bucket(tail, name, property, foundIndex);
+    }
+
+    /**
+     * Specialized method for removing specified existing entry.
+     * NOTE: entry MUST exist, otherwise an exception is thrown.
+     */
+    public void remove(SettableBeanProperty property)
+    {
+        // Mostly this is the same as code with 'replace', just bit simpler...
+        String name = property.getName();
+        int index = name.hashCode() & (_buckets.length-1);
+        Bucket tail = null;
+        boolean found = false;
+        // slightly complex just because chain is immutable, must recreate
+        for (Bucket bucket = _buckets[index]; bucket != null; bucket = bucket.next) {
+            // match to remove?
+            if (!found && bucket.key.equals(name)) {
+                found = true;
+            } else {
+                tail = new Bucket(tail, bucket.key, bucket.value, bucket.index);
+            }
+        }
+        if (!found) { // must be found
+            throw new NoSuchElementException("No entry '"+property+"' found, can't remove");
+        }
+        _buckets[index] = tail;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private SettableBeanProperty _findWithEquals(String key, int index)
+    {
+        Bucket bucket = _buckets[index];
+        while (bucket != null) {
+            if (key.equals(bucket.key)) {
+                return bucket.value;
+            }
+            bucket = bucket.next;
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+    
+    private final static class Bucket
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        public final Bucket next;
+        public final String key;
+        public final SettableBeanProperty value;
+
+        /**
+         * Index that indicates insertion order of the bucket
+         */
+        public final int index;
+        
+        public Bucket(Bucket next, String key, SettableBeanProperty value, int index)
+        {
+            this.next = next;
+            this.key = key;
+            this.value = value;
+            this.index = index;
+        }
+    }
+
+    private final static class IteratorImpl
+        implements Iterator<SettableBeanProperty>
+    {
+        /**
+         * Buckets of the map
+         */
+        private final Bucket[] _buckets;
+
+        /**
+         * Bucket that contains next value to return (if any); null if nothing more to iterate
+         */
+        private Bucket _currentBucket;
+
+        /**
+         * Index of the next bucket in bucket array to check.
+         */
+        private int _nextBucketIndex;
+        
+        public IteratorImpl(Bucket[] buckets) {
+            _buckets = buckets;
+            // need to initialize to point to first entry...
+            int i = 0;
+            for (int len = _buckets.length; i < len; ) {
+                Bucket b = _buckets[i++];
+                if (b != null) {
+                    _currentBucket = b;
+                    break;
+                }
+            }
+            _nextBucketIndex = i;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return _currentBucket != null;
+        }
+
+        @Override
+        public SettableBeanProperty next()
+        {
+            Bucket curr = _currentBucket;
+            if (curr == null) { // sanity check
+                throw new NoSuchElementException();
+            }
+            // need to advance, too
+            Bucket b = curr.next;
+            while (b == null && _nextBucketIndex < _buckets.length) {
+                b = _buckets[_nextBucketIndex++];
+            }
+            _currentBucket = b;
+            return curr.value;
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
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
new file mode 100644
index 0000000..333b39f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/CreatorCollector.java
@@ -0,0 +1,219 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.lang.reflect.Member;
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.deser.CreatorProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Container class for storing information on creators (based on annotations,
+ * visibility), to be able to build actual instantiator later on.
+ */
+public class CreatorCollector
+{
+    /// Type of bean being created
+    final protected BeanDescription _beanDesc;
+
+    final protected boolean _canFixAccess;
+
+    /**
+     * Reference to the default creator (constructor or factory method).
+     *<p>
+     * Note: name is a misnomer, after resolving of [JACKSON-850], since this
+     * can also point to factory method.
+     */
+    protected AnnotatedWithParams _defaultConstructor;
+    
+    protected AnnotatedWithParams _stringCreator, _intCreator, _longCreator;
+    protected AnnotatedWithParams _doubleCreator, _booleanCreator;
+
+    protected AnnotatedWithParams _delegateCreator;
+    // when there are injectable values along with delegate:
+    protected CreatorProperty[] _delegateArgs;
+    
+    protected AnnotatedWithParams _propertyBasedCreator;
+    protected CreatorProperty[] _propertyBasedArgs = null;
+
+    protected AnnotatedParameter _incompleteParameter;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public CreatorCollector(BeanDescription beanDesc, boolean canFixAccess)
+    {
+        _beanDesc = beanDesc;
+        _canFixAccess = canFixAccess;
+    }
+
+    public ValueInstantiator constructValueInstantiator(DeserializationConfig config)
+    {
+        StdValueInstantiator inst = new StdValueInstantiator(config, _beanDesc.getType());
+
+        JavaType delegateType;
+
+        if (_delegateCreator == null) {
+            delegateType = null;
+        } else {
+            // need to find type...
+            int ix = 0;
+            if (_delegateArgs != null) {
+                for (int i = 0, len = _delegateArgs.length; i < len; ++i) {
+                    if (_delegateArgs[i] == null) { // marker for delegate itself
+                        ix = i;
+                        break;
+                    }
+                }
+            }
+            TypeBindings bindings = _beanDesc.bindingsForBeanType();
+            delegateType = bindings.resolveType(_delegateCreator.getGenericParameterType(ix));
+        }
+        
+        inst.configureFromObjectSettings(_defaultConstructor,
+                _delegateCreator, delegateType, _delegateArgs,
+                _propertyBasedCreator, _propertyBasedArgs);
+        inst.configureFromStringCreator(_stringCreator);
+        inst.configureFromIntCreator(_intCreator);
+        inst.configureFromLongCreator(_longCreator);
+        inst.configureFromDoubleCreator(_doubleCreator);
+        inst.configureFromBooleanCreator(_booleanCreator);
+        inst.configureIncompleteParameter(_incompleteParameter);
+        return inst;
+    }
+    
+    /*
+    /**********************************************************
+    /* Setters
+    /**********************************************************
+     */
+
+    /**
+     * @deprecated since 2.1, use {@link #setDefaultCreator} instead.
+     */
+    @Deprecated
+    public void setDefaultConstructor(AnnotatedConstructor ctor) {
+        _defaultConstructor = _fixAccess(ctor);
+    }
+    
+    /**
+     * Method called to indicate the default creator: no-arguments
+     * constructor or factory method that is called to instantiate
+     * a value before populating it with data. Default creator is
+     * only used if no other creators are indicated.
+     * 
+     * @param creator Creator method; no-arguments constructor or static
+     *   factory method.
+     */
+    public void setDefaultCreator(AnnotatedWithParams creator)
+    {
+        // !!! TODO: 23-Jul-2012, tatu: Should change to directly change things
+        //    here in future; but for backwards compatibility, can't do that yet
+        if (creator instanceof AnnotatedConstructor) {
+            setDefaultConstructor((AnnotatedConstructor) creator);
+            return;
+        }
+        _defaultConstructor = _fixAccess(creator);
+    }
+    
+    public void addStringCreator(AnnotatedWithParams creator) {
+        _stringCreator = verifyNonDup(creator, _stringCreator, "String");
+    }
+    public void addIntCreator(AnnotatedWithParams creator) {
+        _intCreator = verifyNonDup(creator, _intCreator, "int");
+    }
+    public void addLongCreator(AnnotatedWithParams creator) {
+        _longCreator = verifyNonDup(creator, _longCreator, "long");
+    }
+    public void addDoubleCreator(AnnotatedWithParams creator) {
+        _doubleCreator = verifyNonDup(creator, _doubleCreator, "double");
+    }
+    public void addBooleanCreator(AnnotatedWithParams creator) {
+        _booleanCreator = verifyNonDup(creator, _booleanCreator, "boolean");
+    }
+
+    public void addDelegatingCreator(AnnotatedWithParams creator,
+            CreatorProperty[] injectables)
+    {
+        _delegateCreator = verifyNonDup(creator, _delegateCreator, "delegate");
+        _delegateArgs = injectables;
+    }
+    
+    public void addPropertyCreator(AnnotatedWithParams creator, CreatorProperty[] properties)
+    {
+        _propertyBasedCreator = verifyNonDup(creator, _propertyBasedCreator, "property-based");
+        // [JACKSON-470] Better ensure we have no duplicate names either...
+        if (properties.length > 1) {
+            HashMap<String,Integer> names = new HashMap<String,Integer>();
+            for (int i = 0, len = properties.length; i < len; ++i) {
+                String name = properties[i].getName();
+                /* [Issue-13]: 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) {
+                    continue;
+                }
+                Integer old = names.put(name, Integer.valueOf(i));
+                if (old != null) {
+                    throw new IllegalArgumentException("Duplicate creator property \""+name+"\" (index "+old+" vs "+i+")");
+                }
+            }
+        }
+        _propertyBasedArgs = properties;
+    }
+
+    public void addIncompeteParameter(AnnotatedParameter parameter) {
+        if (_incompleteParameter == null) {
+            _incompleteParameter = parameter;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.1
+     */
+    public boolean hasDefaultCreator() {
+        return _defaultConstructor != null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private <T extends AnnotatedMember> T _fixAccess(T member)
+    {
+        if (member != null && _canFixAccess) {
+            ClassUtil.checkAndFixAccess((Member) member.getAnnotated());
+        }
+        return member;
+    }
+
+    protected AnnotatedWithParams verifyNonDup(AnnotatedWithParams newOne, AnnotatedWithParams oldOne,
+            String type)
+    {
+        if (oldOne != null) {
+            // important: ok to override factory with constructor; but not within same type, so:
+            if (oldOne.getClass() == newOne.getClass()) {
+                throw new IllegalArgumentException("Conflicting "+type+" creators: already had "+oldOne+", encountered "+newOne);
+            }
+        }
+        return _fixAccess(newOne);
+    }
+}
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
new file mode 100644
index 0000000..2a7040d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ExternalTypeHandler.java
@@ -0,0 +1,317 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Helper class that is used to flatten JSON structure when using
+ * "external type id" (see {@link com.fasterxml.jackson.annotation.JsonTypeInfo.As#EXTERNAL_PROPERTY}).
+ * This is needed to store temporary state and buffer tokens, as the structure is
+ * rearranged a bit so that actual type deserializer can resolve type and 
+ * finalize deserialization.
+ */
+public class ExternalTypeHandler
+{
+    private final ExtTypedProperty[] _properties;
+    private final HashMap<String, Integer> _nameToPropertyIndex;
+
+    private final String[] _typeIds;
+    private final TokenBuffer[] _tokens;
+    
+    protected ExternalTypeHandler(ExtTypedProperty[] properties,
+            HashMap<String, Integer> nameToPropertyIndex,
+            String[] typeIds, TokenBuffer[] tokens)
+    {
+        _properties = properties;        
+        _nameToPropertyIndex = nameToPropertyIndex;
+        _typeIds = typeIds;
+        _tokens = tokens;
+    }
+
+    protected ExternalTypeHandler(ExternalTypeHandler h)
+    {
+        _properties = h._properties;
+        _nameToPropertyIndex = h._nameToPropertyIndex;
+        int len = _properties.length;
+        _typeIds = new String[len];
+        _tokens = new TokenBuffer[len];
+    }
+    
+    public ExternalTypeHandler start() {
+        return new ExternalTypeHandler(this);
+    }
+
+    /**
+     * 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;
+     * otherwise {@link #handlePropertyValue} should be called instead.
+     */
+    public boolean handleTypePropertyValue(JsonParser jp, DeserializationContext ctxt,
+            String propName, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        Integer I = _nameToPropertyIndex.get(propName);
+        if (I == null) {
+            return false;
+        }
+        int index = I.intValue();
+        ExtTypedProperty prop = _properties[index];
+        if (!prop.hasTypePropertyName(propName)) {
+            return false;
+        }
+        String typeId = jp.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:
+        if (canDeserialize) {
+            _deserializeAndSet(jp, ctxt, bean, index, typeId);
+            // clear stored data, to avoid deserializing+setting twice:
+            _tokens[index] = null;
+        } else {
+            _typeIds[index] = typeId;
+        }
+        return true;
+    }
+    
+    /**
+     * Method called to ask handler to handle value of given property,
+     * at point where parser points to the first token of the value.
+     * Handling can mean either resolving type id it contains (if it matches type
+     * property name), or by buffering the value for further use.
+     * 
+     * @return True, if the given property was properly handled
+     */
+    public boolean handlePropertyValue(JsonParser jp, DeserializationContext ctxt,
+            String propName, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        Integer I = _nameToPropertyIndex.get(propName);
+        if (I == null) {
+            return false;
+        }
+        int index = I.intValue();
+        ExtTypedProperty prop = _properties[index];
+        boolean canDeserialize;
+        if (prop.hasTypePropertyName(propName)) {
+            _typeIds[index] = jp.getText();
+            jp.skipChildren();
+            canDeserialize = (bean != null) && (_tokens[index] != null);
+        } else {
+            @SuppressWarnings("resource")
+            TokenBuffer tokens = new TokenBuffer(jp.getCodec());
+            tokens.copyCurrentStructure(jp);
+            _tokens[index] = tokens;
+            canDeserialize = (bean != null) && (_typeIds[index] != null);
+        }
+        /* 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:
+            _typeIds[index] = null;
+            _deserializeAndSet(jp, ctxt, bean, index, typeId);
+            _tokens[index] = null;
+        }
+        return true;
+    }
+    
+    public Object complete(JsonParser jp, DeserializationContext ctxt, Object bean)
+        throws IOException, JsonProcessingException
+    {
+        for (int i = 0, len = _properties.length; i < len; ++i) {
+            String typeId = _typeIds[i];
+            if (typeId == null) {
+                TokenBuffer tokens = _tokens[i];
+                // let's allow missing both type and property (may already have been set, too)
+                // but not just one
+                if (tokens == null) {
+                    continue;
+                }
+                /* [Issue#118]: Need to mind natural types, for which no type id
+                 *   will be included.
+                 */
+                JsonToken t = tokens.firstToken();
+                if (t != null && t.isScalarValue()) {
+                    JsonParser buffered = tokens.asParser(jp);
+                    buffered.nextToken();
+                    SettableBeanProperty extProp = _properties[i].getProperty();
+                    Object result = TypeDeserializer.deserializeIfNatural(buffered, ctxt, extProp.getType());
+                    if (result != null) {
+                        extProp.set(bean, result);
+                        continue;
+                    }
+                    // 26-Oct-2012, tatu: As per [Issue#94], must allow use of 'defaultImpl'
+                    if (!_properties[i].hasDefaultType()) {
+                        throw ctxt.mappingException("Missing external type id property '"+_properties[i].getTypePropertyName()+"'");
+                    }
+                    typeId = _properties[i].getDefaultTypeId();
+                }
+            } else if (_tokens[i] == null) {
+                SettableBeanProperty prop = _properties[i].getProperty();
+                throw ctxt.mappingException("Missing property '"+prop.getName()+"' for external type id '"+_properties[i].getTypePropertyName());
+            }
+            _deserializeAndSet(jp, ctxt, bean, i, typeId);
+        }
+        return bean;
+    }
+
+    /**
+     * Variant called when creation of the POJO involves buffering of creator properties
+     * as well as property-based creator.
+     */
+    public Object complete(JsonParser jp, DeserializationContext ctxt,
+            PropertyValueBuffer buffer, PropertyBasedCreator creator)
+        throws IOException, JsonProcessingException
+    {
+        // first things first: deserialize all data buffered:
+        final int len = _properties.length;
+        Object[] values = new Object[len];
+        for (int i = 0; i < len; ++i) {
+            String typeId = _typeIds[i];
+            if (typeId == null) {
+                // let's allow missing both type and property (may already have been set, too)
+                if (_tokens[i] == null) {
+                    continue;
+                }
+                // but not just one
+                // 26-Oct-2012, tatu: As per [Issue#94], must allow use of 'defaultImpl'
+                if (!_properties[i].hasDefaultType()) {
+                    throw ctxt.mappingException("Missing external type id property '"+_properties[i].getTypePropertyName()+"'");
+                }
+                typeId = _properties[i].getDefaultTypeId();
+            } else if (_tokens[i] == null) {
+                SettableBeanProperty prop = _properties[i].getProperty();
+                throw ctxt.mappingException("Missing property '"+prop.getName()+"' for external type id '"+_properties[i].getTypePropertyName());
+            }
+            values[i] = _deserialize(jp, ctxt, i, typeId);
+        }
+        // second: fill in creator properties:
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = _properties[i].getProperty();
+            if (creator.findCreatorProperty(prop.getName()) != null) {
+                buffer.assignParameter(prop.getCreatorIndex(), values[i]);
+            }
+        }
+        Object bean = creator.build(ctxt, buffer);
+        // third: assign non-creator properties
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = _properties[i].getProperty();
+            if (creator.findCreatorProperty(prop.getName()) == null) {
+                prop.set(bean, values[i]);
+            }
+        }
+        return bean;
+    }
+
+    protected final Object _deserialize(JsonParser jp, DeserializationContext ctxt,
+            int index, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        @SuppressWarnings("resource")
+        TokenBuffer merged = new TokenBuffer(jp.getCodec());
+        merged.writeStartArray();
+        merged.writeString(typeId);
+        JsonParser p2 = _tokens[index].asParser(jp);
+        p2.nextToken();
+        merged.copyCurrentStructure(p2);
+        merged.writeEndArray();
+
+        // needs to point to START_OBJECT (or whatever first token is)
+        p2 = merged.asParser(jp);
+        p2.nextToken();
+        return _properties[index].getProperty().deserialize(p2, ctxt);
+    }
+    
+    protected final void _deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+            Object bean, int index, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        /* Ok: time to mix type id, value; and we will actually use "wrapper-array"
+         * style to ensure we can handle all kinds of JSON constructs.
+         */
+        @SuppressWarnings("resource")
+        TokenBuffer merged = new TokenBuffer(jp.getCodec());
+        merged.writeStartArray();
+        merged.writeString(typeId);
+        JsonParser p2 = _tokens[index].asParser(jp);
+        p2.nextToken();
+        merged.copyCurrentStructure(p2);
+        merged.writeEndArray();
+        
+        // needs to point to START_OBJECT (or whatever first token is)
+        p2 = merged.asParser(jp);
+        p2.nextToken();
+        _properties[index].getProperty().deserializeAndSet(p2, ctxt, bean);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+    
+    public static class Builder
+    {
+        private final ArrayList<ExtTypedProperty> _properties = new ArrayList<ExtTypedProperty>();
+        private final HashMap<String, Integer> _nameToPropertyIndex = new HashMap<String, Integer>();
+
+        // note: signature changed between 2.1.0 and 2.1.1 (alas!)
+        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);
+        }
+        
+        public ExternalTypeHandler build() {
+            return new ExternalTypeHandler(_properties.toArray(new ExtTypedProperty[_properties.size()]),
+                    _nameToPropertyIndex, null, null);
+        }
+    }
+
+    private final static class ExtTypedProperty
+    {
+        private final SettableBeanProperty _property;
+        private final TypeDeserializer _typeDeserializer;
+        private final String _typePropertyName;
+        
+        public ExtTypedProperty(SettableBeanProperty property, TypeDeserializer typeDeser)
+        {
+            _property = property;
+            _typeDeserializer = typeDeser;
+            _typePropertyName = typeDeser.getPropertyName();
+        }
+
+        public boolean hasTypePropertyName(String n) {
+            return n.equals(_typePropertyName);
+        }
+
+        public boolean hasDefaultType() {
+            return _typeDeserializer.getDefaultImpl() != null;
+        }
+
+        public String getDefaultTypeId() {
+            Class<?> defaultType = _typeDeserializer.getDefaultImpl();
+            if (defaultType == null) {
+                return null;
+            }
+            return _typeDeserializer.getTypeIdResolver().idFromValueAndType(null, defaultType);
+        }
+        
+        public String getTypePropertyName() { return _typePropertyName; }
+        
+        public SettableBeanProperty getProperty() {
+            return _property;
+        }
+    }
+}
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
new file mode 100644
index 0000000..dcce7b3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/FieldProperty.java
@@ -0,0 +1,150 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * This concrete sub-class implements property that is set
+ * directly assigning to a Field.
+ */
+public final class FieldProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Transient since there is no need to persist; only needed during
+     * construction of objects.
+     */
+    protected final AnnotatedField _annotated;
+
+    /**
+     * Actual field to set when deserializing this property.
+     */
+    protected final transient Field _field;
+    
+    public FieldProperty(BeanPropertyDefinition propDef, JavaType type,
+            TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedField field)
+    {
+        super(propDef, type, typeDeser, contextAnnotations);
+        _annotated = field;
+        _field = field.getAnnotated();
+    }
+
+    protected FieldProperty(FieldProperty src, JsonDeserializer<?> deser) {
+        super(src, deser);
+        _annotated = src._annotated;
+        _field = src._field;
+    }
+
+    protected FieldProperty(FieldProperty src, String newName) {
+        super(src, newName);
+        _annotated = src._annotated;
+        _field = src._field;
+    }
+
+    /**
+     * Constructor used for JDK Serialization when reading persisted object
+     */
+    protected FieldProperty(FieldProperty src, Field f)
+    {
+        super(src);
+        _annotated = src._annotated;
+        if (f == null) {
+            throw new IllegalArgumentException("No Field passed for property '"+src.getName()
+                    +"' (class "+src.getDeclaringClass().getName()+")");
+        }
+        _field = f;
+    }
+    
+    @Override
+    public FieldProperty withName(String newName) {
+        return new FieldProperty(this, newName);
+    }
+    
+    @Override
+    public FieldProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new FieldProperty(this, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        return _annotated.getAnnotation(acls);
+    }
+
+    @Override public AnnotatedMember getMember() {  return _annotated; }
+
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        set(instance, deserialize(jp, ctxt));
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        return setAndReturn(instance, deserialize(jp, ctxt));
+    }
+    
+    @Override
+    public final void set(Object instance, Object value)
+        throws IOException
+    {
+        try {
+            _field.set(instance, value);
+        } catch (Exception e) {
+            _throwAsIOE(e, value);
+        }
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value)
+        throws IOException
+    {
+        try {
+            _field.set(instance, value);
+        } catch (Exception e) {
+            _throwAsIOE(e, value);
+        }
+        return instance;
+    }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object readResolve() {
+        return new FieldProperty(this, _annotated.getAnnotated());
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..ee21f8c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/InnerClassProperty.java
@@ -0,0 +1,126 @@
+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.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * This sub-class is used to handle special case of value being a
+ * non-static inner class. If so, we will have to use a special
+ * alternative for default constructor; but otherwise can delegate
+ * to regular implementation.
+ */
+public final class InnerClassProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Actual property that we use after value construction.
+     */
+    protected final SettableBeanProperty _delegate;
+
+    /**
+     * Single-arg constructor we use for value instantiation.
+     */
+    protected final Constructor<?> _creator;
+    
+    public InnerClassProperty(SettableBeanProperty delegate,
+            Constructor<?> ctor)
+    {
+        super(delegate);
+        _delegate = delegate;
+        _creator = ctor;
+    }
+
+    protected InnerClassProperty(InnerClassProperty src, JsonDeserializer<?> deser)
+    {
+        super(src, deser);
+        _delegate = src._delegate.withValueDeserializer(deser);
+        _creator = src._creator;
+    }
+
+    protected InnerClassProperty(InnerClassProperty src, String newName) {
+        super(src, newName);
+        _delegate = src._delegate.withName(newName);
+        _creator = src._creator;
+    }
+
+    @Override
+    public InnerClassProperty withName(String newName) {
+        return new InnerClassProperty(this, newName);
+    }
+
+    @Override
+    public InnerClassProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new InnerClassProperty(this, deser);
+    }
+    
+    // // // 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
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+            Object bean)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        Object value;
+        if (t == JsonToken.VALUE_NULL) {
+            value = (_nullProvider == null) ? null : _nullProvider.nullValue(ctxt);
+        } else if (_valueTypeDeserializer != null) {
+            value = _valueDeserializer.deserializeWithType(jp, 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());
+                value = null;
+            }
+            _valueDeserializer.deserialize(jp, ctxt, value);
+        }
+        set(bean, value);
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        return setAndReturn(instance, deserialize(jp, ctxt));
+    }
+    
+    @Override
+    public final 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);
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..de3ec83
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ManagedReferenceProperty.java
@@ -0,0 +1,158 @@
+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.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+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
+ * (see [JACKSON-235] for more information). Basically just need to
+ * delegate first to actual forward property, and 
+ */
+public final class ManagedReferenceProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final String _referenceName;
+    
+    /**
+     * Flag that indicates whether property to handle is a container type
+     * (array, Collection, Map) or not.
+     */
+    protected final boolean _isContainer;
+    
+    protected final SettableBeanProperty _managedProperty;
+
+    protected final SettableBeanProperty _backProperty;
+    
+    public ManagedReferenceProperty(SettableBeanProperty forward,
+            String refName, SettableBeanProperty backward,
+            Annotations contextAnnotations, boolean isContainer)
+    {
+        super(forward.getName(), forward.getType(), forward.getWrapperName(),
+                forward.getValueTypeDeserializer(), contextAnnotations,
+                forward.isRequired());
+        _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, String newName) {
+        super(src, newName);
+        _referenceName = src._referenceName;
+        _isContainer = src._isContainer;
+        _managedProperty = src._managedProperty;
+        _backProperty = src._backProperty;
+    }
+
+    @Override
+    public ManagedReferenceProperty withName(String newName) {
+        return new ManagedReferenceProperty(this, newName);
+    }
+    
+    @Override
+    public ManagedReferenceProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new ManagedReferenceProperty(this, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* 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
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+                                  Object instance)
+        throws IOException, JsonProcessingException
+    {
+        set(instance, _managedProperty.deserialize(jp, ctxt));
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        return setAndReturn(instance, deserialize(jp, ctxt));
+    }
+    
+    @Override
+    public final void set(Object instance, Object value)
+        throws IOException
+    {
+    	setAndReturn(instance, value);
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value)
+   		throws IOException
+	{
+        Object result = _managedProperty.setAndReturn(instance, value);
+        /* And then back reference, if (and only if!) we actually have a non-null
+         * reference
+         */
+        if (value != null) {
+            if (_isContainer) { // ok, this gets ugly... but has to do for now
+                if (value instanceof Object[]) {
+                    for (Object ob : (Object[]) value) {
+                        if (ob != null) {
+                            _backProperty.set(ob, instance);                            
+                        }
+                    }
+                } else if (value instanceof Collection<?>) {
+                    for (Object ob : (Collection<?>) value) {
+                        if (ob != null) {
+                            _backProperty.set(ob, instance);                            
+                        }
+                    }
+                } else if (value instanceof Map<?,?>) {
+                    for (Object ob : ((Map<?,?>) value).values()) {
+                        if (ob != null) {
+                            _backProperty.set(ob, instance);                            
+                        }
+                    }
+                } else {
+                    throw new IllegalStateException("Unsupported container type ("+value.getClass().getName()
+                            +") when resolving reference '"+_referenceName+"'");
+                }
+            } else {
+                _backProperty.set(value, instance);
+            }
+        }
+        return result;
+	}
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..fa30f54
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/MethodProperty.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * This concrete sub-class implements property that is set
+ * using regular "setter" method.
+ */
+public final class MethodProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1;
+
+    protected final AnnotatedMethod _annotated;
+    
+    /**
+     * Setter method for modifying property value; used for
+     * "regular" method-accessible properties.
+     */
+    protected final transient Method _setter;
+    
+    public MethodProperty(BeanPropertyDefinition propDef,
+            JavaType type, TypeDeserializer typeDeser,
+            Annotations contextAnnotations, AnnotatedMethod method)
+    {
+        super(propDef, type, typeDeser, contextAnnotations);
+        _annotated = method;
+        _setter = method.getAnnotated();
+    }
+
+    protected MethodProperty(MethodProperty src, JsonDeserializer<?> deser) {
+        super(src, deser);
+        _annotated = src._annotated;
+        _setter = src._setter;
+    }
+
+    protected MethodProperty(MethodProperty src, String newName) {
+        super(src, newName);
+        _annotated = src._annotated;
+        _setter = src._setter;
+    }
+
+    /**
+     * Constructor used for JDK Serialization when reading persisted object
+     */
+    protected MethodProperty(MethodProperty src, Method m) {
+        super(src);
+        _annotated = src._annotated;
+        _setter = m;
+    }
+    
+    @Override
+    public MethodProperty withName(String newName) {
+        return new MethodProperty(this, newName);
+    }
+    
+    @Override
+    public MethodProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new MethodProperty(this, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        return _annotated.getAnnotation(acls);
+    }
+
+    @Override public AnnotatedMember getMember() {  return _annotated; }
+
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+            Object instance)
+        throws IOException, JsonProcessingException
+    {
+        set(instance, deserialize(jp, ctxt));
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+    	return setAndReturn(instance, deserialize(jp, ctxt));
+    }
+    
+    @Override
+    public final void set(Object instance, Object value)
+        throws IOException
+    {
+        try {
+            _setter.invoke(instance, value);
+        } catch (Exception e) {
+            _throwAsIOE(e, value);
+        }
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value)
+		throws IOException
+    {
+        try {
+            Object result = _setter.invoke(instance, value);
+            return (result == null) ? instance : result;
+        } catch (Exception e) {
+            _throwAsIOE(e, value);
+            return null;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object readResolve() {
+        return new MethodProperty(this, _annotated.getAnnotated());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java
new file mode 100644
index 0000000..54e3d4f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/NullProvider.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * To support [JACKSON-420] we need bit more indirection; this is used to produce
+ * artificial failure for primitives that don't accept JSON null as value.
+ */
+public final class NullProvider
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Object _nullValue;
+
+    private final boolean _isPrimitive;
+    
+    private final Class<?> _rawType;
+    
+    public NullProvider(JavaType type, Object nullValue)
+    {
+        _nullValue = nullValue;
+        // [JACKSON-420]
+        _isPrimitive = type.isPrimitive();
+        _rawType = type.getRawClass();
+    }
+
+    public Object nullValue(DeserializationContext ctxt) throws JsonProcessingException
+    {
+        if (_isPrimitive && ctxt.isEnabled(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)) {
+            throw ctxt.mappingException("Can not map JSON null into type "+_rawType.getName()
+                    +" (set DeserializationConfig.DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES to 'false' to allow)");
+        }
+        return _nullValue;
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..b049996
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdReader.java
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+
+/**
+ * Object that knows how to serialize Object Ids.
+ */
+public final class ObjectIdReader
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    public final JavaType idType;
+
+    public final String propertyName;
+    
+    /**
+     * Blueprint generator instance: actual instance will be
+     * fetched from {@link SerializerProvider} using this as
+     * the key.
+     */
+    public final ObjectIdGenerator<?> generator;
+    
+    /**
+     * Deserializer used for deserializing id values.
+     */
+    public final JsonDeserializer<Object> deserializer;
+
+    public final SettableBeanProperty idProperty;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    protected ObjectIdReader(JavaType t, String propName, ObjectIdGenerator<?> gen,
+            JsonDeserializer<?> deser, SettableBeanProperty idProp)
+    {
+        idType = t;
+        propertyName = propName;
+        generator = gen;
+        deserializer = (JsonDeserializer<Object>) deser;
+        idProperty = idProp;
+    }
+
+    /**
+     * Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase}
+     * with the initial information based on standard settings for the type
+     * for which serializer is being built.
+     */
+    public static ObjectIdReader construct(JavaType idType, String propName,
+            ObjectIdGenerator<?> generator, JsonDeserializer<?> deser,
+            SettableBeanProperty idProp)
+    {
+        return new ObjectIdReader(idType, propName, generator, deser, idProp);
+    }
+}
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
new file mode 100644
index 0000000..c1bf46e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ObjectIdValueProperty.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+
+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.introspect.AnnotatedMember;
+
+/**
+ * Specialized {@link SettableBeanProperty} implementation used
+ * for virtual property that represents Object Id that is used
+ * for some POJO types (or properties).
+ */
+public final class ObjectIdValueProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final ObjectIdReader _objectIdReader;
+
+    @Deprecated // since 2.2
+    public ObjectIdValueProperty(ObjectIdReader objectIdReader) {
+        this(objectIdReader, true);
+    }
+    
+    public ObjectIdValueProperty(ObjectIdReader objectIdReader,
+            boolean isRequired)
+    {
+        super(objectIdReader.propertyName, objectIdReader.idType, null, null, null,
+                isRequired);
+        _objectIdReader = objectIdReader;
+        _valueDeserializer = objectIdReader.deserializer;
+    }
+
+    protected ObjectIdValueProperty(ObjectIdValueProperty src, JsonDeserializer<?> deser)
+    {
+        super(src, deser);
+        _objectIdReader = src._objectIdReader;
+    }
+
+    protected ObjectIdValueProperty(ObjectIdValueProperty src, String newName) {
+        super(src, newName);
+        _objectIdReader = src._objectIdReader;
+    }
+
+    @Override
+    public ObjectIdValueProperty withName(String newName) {
+        return new ObjectIdValueProperty(this, newName);
+    }
+
+    @Override
+    public ObjectIdValueProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new ObjectIdValueProperty(this, deser);
+    }
+    
+    // // // BeanProperty impl
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        return null;
+    }
+
+    @Override public AnnotatedMember getMember() {  return null; }
+
+    /*
+    /**********************************************************
+    /* Deserialization methods
+    /**********************************************************
+     */
+
+    @Override
+    public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+            Object instance)
+        throws IOException, JsonProcessingException
+    {
+    	deserializeSetAndReturn(jp, ctxt, instance);
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+        // note: no null checks (unlike usually); deserializer should fail if one found
+        Object id = _valueDeserializer.deserialize(jp, ctxt);
+        ReadableObjectId roid = ctxt.findObjectId(id, _objectIdReader.generator);
+        roid.bindItem(instance);
+        // also: may need to set a property value as well
+        SettableBeanProperty idProp = _objectIdReader.idProperty;
+        if (idProp != null) {
+            return idProp.setAndReturn(instance, id);
+        }
+        return instance;
+    }
+    
+    
+    @Override
+    public void set(Object instance, Object value) throws IOException {
+    	setAndReturn(instance, value);
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value)
+        throws IOException
+    {
+        SettableBeanProperty idProp = _objectIdReader.idProperty;
+        if (idProp == null) {
+            throw new UnsupportedOperationException(
+                    "Should not call set() on ObjectIdProperty that has no SettableBeanProperty");
+        }
+        return idProp.setAndReturn(instance, value);
+    }
+}
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
new file mode 100644
index 0000000..324683d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedCreator.java
@@ -0,0 +1,168 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Object that is used to collect arguments for non-default creator
+ * (non-default-constructor, or argument-taking factory method)
+ * before creator can be called.
+ * Since ordering of JSON properties is not guaranteed, this may
+ * require buffering of values other than ones being passed to
+ * creator.
+ */
+public final class PropertyBasedCreator
+{
+    protected final ValueInstantiator _valueInstantiator;
+    
+    /**
+     * Map that contains property objects for either constructor or factory
+     * method (whichever one is null: one property for each
+     * parameter for that one), keyed by logical property name
+     */
+    protected final HashMap<String, SettableBeanProperty> _properties;
+
+    /**
+     * Number of properties: usually same as size of {@link #_properties},
+     * but not necessarily, when we have unnamed injectable properties.
+     */
+    protected final int _propertyCount;
+    
+    /**
+     * If some property values must always have a non-null value (like
+     * primitive types do), this array contains such default values.
+     */
+    protected final Object[]  _defaultValues;
+
+    /**
+     * Array that contains properties that expect value to inject, if any;
+     * null if no injectable values are expected.
+     */
+    protected final SettableBeanProperty[] _propertiesWithInjectables;
+    
+    /*
+    /**********************************************************
+    /* Construction, initialization
+    /**********************************************************
+     */
+    
+    protected PropertyBasedCreator(ValueInstantiator valueInstantiator,
+            SettableBeanProperty[] creatorProps, Object[] defaultValues)
+    {
+        _valueInstantiator = valueInstantiator;
+        _properties = new HashMap<String, SettableBeanProperty>();
+        SettableBeanProperty[] propertiesWithInjectables = null;
+        final int len = creatorProps.length;
+        _propertyCount = len;
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = creatorProps[i];
+            _properties.put(prop.getName(), prop);
+            Object injectableValueId = prop.getInjectableValueId();
+            if (injectableValueId != null) {
+                if (propertiesWithInjectables == null) {
+                    propertiesWithInjectables = new SettableBeanProperty[len];
+                }
+                propertiesWithInjectables[i] = prop;
+            }
+        }
+        _defaultValues = defaultValues;
+        _propertiesWithInjectables = propertiesWithInjectables;
+    }
+
+    /**
+     * Factory method used for building actual instances: resolves deserializers
+     * and checks for "null values".
+     */
+    public static PropertyBasedCreator construct(DeserializationContext ctxt,
+            ValueInstantiator valueInstantiator, SettableBeanProperty[] srcProps)
+        throws JsonMappingException
+    {
+        final int len = srcProps.length;
+        SettableBeanProperty[] creatorProps = new SettableBeanProperty[len];
+        Object[] defaultValues = null;
+        for (int i = 0; i < len; ++i) {
+            SettableBeanProperty prop = srcProps[i];
+            if (!prop.hasValueDeserializer()) {
+                prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop));
+            }
+            creatorProps[i] = prop;
+            // [JACKSON-372]: primitive types need extra care
+            // [JACKSON-774]: as do non-default nulls...
+            JsonDeserializer<?> deser = prop.getValueDeserializer();
+            Object nullValue = (deser == null) ? null : deser.getNullValue();
+            if ((nullValue == null) && prop.getType().isPrimitive()) {
+                nullValue = ClassUtil.defaultValue(prop.getType().getRawClass());
+            }
+            if (nullValue != null) {
+                if (defaultValues == null) {
+                    defaultValues = new Object[len];
+                }
+                defaultValues[i] = nullValue;
+            }
+        }
+        return new PropertyBasedCreator(valueInstantiator, creatorProps, defaultValues);
+    }
+    
+    public void assignDeserializer(SettableBeanProperty prop, JsonDeserializer<Object> deser) {
+        prop = prop.withValueDeserializer(deser);
+        _properties.put(prop.getName(), prop);
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    public Collection<SettableBeanProperty> properties() {
+        return _properties.values();
+    }
+    
+    public SettableBeanProperty findCreatorProperty(String name) {
+        return _properties.get(name);
+    }
+
+    /*
+    /**********************************************************
+    /* Building process
+    /**********************************************************
+     */
+
+    /**
+     * Method called when starting to build a bean instance.
+     * 
+     * @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without)
+     */
+    public PropertyValueBuffer startBuilding(JsonParser jp, DeserializationContext ctxt,
+            ObjectIdReader oir)
+    {
+        PropertyValueBuffer buffer = new PropertyValueBuffer(jp, ctxt, _propertyCount, oir);
+        if (_propertiesWithInjectables != null) {
+            buffer.inject(_propertiesWithInjectables);
+        }
+        return buffer;
+    }
+
+    public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException
+    {
+        Object bean = _valueInstantiator.createFromObjectWith(ctxt, buffer.getParameters(_defaultValues));
+        // Object Id to handle?
+        bean = buffer.handleIdValue(ctxt, bean);
+        
+        // Anything buffered?
+        for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) {
+            pv.assign(bean);
+        }
+        return bean;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java
new file mode 100644
index 0000000..0ffd8b3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyBasedObjectIdGenerator.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+// Simple placeholder
+public class PropertyBasedObjectIdGenerator
+	extends ObjectIdGenerators.PropertyGenerator
+{
+    private static final long serialVersionUID = 1L;
+
+    public PropertyBasedObjectIdGenerator(Class<?> scope) {
+        super(scope);
+    }
+    
+    @Override
+    public Object generateId(Object forPojo) {
+    	throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ObjectIdGenerator<Object> forScope(Class<?> scope) {
+        return (scope == _scope) ? this : new PropertyBasedObjectIdGenerator(scope);
+    }
+
+    @Override
+    public ObjectIdGenerator<Object> newForSerialization(Object context) {
+        return this;
+    }
+
+    @Override
+    public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
+        // should we use general type for all; or type of property itself?
+        return new IdKey(getClass(), _scope, key);
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java
new file mode 100644
index 0000000..c23f043
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValue.java
@@ -0,0 +1,118 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.deser.SettableAnyProperty;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+
+/**
+ * Base class for property values that need to be buffered during
+ * deserialization.
+ */
+public abstract class PropertyValue
+{
+    public final PropertyValue next;
+
+    /**
+     * Value to assign when POJO has been instantiated.
+     */
+    public final Object value;
+    
+    protected PropertyValue(PropertyValue next, Object value)
+    {
+        this.next = next;
+        this.value = value;
+    }
+
+    /**
+     * Method called to assign stored value of this property to specified
+     * bean instance
+     */
+    public abstract void assign(Object bean)
+        throws IOException, JsonProcessingException;
+
+    /*
+    /**********************************************************
+    /* Concrete property value classes
+    /**********************************************************
+     */
+
+    /**
+     * Property value that used when assigning value to property using
+     * a setter method or direct field access.
+     */
+    final static class Regular
+        extends PropertyValue
+    {
+        final SettableBeanProperty _property;
+        
+        public Regular(PropertyValue next, Object value,
+                       SettableBeanProperty prop)
+        {
+            super(next, value);
+            _property = prop;
+        }
+
+        @Override
+        public void assign(Object bean)
+            throws IOException, JsonProcessingException
+        {
+            _property.set(bean, value);
+        }
+    }
+    
+    /**
+     * Property value type used when storing entries to be added
+     * to a POJO using "any setter" (method that takes name and
+     * value arguments, allowing setting multiple different
+     * properties using single method).
+     */
+    final static class Any
+        extends PropertyValue
+    {
+        final SettableAnyProperty _property;
+        final String _propertyName;
+        
+        public Any(PropertyValue next, Object value,
+                   SettableAnyProperty prop,
+                   String propName)
+        {
+            super(next, value);
+            _property = prop;
+            _propertyName = propName;
+        }
+
+        @Override
+        public void assign(Object bean)
+            throws IOException, JsonProcessingException
+        {
+            _property.set(bean, _propertyName, value);
+        }
+    }
+
+    /**
+     * Property value type used when storing entries to be added
+     * to a Map.
+     */
+    final static class Map
+        extends PropertyValue
+    {
+        final Object _key;
+        
+        public Map(PropertyValue next, Object value, Object key)
+        {
+            super(next, value);
+            _key = key;
+        }
+
+        @SuppressWarnings("unchecked") 
+        @Override
+        public void assign(Object bean)
+            throws IOException, JsonProcessingException
+        {
+            ((java.util.Map<Object,Object>) bean).put(_key, value);
+        }
+    }
+}
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
new file mode 100644
index 0000000..2ea3815
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/PropertyValueBuffer.java
@@ -0,0 +1,155 @@
+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.SettableAnyProperty;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+
+/**
+ * Simple container used for temporarily buffering a set of
+ * <code>PropertyValue</code>s.
+ * Using during construction of beans (and Maps) that use Creators, 
+ * and hence need buffering before instance (that will have properties
+ * to assign values to) is constructed.
+ */
+public final class PropertyValueBuffer
+{
+    protected final JsonParser _parser;
+    protected final DeserializationContext _context;
+    
+    /**
+     * Buffer used for storing creator parameters for constructing
+     * instance
+     */
+    protected final Object[] _creatorParameters;
+
+    protected final ObjectIdReader _objectIdReader;
+    
+    /**
+     * Number of creator parameters we are still missing.
+     *<p>
+     * NOTE: assumes there are no duplicates, for now.
+     */
+    private int _paramsNeeded;
+    
+    /**
+     * If we get non-creator parameters before or between
+     * creator parameters, those need to be buffered. Buffer
+     * is just a simple linked list
+     */
+    private PropertyValue _buffered;
+
+    /**
+     * In case there is an Object Id property to handle, this is the value
+     * we have for it.
+     */
+    private Object _idValue;
+    
+    public PropertyValueBuffer(JsonParser jp, DeserializationContext ctxt, int paramCount,
+            ObjectIdReader oir)
+    {
+        _parser = jp;
+        _context = ctxt;
+        _paramsNeeded = paramCount;
+        _objectIdReader = oir;
+        _creatorParameters = new Object[paramCount];
+    }
+
+    public void inject(SettableBeanProperty[] injectableProperties)
+    {
+        for (int i = 0, len = injectableProperties.length; i < len; ++i) {
+            SettableBeanProperty prop = injectableProperties[i];
+            if (prop != null) {
+                // null since there is no POJO yet
+                _creatorParameters[i] = _context.findInjectableValue(prop.getInjectableValueId(),
+                        prop, null);
+            }
+        }
+    }
+    
+    /**
+     * @param defaults If any of parameters requires nulls to be replaced with a non-null
+     *    object (usually primitive types), this is a non-null array that has such replacement
+     *    values (and nulls for cases where nulls are ok)
+     */
+    protected final Object[] getParameters(Object[] defaults)
+    {
+        if (defaults != null) {
+            for (int i = 0, len = _creatorParameters.length; i < len; ++i) {
+                if (_creatorParameters[i] == null) {
+                    Object value = defaults[i];
+                    if (value != null) {
+                        _creatorParameters[i] = value;
+                    }
+                }
+            }
+        }
+        return _creatorParameters;
+    }
+
+
+    /**
+     * Helper method called to see if given non-creator property is the "id property";
+     * and if so, handle appropriately.
+     * 
+     * @since 2.1
+     */
+    public boolean readIdProperty(String propName) throws IOException
+    {
+        if ((_objectIdReader != null) && propName.equals(_objectIdReader.propertyName)) {
+            _idValue = _objectIdReader.deserializer.deserialize(_parser, _context);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Helper method called to handle Object Id value collected earlier, if any
+     */
+    public Object handleIdValue(final DeserializationContext ctxt, Object bean)
+        throws IOException
+    {
+        if (_objectIdReader != null) {
+            if (_idValue != null) {
+                ReadableObjectId roid = ctxt.findObjectId(_idValue, _objectIdReader.generator);
+                roid.bindItem(bean);
+                // also: may need to set a property value as well
+                SettableBeanProperty idProp = _objectIdReader.idProperty;
+                if (idProp != null) {
+                    return idProp.setAndReturn(bean, _idValue);
+                }
+            } else {
+                // TODO: is this an error case?
+            }
+        }
+        return bean;
+    }
+    
+    protected PropertyValue buffered() { return _buffered; }
+
+    public boolean isComplete() { return _paramsNeeded <= 0; }
+    
+    /**
+     * @return True if we have received all creator parameters
+     */
+    public boolean assignParameter(int index, Object value) {
+        _creatorParameters[index] = value;
+        return --_paramsNeeded <= 0;
+    }
+    
+    public void bufferProperty(SettableBeanProperty prop, Object value) {
+        _buffered = new PropertyValue.Regular(_buffered, value, prop);
+    }
+    
+    public void bufferAnyProperty(SettableAnyProperty prop, String propName, Object value) {
+        _buffered = new PropertyValue.Any(_buffered, value, prop, propName);
+    }
+
+    public void bufferMapProperty(Object key, Object value) {
+        _buffered = new PropertyValue.Map(_buffered, value, key);
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java
new file mode 100644
index 0000000..90aaa96
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ReadableObjectId.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+/**
+ * Simple value container for containing information about single
+ * Object Id during deserialization.
+ */
+public class ReadableObjectId
+{
+    public final Object id;
+    
+    public Object item;
+    
+    public ReadableObjectId(Object id)
+    {
+        this.id = id;
+    }
+
+    /**
+     * Method called to assign actual POJO to which ObjectId refers to:
+     * will also handle referring properties, if any, by assigning POJO.
+     */
+    public void bindItem(Object ob) throws IOException
+    {
+        if (item != null) {
+            throw new IllegalStateException("Already had POJO for id ("+id.getClass().getName()+") ["+id+"]");
+        }
+        item = ob;
+    }
+}
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
new file mode 100644
index 0000000..b742dd8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/SetterlessProperty.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+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.SettableBeanProperty;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * This concrete sub-class implements Collection or Map property that is
+ * indirectly by getting the property value and directly modifying it.
+ */
+public final class SetterlessProperty
+    extends SettableBeanProperty
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final AnnotatedMethod _annotated;
+
+    /**
+     * Get method for accessing property value used to access property
+     * (of Collection or Map type) to modify.
+     */
+    protected final Method _getter;
+
+    public SetterlessProperty(BeanPropertyDefinition propDef, JavaType type,
+            TypeDeserializer typeDeser, Annotations contextAnnotations, AnnotatedMethod method)
+        {
+        super(propDef, type, typeDeser, contextAnnotations);
+        _annotated = method;
+        _getter = method.getAnnotated();
+    }
+
+    protected SetterlessProperty(SetterlessProperty src, JsonDeserializer<?> deser) {
+        super(src, deser);
+        _annotated = src._annotated;
+        _getter = src._getter;
+    }
+
+    protected SetterlessProperty(SetterlessProperty src, String newName) {
+        super(src, newName);
+        _annotated = src._annotated;
+        _getter = src._getter;
+    }
+
+    @Override
+    public SetterlessProperty withName(String newName) {
+        return new SetterlessProperty(this, newName);
+    }
+    
+    @Override
+    public SetterlessProperty withValueDeserializer(JsonDeserializer<?> deser) {
+        return new SetterlessProperty(this, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        return _annotated.getAnnotation(acls);
+    }
+
+    @Override public AnnotatedMember getMember() {  return _annotated; }
+
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+    
+    @Override
+    public final void deserializeAndSet(JsonParser jp, DeserializationContext ctxt,
+            Object instance)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.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
+             */
+            return;
+        }
+
+        // Ok: then, need to fetch Collection/Map to modify:
+        Object toModify;
+        try {
+            toModify = _getter.invoke(instance);
+        } catch (Exception e) {
+            _throwAsIOE(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.
+         */
+        if (toModify == null) {
+            throw new JsonMappingException("Problem deserializing 'setterless' property '"+getName()+"': get method returned null");
+        }
+        _valueDeserializer.deserialize(jp, ctxt, toModify);
+    }
+
+    @Override
+    public Object deserializeSetAndReturn(JsonParser jp,
+    		DeserializationContext ctxt, Object instance)
+        throws IOException, JsonProcessingException
+    {
+    	deserializeAndSet(jp, 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");
+    }
+
+    @Override
+    public Object setAndReturn(Object instance, Object value)
+            throws IOException
+    {
+    	set(instance, value);
+    	return null;
+    }
+}
\ 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
new file mode 100644
index 0000000..27b34ac
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/TypeWrappedDeserializer.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Simple deserializer that will call configured type deserializer, passing
+ * in configured data deserializer, and exposing it all as a simple
+ * deserializer.
+ * This is necessary when there is no "parent" deserializer which could handle
+ * details of calling a {@link TypeDeserializer}, most commonly used with
+ * root values.
+ */
+public final class TypeWrappedDeserializer
+    extends JsonDeserializer<Object>
+{
+    final TypeDeserializer _typeDeserializer;
+    final JsonDeserializer<Object> _deserializer;
+
+    public TypeWrappedDeserializer(TypeDeserializer typeDeser, JsonDeserializer<Object> deser)
+    {
+        super();
+        _typeDeserializer = typeDeser;
+        _deserializer = deser;
+    }
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        return _deserializer.deserializeWithType(jp, ctxt, _typeDeserializer);
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+        TypeDeserializer typeDeserializer)
+            throws IOException, JsonProcessingException
+    {
+        // should never happen? (if it can, could call on that object)
+        throw new IllegalStateException("Type-wrapped deserializer's deserializeWithType should never get called");
+    }
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt,
+            Object intoValue)
+        throws IOException, JsonProcessingException
+    {
+        /* 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);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
new file mode 100644
index 0000000..3da7ffb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/UnwrappedPropertyHandler.java
@@ -0,0 +1,65 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Object that is responsible for handling acrobatics related to
+ * deserializing "unwrapped" values; sets of properties that are
+ * embedded (inlined) as properties of parent JSON object.
+ */
+public class UnwrappedPropertyHandler
+{
+    protected final List<SettableBeanProperty> _properties;
+
+    public UnwrappedPropertyHandler()  {
+        _properties = new ArrayList<SettableBeanProperty>();
+   }
+    protected UnwrappedPropertyHandler(List<SettableBeanProperty> props)  {
+        _properties = props;
+    }
+
+    public void addProperty(SettableBeanProperty property) {
+        _properties.add(property);
+    }
+
+    public UnwrappedPropertyHandler renameAll(NameTransformer transformer)
+    {
+        ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(_properties.size());
+        for (SettableBeanProperty prop : _properties) {
+            String newName = transformer.transform(prop.getName());
+            prop = prop.withName(newName);
+            JsonDeserializer<?> deser = prop.getValueDeserializer();
+            if (deser != null) {
+                @SuppressWarnings("unchecked")
+                JsonDeserializer<Object> newDeser = (JsonDeserializer<Object>)
+                    deser.unwrappingDeserializer(transformer);
+                if (newDeser != deser) {
+                    prop = prop.withValueDeserializer(newDeser);
+                }
+            }
+            newProps.add(prop);
+        }
+        return new UnwrappedPropertyHandler(newProps);
+    }
+    
+    public Object processUnwrapped(JsonParser originalParser, DeserializationContext ctxt, Object bean,
+            TokenBuffer buffered)
+        throws IOException, JsonProcessingException
+    {
+        for (int i = 0, len = _properties.size(); i < len; ++i) {
+            SettableBeanProperty prop = _properties.get(i);
+            JsonParser jp = buffered.asParser();
+            jp.nextToken();
+            prop.deserializeAndSet(jp, ctxt, bean);
+        }
+        return bean;
+    }
+}
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
new file mode 100644
index 0000000..d60a92c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/ValueInjector.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.databind.deser.impl;
+
+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.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * Class that encapsulates details of value injection that occurs before
+ * deserialization of a POJO. Details include information needed to find
+ * injectable value (logical id) as well as method used for assigning
+ * value (setter or field)
+ */
+public class ValueInjector
+    extends BeanProperty.Std
+{
+    /**
+     * Identifier used for looking up value to inject
+     */
+    protected final Object _valueId;
+
+    public ValueInjector(String propertyName, JavaType type,
+            Annotations contextAnnotations, AnnotatedMember mutator,
+            Object valueId)
+    {
+        super(propertyName, type, null, contextAnnotations, mutator, false);
+        _valueId = valueId;
+    }
+
+    public Object findValue(DeserializationContext context, Object beanInstance)
+    {
+        return context.findInjectableValue(_valueId, this, beanInstance);
+    }
+    
+    public void inject(DeserializationContext context, Object beanInstance)
+        throws IOException
+    {
+        _member.setValue(beanInstance, findValue(context, beanInstance));
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/impl/package-info.java b/src/main/java/com/fasterxml/jackson/databind/deser/impl/package-info.java
new file mode 100644
index 0000000..5ce9e49
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/impl/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Contains those implementation classes of deserialization part of 
+ * data binding that are not considered part of public or semi-public
+ * interfaces. Use of these classes by non-core classes is discouraged,
+ * although occasionally this may be necessary.
+ * Note that backwards-compatibility of these classes is not guaranteed
+ * between minor releases (but is between patch releases).
+ */
+package com.fasterxml.jackson.databind.deser.impl;
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/package-info.java b/src/main/java/com/fasterxml/jackson/databind/deser/package-info.java
new file mode 100644
index 0000000..3bba0d0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Contains implementation classes of deserialization part of 
+ * data binding.
+ */
+package com.fasterxml.jackson.databind.deser;
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
new file mode 100644
index 0000000..93faec6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ArrayBlockingQueueDeserializer.java
@@ -0,0 +1,134 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * We need a custom deserializer both because {@link ArrayBlockingQueue} has no
+ * default constructor AND because it has size limit used for constructing
+ * underlying storage automatically.
+ */
+public class ArrayBlockingQueueDeserializer
+    extends CollectionDeserializer
+{
+    private static final long serialVersionUID = 5471961369237518580L;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used when creating contextualized instances.
+     */
+    public ArrayBlockingQueueDeserializer(JavaType collectionType,
+            JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
+            ValueInstantiator valueInstantiator,
+            JsonDeserializer<Object> delegateDeser)
+    {
+        super(collectionType, valueDeser, valueTypeDeser, valueInstantiator, delegateDeser);
+    }
+
+    /**
+     * Copy-constructor that can be used by sub-classes to allow
+     * copy-on-write styling copying of settings of an existing instance.
+     */
+    protected ArrayBlockingQueueDeserializer(ArrayBlockingQueueDeserializer src) {
+        super(src);
+    }
+
+    /**
+     * Fluent-factory method call to construct contextual instance.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    protected ArrayBlockingQueueDeserializer withResolved(JsonDeserializer<?> dd,
+            JsonDeserializer<?> vd, TypeDeserializer vtd)
+    {
+        if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer)) {
+            return this;
+        }
+        return new ArrayBlockingQueueDeserializer(_collectionType,
+                (JsonDeserializer<Object>) vd, vtd,
+                _valueInstantiator, (JsonDeserializer<Object>) dd);
+                
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        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);
+    }
+
+    @Override
+    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt,
+            Collection<Object> result0)
+        throws IOException, JsonProcessingException
+    {
+        // 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;
+
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            Object value;
+            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            tmp.add(value);
+        }
+        if (result0 != null) {
+            result0.addAll(tmp);
+            return result0;
+        }
+        return new ArrayBlockingQueue<Object>(tmp.size(), false, tmp);
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/ClassDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/ClassDeserializer.java
new file mode 100644
index 0000000..30770b6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ClassDeserializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+ at JacksonStdImpl
+public class ClassDeserializer
+    extends StdScalarDeserializer<Class<?>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public final static ClassDeserializer instance = new ClassDeserializer();
+    
+    public ClassDeserializer() { super(Class.class); }
+
+    @Override
+    public Class<?> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken curr = jp.getCurrentToken();
+        // Currently will only accept if given simple class name
+        if (curr == JsonToken.VALUE_STRING) {
+            String className = jp.getText().trim();
+            try {
+                return ctxt.findClass(className);
+            } catch (Exception e) {
+                throw ctxt.instantiationException(_valueClass, ClassUtil.getRootCause(e));
+            }
+        }
+        throw ctxt.mappingException(_valueClass, curr);
+    }
+}
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
new file mode 100644
index 0000000..ee9d337
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/CollectionDeserializer.java
@@ -0,0 +1,278 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+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.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Basic serializer that can take JSON "Array" structure and
+ * construct a {@link java.util.Collection} instance, with typed contents.
+ *<p>
+ * Note: for untyped content (one indicated by passing Object.class
+ * as the type), {@link UntypedObjectDeserializer} is used instead.
+ * It can also construct {@link java.util.List}s, but not with specific
+ * POJO types, only other containers and primitives/wrappers.
+ */
+ at JacksonStdImpl
+public class CollectionDeserializer
+    extends ContainerDeserializerBase<Collection<Object>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = -2003828398549708958L;
+
+    // // Configuration
+
+    protected final JavaType _collectionType;
+    
+    /**
+     * Value deserializer.
+     */
+    protected final JsonDeserializer<Object> _valueDeserializer;
+
+    /**
+     * If element instances have polymorphic type information, this
+     * is the type deserializer that can handle it
+     */
+    protected final TypeDeserializer _valueTypeDeserializer;
+
+    // // Instance construction settings:
+    
+    protected final ValueInstantiator _valueInstantiator;
+    
+    /**
+     * Deserializer that is used iff delegate-based creator is
+     * to be used for deserializing from JSON Object.
+     */
+    protected final JsonDeserializer<Object> _delegateDeserializer;
+
+    // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * Constructor for context-free instances, where we do not yet know
+     * which property is using this deserializer.
+     */
+    public CollectionDeserializer(JavaType collectionType,
+            JsonDeserializer<Object> valueDeser,
+            TypeDeserializer valueTypeDeser, ValueInstantiator valueInstantiator)
+    {
+        this(collectionType, valueDeser, valueTypeDeser, valueInstantiator, null);
+    }
+
+    /**
+     * Constructor used when creating contextualized instances.
+     */
+    protected CollectionDeserializer(JavaType collectionType,
+            JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser,
+            ValueInstantiator valueInstantiator,
+            JsonDeserializer<Object> delegateDeser)
+    {
+        super(collectionType.getRawClass());
+        _collectionType = collectionType;
+        _valueDeserializer = valueDeser;
+        _valueTypeDeserializer = valueTypeDeser;
+        _valueInstantiator = valueInstantiator;
+        _delegateDeserializer = delegateDeser;
+    }
+
+    /**
+     * Copy-constructor that can be used by sub-classes to allow
+     * copy-on-write styling copying of settings of an existing instance.
+     */
+    protected CollectionDeserializer(CollectionDeserializer src)
+    {
+        super(src._valueClass);
+        _collectionType = src._collectionType;
+        _valueDeserializer = src._valueDeserializer;
+        _valueTypeDeserializer = src._valueTypeDeserializer;
+        _valueInstantiator = src._valueInstantiator;
+        _delegateDeserializer = src._delegateDeserializer;
+    }
+
+    /**
+     * Fluent-factory method call to construct contextual instance.
+     */
+    @SuppressWarnings("unchecked")
+    protected CollectionDeserializer withResolved(JsonDeserializer<?> dd,
+            JsonDeserializer<?> vd, TypeDeserializer vtd)
+    {
+        if ((dd == _delegateDeserializer) && (vd == _valueDeserializer) && (vtd == _valueTypeDeserializer)) {
+            return this;
+        }
+        return new CollectionDeserializer(_collectionType,
+                (JsonDeserializer<Object>) vd, vtd,
+                _valueInstantiator, (JsonDeserializer<Object>) dd);
+                
+    }
+    
+    /*
+    /**********************************************************
+    /* Validation, post-processing (ResolvableDeserializer)
+    /**********************************************************
+     */
+
+    /**
+     * Method called to finalize setup of this deserializer,
+     * when it is known for which property deserializer is needed
+     * for.
+     */
+    @Override
+    public CollectionDeserializer createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        // May need to resolve types for delegate-based creators:
+        JsonDeserializer<Object> delegateDeser = null;
+        if ((_valueInstantiator != null) && _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()'");
+            }
+            delegateDeser = findDeserializer(ctxt, delegateType, property);
+        }
+        // also, often value deserializer is resolved here:
+        JsonDeserializer<?> valueDeser = _valueDeserializer;
+        // #125: May have a content converter
+        valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
+        if (valueDeser == null) {
+            valueDeser = ctxt.findContextualValueDeserializer(
+                    _collectionType.getContentType(), property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (valueDeser instanceof ContextualDeserializer) {
+                valueDeser = ((ContextualDeserializer) valueDeser).createContextual(ctxt, property);
+            }
+        }
+        // and finally, type deserializer needs context as well
+        TypeDeserializer valueTypeDeser = _valueTypeDeserializer;
+        if (valueTypeDeser != null) {
+            valueTypeDeser = valueTypeDeser.forProperty(property);
+        }
+        return withResolved(delegateDeser, valueDeser, valueTypeDeser);
+    }
+    
+    /*
+    /**********************************************************
+    /* ContainerDeserializerBase API
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return _collectionType.getContentType();
+    }
+
+    @Override
+    public JsonDeserializer<Object> getContentDeserializer() {
+        return _valueDeserializer;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            return (Collection<Object>) _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        /* [JACKSON-620]: 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 (jp.getCurrentToken() == JsonToken.VALUE_STRING) {
+            String str = jp.getText();
+            if (str.length() == 0) {
+                return (Collection<Object>) _valueInstantiator.createFromString(ctxt, str);
+            }
+        }
+        return deserialize(jp, ctxt, (Collection<Object>) _valueInstantiator.createUsingDefault(ctxt));
+    }
+
+    @Override
+    public Collection<Object> deserialize(JsonParser jp, DeserializationContext ctxt,
+            Collection<Object> result)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!jp.isExpectedStartArrayToken()) {
+            return handleNonArray(jp, ctxt, result);
+        }
+
+        JsonDeserializer<Object> valueDes = _valueDeserializer;
+        JsonToken t;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            Object value;
+            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            result.add(value);
+        }
+        return result;
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+
+    /**
+     * Helper method called when current token is no START_ARRAY. Will either
+     * throw an exception, or try to handle value as if member of implicit
+     * array, depending on configuration.
+     */
+    protected final Collection<Object> handleNonArray(JsonParser jp, DeserializationContext ctxt,
+            Collection<Object> result)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-526]: implicit arrays from single values?
+        if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+            throw ctxt.mappingException(_collectionType.getRawClass());
+        }
+        JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+        JsonToken t = jp.getCurrentToken();
+
+        Object value;
+        
+        if (t == JsonToken.VALUE_NULL) {
+            value = null;
+        } else if (typeDeser == null) {
+            value = valueDes.deserialize(jp, ctxt);
+        } else {
+            value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+        }
+        result.add(value);
+        return 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
new file mode 100644
index 0000000..93cdf04
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ContainerDeserializerBase.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+/**
+ * Intermediate base deserializer class that adds more shared accessor
+ * so that other classes can access information about contained (value)
+ * types
+ */
+ at SuppressWarnings("serial")
+public abstract class ContainerDeserializerBase<T>
+    extends StdDeserializer<T>
+{
+    protected ContainerDeserializerBase(Class<?> selfType)
+    {
+        super(selfType);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for declared type of contained value elements; either exact
+     * type, or one of its supertypes.
+     */
+    public abstract JavaType getContentType();
+
+    /**
+     * Accesor for deserializer use for deserializing content values.
+     */
+    public abstract JsonDeserializer<Object> getContentDeserializer();
+}
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
new file mode 100644
index 0000000..47e4864
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DateDeserializers.java
@@ -0,0 +1,364 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+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.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+
+/**
+ * Container class for core JDK date/time type deserializers.
+ */
+ at SuppressWarnings("serial")
+public class DateDeserializers
+{
+    private final static HashSet<String> _classNames = new HashSet<String>();
+    static {
+        Class<?>[] numberTypes = new Class<?>[] {
+            Calendar.class,
+            GregorianCalendar.class,
+            java.sql.Date.class,
+            java.util.Date.class,
+            Timestamp.class,
+            TimeZone.class
+        };
+        for (Class<?> cls : numberTypes) {
+            _classNames.add(cls.getName());
+        }
+    }
+
+    /**
+     * @deprecated Since 2.2 -- use {@link #find} instead.
+     */
+    @Deprecated
+    public static StdDeserializer<?>[] all()
+    {
+        return  new StdDeserializer[] {
+            CalendarDeserializer.instance, // for nominal type of java.util.Calendar
+            DateDeserializer.instance,
+            /* 24-Jan-2010, tatu: When including type information, we may
+             *    know that we specifically need GregorianCalendar...
+             */
+            CalendarDeserializer.gregorianInstance,
+            SqlDateDeserializer.instance,
+            TimestampDeserializer.instance,
+            TimeZoneDeserializer.instance
+        };
+    }
+
+    public static JsonDeserializer<?> find(Class<?> rawType, String clsName)
+    {
+        if (!_classNames.contains(clsName)) {
+            return null;
+        }
+        // Start with most common types; int, boolean, long, double
+        if (rawType == Calendar.class) {
+            return CalendarDeserializer.instance;
+        }
+        if (rawType == java.util.Date.class) {
+            return DateDeserializer.instance;
+        }
+        if (rawType == java.sql.Date.class) {
+            return SqlDateDeserializer.instance;
+        }
+        if (rawType == Timestamp.class) {
+            return TimestampDeserializer.instance;
+        }
+        if (rawType == TimeZone.class) {
+            return TimeZoneDeserializer.instance;
+        }
+        if (rawType == GregorianCalendar.class) {
+            return CalendarDeserializer.gregorianInstance;
+        }
+        // should never occur
+        throw new IllegalArgumentException("Internal error: can't find deserializer for "+clsName);
+    }
+    
+    /*
+    /**********************************************************
+    /* Intermediate class for Date-based ones
+    /**********************************************************
+     */
+
+    protected abstract static class DateBasedDeserializer<T>
+        extends StdScalarDeserializer<T>
+        implements ContextualDeserializer
+    {
+        /**
+         * Specific format to use, if non-null; if null will
+         * just use default format.
+         */
+        protected final DateFormat _customFormat;
+
+        /**
+         * 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;
+            _formatString = null;
+        }
+
+        protected DateBasedDeserializer(DateBasedDeserializer<T> base,
+                DateFormat format, String formatStr) {
+            super(base._valueClass);
+            _customFormat = format;
+            _formatString = formatStr;
+        }
+
+        protected abstract DateBasedDeserializer<T> withDateFormat(DateFormat df, String formatStr);
+        
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+           throws JsonMappingException
+        {
+            if (property != null) {
+                JsonFormat.Value format = ctxt.getAnnotationIntrospector().findFormat((Annotated) property.getMember());
+                if (format != null) {
+                    TimeZone tz = format.getTimeZone();
+                    // First: fully custom pattern?
+                    String pattern = format.getPattern();
+                    if (pattern.length() > 0){
+                        Locale loc = format.getLocale();
+                        if (loc == null) {
+                            loc = ctxt.getLocale();
+                        }
+                        SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
+                        if (tz == null) {
+                            tz = ctxt.getTimeZone();
+                        }
+                        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) {
+                            df = ((StdDateFormat) df).withTimeZone(tz);
+                        } else {
+                            // otherwise need to clone, re-set timezone:
+                            df = (DateFormat) df.clone();
+                            df.setTimeZone(tz);
+                        }
+                        return withDateFormat(df, pattern);
+                    }
+                }
+            }
+            return this;
+        }
+        
+        @Override
+        protected java.util.Date _parseDate(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (_customFormat != null && jp.getCurrentToken() == JsonToken.VALUE_STRING) {
+                String str = jp.getText().trim();
+                if (str.length() == 0) {
+                    return (Date) getEmptyValue();
+                }
+                synchronized (_customFormat) {
+                    try {
+                        return _customFormat.parse(str);
+                    } catch (ParseException e) {
+                        throw new IllegalArgumentException("Failed to parse Date value '"+str
+                                +"' (format: \""+_formatString+"\"): "+e.getMessage());
+                    }
+                }
+            }
+            return super._parseDate(jp, ctxt);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializer implementations for Date types
+    /**********************************************************
+     */
+    
+    @JacksonStdImpl
+    public static class CalendarDeserializer
+        extends DateBasedDeserializer<Calendar>
+    {
+        public final static CalendarDeserializer instance = new CalendarDeserializer();
+        public final static CalendarDeserializer gregorianInstance = new CalendarDeserializer(GregorianCalendar.class);
+        
+        /**
+         * We may know actual expected type; if so, it will be
+         * used for instantiation.
+         */
+        protected final Class<? extends Calendar> _calendarClass;
+        
+        public CalendarDeserializer() {
+            super(Calendar.class);
+            _calendarClass = null;
+        }
+
+        public CalendarDeserializer(Class<? extends Calendar> cc) {
+            super(cc);
+            _calendarClass = cc;
+        }
+
+        public CalendarDeserializer(CalendarDeserializer src, DateFormat df, String formatString) {
+            super(src, df, formatString);
+            _calendarClass = src._calendarClass;
+        }
+
+        @Override
+        protected CalendarDeserializer withDateFormat(DateFormat df, String formatString) {
+            return new CalendarDeserializer(this, df, formatString);
+        }
+        
+        @Override
+        public Calendar deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            Date d = _parseDate(jp, ctxt);
+            if (d == null) {
+                return null;
+            }
+            if (_calendarClass == null) {
+                return ctxt.constructCalendar(d);
+            }
+            try {
+                Calendar c = _calendarClass.newInstance();            
+                c.setTimeInMillis(d.getTime());
+                TimeZone tz = ctxt.getTimeZone();
+                if (tz != null) {
+                    c.setTimeZone(tz);
+                }
+                return c;
+            } catch (Exception e) {
+                throw ctxt.instantiationException(_calendarClass, e);
+            }
+        }
+    }
+
+    /**
+     * Simple deserializer for handling {@link java.util.Date} values.
+     *<p>
+     * One way to customize Date formats accepted is to override method
+     * {@link DeserializationContext#parseDate} that this basic
+     * deserializer calls.
+     */
+    public static class DateDeserializer
+        extends DateBasedDeserializer<Date>
+    {
+        public final static DateDeserializer instance = new DateDeserializer();
+
+        public DateDeserializer() { super(Date.class); }
+        public DateDeserializer(DateDeserializer base, DateFormat df, String formatString) {
+            super(base, df, formatString);
+        }
+
+        @Override
+        protected DateDeserializer withDateFormat(DateFormat df, String formatString) {
+            return new DateDeserializer(this, df, formatString);
+        }
+        
+        @Override
+        public java.util.Date deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseDate(jp, ctxt);
+        }
+    }
+
+    /**
+     * Compared to plain old {@link java.util.Date}, SQL version is easier
+     * to deal with: mostly because it is more limited.
+     */
+    public static class SqlDateDeserializer
+        extends DateBasedDeserializer<java.sql.Date>
+    {
+        public final static SqlDateDeserializer instance = new SqlDateDeserializer();
+
+        public SqlDateDeserializer() { super(java.sql.Date.class); }
+        public SqlDateDeserializer(SqlDateDeserializer src, DateFormat df, String formatString) {
+            super(src, df, formatString);
+        }
+
+        @Override
+        protected SqlDateDeserializer withDateFormat(DateFormat df, String formatString) {
+            return new SqlDateDeserializer(this, df, formatString);
+        }
+        
+        @Override
+        public java.sql.Date deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            Date d = _parseDate(jp, ctxt);
+            return (d == null) ? null : new java.sql.Date(d.getTime());
+        }
+    }
+
+    /**
+     * Simple deserializer for handling {@link java.sql.Timestamp} values.
+     *<p>
+     * One way to customize Timestamp formats accepted is to override method
+     * {@link DeserializationContext#parseDate} that this basic
+     * deserializer calls.
+     */
+    public static class TimestampDeserializer
+        extends DateBasedDeserializer<Timestamp>
+    {
+        public final static TimestampDeserializer instance = new TimestampDeserializer();
+
+        public TimestampDeserializer() { super(Timestamp.class); }
+        public TimestampDeserializer(TimestampDeserializer src, DateFormat df, String formatString) {
+            super(src, df, formatString);
+        }
+
+        @Override
+        protected TimestampDeserializer withDateFormat(DateFormat df, String formatString) {
+            return new TimestampDeserializer(this, df, formatString);
+        }
+        
+        @Override
+        public java.sql.Timestamp deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return new Timestamp(_parseDate(jp, ctxt).getTime());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Deserializer implementations for Date-related types
+    /**********************************************************
+     */
+    
+    /**
+     * As per [JACKSON-522], also need special handling for TimeZones
+     */
+    protected static class TimeZoneDeserializer
+        extends FromStringDeserializer<TimeZone>
+    {
+        public final static TimeZoneDeserializer instance = new TimeZoneDeserializer();
+
+        public TimeZoneDeserializer() { super(TimeZone.class); }
+
+        @Override
+        protected TimeZone _deserialize(String value, DeserializationContext ctxt)
+            throws IOException
+        {
+            return TimeZone.getTimeZone(value);
+        }
+    }
+}
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
new file mode 100644
index 0000000..a7aa75c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/DelegatingDeserializer.java
@@ -0,0 +1,149 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+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;
+
+/**
+ * Base class that simplifies implementations of {@link JsonDeserializer}s
+ * that mostly delegate functionality to another deserializer implementation
+ * (possibly forming a chaing of deserializers delegating functionality
+ * in some cases)
+ * 
+ * @since 2.1
+ */
+public abstract class DelegatingDeserializer
+    extends StdDeserializer<Object>
+    implements ContextualDeserializer, ResolvableDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final JsonDeserializer<?> _delegatee;
+    
+    /*
+    /**********************************************************************
+    /* Construction
+    /**********************************************************************
+     */
+
+    public DelegatingDeserializer(JsonDeserializer<?> delegatee)
+    {
+        super(_figureType(delegatee));
+        _delegatee = delegatee;
+    }
+
+    protected abstract JsonDeserializer<?> newDelegatingInstance(JsonDeserializer<?> newDelegatee);
+    
+    private static Class<?> _figureType(JsonDeserializer<?> deser)
+    {
+        if (deser instanceof StdDeserializer<?>) {
+            return ((StdDeserializer<?>) deser).getValueClass();
+        }
+        return Object.class;
+    }
+    
+    /*
+    /**********************************************************************
+    /* Overridden methods for contextualization, resolving
+    /**********************************************************************
+     */
+
+    @Override
+    public void resolve(DeserializationContext ctxt) throws JsonMappingException {
+        if (_delegatee instanceof ResolvableDeserializer) {
+            ((ResolvableDeserializer) _delegatee).resolve(ctxt);
+        }
+    }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        JsonDeserializer<?> del = _delegatee;
+        if (del instanceof ContextualDeserializer) {
+            del = ((ContextualDeserializer) del).createContextual(ctxt, property);
+        }
+        return _createContextual(ctxt, property, del);
+    }
+
+    protected JsonDeserializer<?> _createContextual(DeserializationContext ctxt,
+            BeanProperty property, JsonDeserializer<?> newDelegatee)
+    {
+        if (newDelegatee == _delegatee) {
+            return this;
+        }
+        return newDelegatingInstance(newDelegatee);
+    }
+
+    /*
+    /**********************************************************************
+    /* Overridden deserialization methods
+    /**********************************************************************
+     */
+
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _delegatee.deserialize(jp,  ctxt);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt,
+            Object intoValue)
+        throws IOException, JsonProcessingException
+    {
+        return ((JsonDeserializer<Object>)_delegatee).deserialize(jp, ctxt, intoValue);
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return _delegatee.deserializeWithType(jp, ctxt, typeDeserializer);
+    }
+
+    /*
+    /**********************************************************************
+    /* Overridden other methods
+    /**********************************************************************
+     */
+
+    @Override
+    public JsonDeserializer<?> replaceDelegatee(JsonDeserializer<?> delegatee)
+    {
+        if (delegatee == _delegatee) {
+            return this;
+        }
+        return newDelegatingInstance(delegatee);
+    }
+
+    @Override
+    public Object getNullValue() { return _delegatee.getNullValue(); }
+
+    @Override
+    public Object getEmptyValue() { return _delegatee.getEmptyValue(); }
+
+    @Override
+    public Collection<Object> getKnownPropertyNames() { return _delegatee.getKnownPropertyNames(); }
+    
+    @Override
+    public boolean isCachable() { return false; }
+
+    @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
new file mode 100644
index 0000000..32692b2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java
@@ -0,0 +1,169 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.EnumResolver;
+
+/**
+ * Deserializer class that can deserialize instances of
+ * specified Enum class from Strings and Integers.
+ */
+public class EnumDeserializer
+    extends StdScalarDeserializer<Enum<?>>
+{
+    private static final long serialVersionUID = -5893263645879532318L;
+
+    protected final EnumResolver<?> _resolver;
+    
+    public EnumDeserializer(EnumResolver<?> res)
+    {
+        super(Enum.class);
+        _resolver = res;
+    }
+
+    /**
+     * Factory method used when Enum instances are to be deserialized
+     * using a creator (static factory method)
+     * 
+     * @return Deserializer based on given factory method, if type was suitable;
+     *  null if type can not be used
+     */
+    public static JsonDeserializer<?> deserializerForCreator(DeserializationConfig config,
+            Class<?> enumClass, AnnotatedMethod factory)
+    {
+        // note: caller has verified there's just one arg; but we must verify its type
+        Class<?> paramClass = factory.getRawParameterType(0);
+        if (paramClass == String.class) {
+            paramClass = null;
+        } else  if (paramClass == Integer.TYPE || paramClass == Integer.class) {
+            paramClass = Integer.class;
+        } else  if (paramClass == Long.TYPE || paramClass == Long.class) {
+            paramClass = Long.class;
+        } else {
+            throw new IllegalArgumentException("Parameter #0 type for factory method ("+factory
+                    +") not suitable, must be java.lang.String or int/Integer/long/Long");
+        }
+        if (config.canOverrideAccessModifiers()) {
+            ClassUtil.checkAndFixAccess(factory.getMember());
+        }
+        return new FactoryBasedDeserializer(enumClass, factory, paramClass);
+    }
+    
+    /*
+    /**********************************************************
+    /* Default JsonDeserializer implementation
+    /**********************************************************
+     */
+
+    /**
+     * Because of costs associated with constructing Enum resolvers,
+     * let's cache instances by default.
+     */
+    @Override
+    public boolean isCachable() { return true; }
+    
+    @Override
+    public Enum<?> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken curr = jp.getCurrentToken();
+        
+        // Usually should just get string value:
+        if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
+            String name = jp.getText();
+            Enum<?> result = _resolver.findEnum(name);
+            if (result == null) {
+                if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                    if (name.length() == 0 || name.trim().length() == 0) {
+                        return null;
+                    }
+                }
+                if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                    throw ctxt.weirdStringException(name, _resolver.getEnumClass(),
+                            "value not one of declared Enum instance names");
+                }
+            }
+            return result;
+        }
+        // But let's consider int acceptable as well (if within ordinal range)
+        if (curr == JsonToken.VALUE_NUMBER_INT) {
+            /* ... unless told not to do that. :-)
+             * (as per [JACKSON-412]
+             */
+            if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
+                throw ctxt.mappingException("Not allowed to deserialize Enum value out of JSON number (disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow)");
+            }
+            
+            int index = jp.getIntValue();
+            Enum<?> result = _resolver.getEnum(index);
+            if (result == null && !ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                throw ctxt.weirdNumberException(Integer.valueOf(index), _resolver.getEnumClass(),
+                        "index value outside legal index range [0.."+_resolver.lastValidIndex()+"]");
+            }
+            return result;
+        }
+        throw ctxt.mappingException(_resolver.getEnumClass());
+    }
+
+    /*
+    /**********************************************************
+    /* Default JsonDeserializer implementation
+    /**********************************************************
+     */
+
+    /**
+     * Deserializer that uses a single-String static factory method
+     * for locating Enum values by String id.
+     */
+    protected static class FactoryBasedDeserializer
+        extends StdScalarDeserializer<Object>
+    {
+        private static final long serialVersionUID = -7775129435872564122L;
+
+        protected final Class<?> _enumClass;
+        // Marker type; null if String expected; otherwise numeric wrapper
+        protected final Class<?> _inputType;
+        protected final Method _factory;
+        
+        public FactoryBasedDeserializer(Class<?> cls, AnnotatedMethod f,
+                Class<?> inputType)
+        {
+            super(Enum.class);
+            _enumClass = cls;
+            _factory = f.getAnnotated();
+            _inputType = inputType;
+        }
+
+        @Override
+        public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // couple of accepted types...
+            Object value;
+            if (_inputType == null) {
+                value = jp.getText();
+            } else  if (_inputType == Integer.class) {
+                value = Integer.valueOf(jp.getValueAsInt());
+            } else  if (_inputType == Long.class) {
+                value = Long.valueOf(jp.getValueAsLong());
+            } else {
+                throw ctxt.mappingException(_enumClass);
+            }
+            try {
+                return _factory.invoke(_enumClass, value);
+            } catch (Exception e) {
+                ClassUtil.unwrapAndThrowAsIAE(e);
+            }
+            return null;
+        }
+    }
+}
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
new file mode 100644
index 0000000..8aa3dee
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumMapDeserializer.java
@@ -0,0 +1,196 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Deserializer for {@link EnumMap} values.
+ * <p>
+ * Note: casting within this class is all messed up -- just could not figure out a way
+ * to properly deal with recursive definition of "EnumMap<K extends Enum<K>, V>
+ * 
+ * @author tsaloranta
+ */
+ at SuppressWarnings({ "unchecked", "rawtypes" }) 
+public class EnumMapDeserializer
+    extends StdDeserializer<EnumMap<?,?>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1518773374647478964L;
+
+    protected final JavaType _mapType;
+    
+    protected final Class<?> _enumClass;
+
+    protected JsonDeserializer<Enum<?>> _keyDeserializer;
+
+    protected JsonDeserializer<Object> _valueDeserializer;
+
+    /**
+     * If value instances have polymorphic type information, this
+     * is the type deserializer that can handle it
+     */
+    protected final TypeDeserializer _valueTypeDeserializer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @deprecated Since 2.1.3 -- use variant that takes one more argument.
+     */
+    @Deprecated
+    public EnumMapDeserializer(JavaType mapType,
+            JsonDeserializer<?> keyDeserializer, JsonDeserializer<?> valueDeser) {
+        this(mapType, keyDeserializer, valueDeser, null);
+    }
+    
+    public EnumMapDeserializer(JavaType mapType,
+            JsonDeserializer<?> keyDeserializer, JsonDeserializer<?> valueDeser,
+            TypeDeserializer valueTypeDeser)
+    {
+        super(EnumMap.class);
+        _mapType = mapType;
+        _enumClass = mapType.getKeyType().getRawClass();
+        _keyDeserializer = (JsonDeserializer<Enum<?>>) keyDeserializer;
+        _valueDeserializer = (JsonDeserializer<Object>) valueDeser;
+        _valueTypeDeserializer = valueTypeDeser;
+    }
+
+    /**
+     * @deprecated Since 2.1.3 -- use variant that takes one more argument.
+     */
+    @Deprecated
+    public EnumMapDeserializer withResolved(JsonDeserializer<?> keyDeserializer,
+            JsonDeserializer<?> valueDeserializer)
+    {
+        return withResolved(keyDeserializer, valueDeserializer, null);
+    } 
+    
+    public EnumMapDeserializer withResolved(JsonDeserializer<?> keyDeserializer,
+            JsonDeserializer<?> valueDeserializer, TypeDeserializer valueTypeDeser)
+    {
+        if ((keyDeserializer == _keyDeserializer)
+                && (valueDeserializer == _valueDeserializer)
+                && (valueTypeDeser == _valueTypeDeserializer)) {
+            return this;
+        }
+        return new EnumMapDeserializer(_mapType,
+                keyDeserializer, valueDeserializer, _valueTypeDeserializer);
+    }
+    
+    /**
+     * Method called to finalize setup of this deserializer,
+     * when it is known for which property deserializer is needed for.
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        // 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)
+        JsonDeserializer<?> kd = _keyDeserializer;
+        if (kd == null) {
+            kd = ctxt.findContextualValueDeserializer(_mapType.getKeyType(), property);
+        }
+        JsonDeserializer<?> vd = _valueDeserializer;
+        if (vd == null) {
+            vd = ctxt.findContextualValueDeserializer(_mapType.getContentType(), property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (vd instanceof ContextualDeserializer) {
+                vd = ((ContextualDeserializer) vd).createContextual(ctxt, property);
+            }
+        }
+        TypeDeserializer vtd = _valueTypeDeserializer;
+        if (vtd != null) {
+            vtd = vtd.forProperty(property);
+        }
+        return withResolved(kd, vd, vtd);
+    }
+    
+    /**
+     * Because of costs associated with constructing Enum resolvers,
+     * let's cache instances by default.
+     */
+    @Override
+    public boolean isCachable() { return true; }
+    
+    /*
+    /**********************************************************
+    /* Actual deserialization
+    /**********************************************************
+     */
+
+    @Override
+    public EnumMap<?,?> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_OBJECT
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw ctxt.mappingException(EnumMap.class);
+        }
+        EnumMap result = constructMap();
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+        while ((jp.nextToken()) != JsonToken.END_OBJECT) {
+            Enum<?> key = _keyDeserializer.deserialize(jp, ctxt);
+            if (key == null) {
+                if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                    String value = null;
+                    try { // bit ugly, but will have to do; works with usual scalars
+                        if (jp.hasCurrentToken()) {
+                            value = jp.getText();
+                        }
+                    } catch (Exception e) { }
+                    throw ctxt.weirdStringException(value, _enumClass, "value not one of declared Enum instance names");
+                }
+                /* 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 then.
+                 */
+                jp.nextToken();
+                jp.skipChildren();
+                continue;
+            }
+            // And then the value...
+            JsonToken t = jp.nextToken();
+            /* note: MUST check for nulls separately: deserializers will
+             * not handle them (and maybe fail or return bogus data)
+             */
+            Object value;
+            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value =  valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            result.put(key, value);
+        }
+        return result;
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+    
+    private EnumMap<?,?> constructMap() {
+        return new EnumMap(_enumClass);
+    }
+}
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
new file mode 100644
index 0000000..556ca79
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/EnumSetDeserializer.java
@@ -0,0 +1,127 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Standard deserializer for {@link EnumSet}s.
+ * <p>
+ * Note: casting within this class is all messed up -- just could not figure out a way
+ * to properly deal with recursive definition of "EnumSet<K extends Enum<K>, V>
+ */
+ at SuppressWarnings("rawtypes")
+public class EnumSetDeserializer
+    extends StdDeserializer<EnumSet<?>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 3479455075597887177L;
+
+    protected final JavaType _enumType;
+
+    protected final Class<Enum> _enumClass;
+
+    protected JsonDeserializer<Enum<?>> _enumDeserializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked" )
+    public EnumSetDeserializer(JavaType enumType, JsonDeserializer<?> deser)
+    {
+        super(EnumSet.class);
+        _enumType = enumType;
+        _enumClass = (Class<Enum>) enumType.getRawClass();
+        _enumDeserializer = (JsonDeserializer<Enum<?>>) deser;
+    }
+
+    public EnumSetDeserializer withDeserializer(JsonDeserializer<?> deser) {
+        if (_enumDeserializer == deser) {
+            return this;
+        }
+        return new EnumSetDeserializer(_enumType, deser);
+    }
+    
+    /**
+     * Because of costs associated with constructing Enum resolvers,
+     * let's cache instances by default.
+     */
+    @Override
+    public boolean isCachable() { return true; }
+    
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<?> deser = _enumDeserializer;
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(_enumType, property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (deser instanceof ContextualDeserializer) {
+                deser = ((ContextualDeserializer) deser).createContextual(ctxt, property);
+            }
+        }
+        return withDeserializer(deser);
+    }
+
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked") 
+    @Override
+    public EnumSet<?> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!jp.isExpectedStartArrayToken()) {
+            throw ctxt.mappingException(EnumSet.class);
+        }
+        EnumSet result = constructSet();
+        JsonToken t;
+
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            /* What to do with nulls? Fail or ignore? Fail, for now
+             * (note: would fail if we passed it to EnumDeserializer, too,
+             * but in general nulls should never be passed to non-container
+             * deserializers)
+             */
+            if (t == JsonToken.VALUE_NULL) {
+                throw ctxt.mappingException(_enumClass);
+            }
+            Enum<?> value = _enumDeserializer.deserialize(jp, ctxt);
+            /* 24-Mar-2012, tatu: As per [JACKSON-810], may actually get nulls;
+             *    but EnumSets don't allow nulls so need to skip.
+             */
+            if (value != null) { 
+                result.add(value);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+    
+    @SuppressWarnings("unchecked") 
+    private EnumSet constructSet()
+    {
+    	// superbly ugly... but apparently necessary
+    	return EnumSet.noneOf(_enumClass);
+    }
+}
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
new file mode 100644
index 0000000..223b242
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/FromStringDeserializer.java
@@ -0,0 +1,75 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+/**
+ * Base class for simple deserializers that only accept JSON String
+ * values as the source.
+ */
+public abstract class FromStringDeserializer<T>
+    extends StdScalarDeserializer<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    protected FromStringDeserializer(Class<?> vc) {
+        super(vc);
+    }
+
+    /*
+    /**********************************************************
+    /* Deserializer implementations
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public final T deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
+        String text = jp.getValueAsString();
+        if (text != null) { // has String representation
+            if (text.length() == 0 || (text = text.trim()).length() == 0) {
+                // 15-Oct-2010, tatu: Empty String usually means null, so
+                return null;
+            }
+            try {
+                T result = _deserialize(text, ctxt);
+                if (result != null) {
+                    return result;
+                }
+            } catch (IllegalArgumentException iae) {
+                // nothing to do here, yet? We'll fail anyway
+            }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid textual representation");
+        }
+        if (jp.getCurrentToken() == JsonToken.VALUE_EMBEDDED_OBJECT) {
+            // Trivial cases; null to null, instance of type itself returned as is
+            Object ob = jp.getEmbeddedObject();
+            if (ob == null) {
+                return null;
+            }
+            if (_valueClass.isAssignableFrom(ob.getClass())) {
+                return (T) ob;
+            }
+            return _deserializeEmbedded(ob, ctxt);
+        }
+        throw ctxt.mappingException(_valueClass);
+    }
+        
+    protected abstract T _deserialize(String value, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    protected T _deserializeEmbedded(Object ob, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // default impl: error out
+        throw ctxt.mappingException("Don't know how to convert embedded Object of type "
+                +ob.getClass().getName()+" into "+_valueClass.getName());
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JacksonDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JacksonDeserializers.java
new file mode 100644
index 0000000..159f07d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JacksonDeserializers.java
@@ -0,0 +1,169 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.CreatorProperty;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Container class for core Jackson type deserializers.
+ */
+ at SuppressWarnings("serial")
+public class JacksonDeserializers
+{
+    /**
+     * @deprecated Since 2.2 -- use {@link #find} instead.
+     */
+    @Deprecated
+    public static StdDeserializer<?>[] all()
+    {
+        // note: JsonLocation supported via ValueInstantiator
+        return  new StdDeserializer[] {
+            JavaTypeDeserializer.instance,
+            TokenBufferDeserializer.instance
+        };
+    }
+
+    public static JsonDeserializer<?> find(Class<?> rawType)
+    {
+        if (rawType == TokenBuffer.class) {
+            return TokenBufferDeserializer.instance;
+        }
+        if (JavaType.class.isAssignableFrom(rawType)) {
+            return JavaTypeDeserializer.instance;
+        }
+        return null;
+    }
+    
+    public static ValueInstantiator findValueInstantiator(DeserializationConfig config,
+            BeanDescription beanDesc)
+    {
+        if (beanDesc.getBeanClass() == JsonLocation.class) {
+            return JsonLocationInstantiator.instance;
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializer implementations
+    /**********************************************************
+     */
+    
+    /**
+     * Deserializer for {@link JavaType} values.
+     */
+    public static class JavaTypeDeserializer
+        extends StdScalarDeserializer<JavaType>
+    {
+        public final static JavaTypeDeserializer instance = new JavaTypeDeserializer();
+        
+        public JavaTypeDeserializer() { super(JavaType.class); }
+        
+        @Override
+        public JavaType deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken curr = jp.getCurrentToken();
+            // Usually should just get string value:
+            if (curr == JsonToken.VALUE_STRING) {
+                String str = jp.getText().trim();
+                if (str.length() == 0) {
+                    return getEmptyValue();
+                }
+                return ctxt.getTypeFactory().constructFromCanonical(str);
+            }
+            // or occasionally just embedded object maybe
+            if (curr == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                return (JavaType) jp.getEmbeddedObject();
+            }
+            throw ctxt.mappingException(_valueClass);
+        }
+    }
+
+    /**
+     * For {@link JsonLocation}, we should be able to just implement
+     * {@link ValueInstantiator} (not that explicit one would be very
+     * hard but...)
+     */
+    public static class JsonLocationInstantiator extends ValueInstantiator
+    {
+        public final static JsonLocationInstantiator instance = new JsonLocationInstantiator();
+        
+        @Override
+        public String getValueTypeDesc() {
+            return JsonLocation.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateFromObjectWith() { return true; }
+        
+        @Override
+        public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) {
+            JavaType intType = config.constructType(Integer.TYPE);
+            JavaType longType = config.constructType(Long.TYPE);
+            return  new CreatorProperty[] {
+                    creatorProp("sourceRef", config.constructType(Object.class), 0),
+                    creatorProp("byteOffset", longType, 1),
+                    creatorProp("charOffset", longType, 2),
+                    creatorProp("lineNr", intType, 3),
+                    creatorProp("columnNr", intType, 4)
+            };
+        }
+
+        private static CreatorProperty creatorProp(String name, JavaType type, int index) {
+            return new CreatorProperty(name, type, null,
+                    null, null, null, index, null, true);
+        }
+        
+        @Override
+        public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) {
+            return new JsonLocation(args[0], _long(args[1]), _long(args[2]),
+                    _int(args[3]), _int(args[4]));
+        }
+
+        private final static long _long(Object o) {
+            return (o == null) ? 0L : ((Number) o).longValue();
+        }
+        private final static int _int(Object o) {
+            return (o == null) ? 0 : ((Number) o).intValue();
+        }
+    }
+    
+    /**
+     * We also want to directly support deserialization of {@link TokenBuffer}.
+     *<p>
+     * Note that we use scalar deserializer base just because we claim
+     * to be of scalar for type information inclusion purposes; actual
+     * underlying content can be of any (Object, Array, scalar) type.
+     */
+    @JacksonStdImpl
+    public static class TokenBufferDeserializer
+        extends StdScalarDeserializer<TokenBuffer>
+    {
+        public final static TokenBufferDeserializer instance = new TokenBufferDeserializer();
+        
+        public TokenBufferDeserializer() { super(TokenBuffer.class); }
+
+        @Override
+        public TokenBuffer deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            TokenBuffer tb = new TokenBuffer(jp.getCodec());
+            // quite simple, given that TokenBuffer is a JsonGenerator:
+            tb.copyCurrentStructure(jp);
+            return tb;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java
new file mode 100644
index 0000000..6a57a50
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JdkDeserializers.java
@@ -0,0 +1,422 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.*;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+
+/**
+ * Container class that contains serializers for JDK types that
+ * require special handling for some reason.
+ */
+ at SuppressWarnings("serial")
+public class JdkDeserializers
+{
+    private final static HashSet<String> _classNames = new HashSet<String>();
+    static {
+        // note: can skip primitive types; other ways to check them:
+        Class<?>[] numberTypes = new Class<?>[] {
+                UUID.class,
+                URL.class,
+                URI.class,
+                File.class,
+                Currency.class,
+                Pattern.class,
+                Locale.class,
+                InetAddress.class,
+                Charset.class,
+                AtomicBoolean.class,
+                Class.class,
+                StackTraceElement.class
+
+        };
+        for (Class<?> cls : numberTypes) {
+            _classNames.add(cls.getName());
+        }
+    }
+
+    /**
+     * @deprecated Since 2.2 -- use {@link #find} instead.
+     */
+    @Deprecated
+    public static StdDeserializer<?>[] all()
+    {
+        return new StdDeserializer[] {
+            // from String types:
+            UUIDDeserializer.instance,
+            URLDeserializer.instance,
+            URIDeserializer.instance,
+            FileDeserializer.instance,
+            CurrencyDeserializer.instance,
+            PatternDeserializer.instance,
+            LocaleDeserializer.instance,
+            InetAddressDeserializer.instance,
+            CharsetDeserializer.instance,
+            
+            // other types:
+
+            // (note: AtomicInteger/Long work due to single-arg constructor;
+            AtomicBooleanDeserializer.instance,
+            ClassDeserializer.instance,
+            StackTraceElementDeserializer.instance
+        };
+    }
+
+    public static JsonDeserializer<?> find(Class<?> rawType, String clsName)
+    {
+        if (!_classNames.contains(clsName)) {
+            return null;
+        }
+        /* Ok: following ones would work via String-arg detection too;
+         * if we get more may want to formally change.
+         */
+        if (rawType == URI.class) {
+            return URIDeserializer.instance;
+        }
+        if (rawType == URL.class) {
+            return URLDeserializer.instance;
+        }
+        if (rawType == File.class) {
+            return FileDeserializer.instance;
+        }
+        /* But these will require custom handling regardless:
+         */
+        if (rawType == UUID.class) {
+            return UUIDDeserializer.instance;
+        }
+        if (rawType == Currency.class) {
+            return CurrencyDeserializer.instance;
+        }
+        if (rawType == Pattern.class) {
+            return PatternDeserializer.instance;
+        }
+        if (rawType == Locale.class) {
+            return LocaleDeserializer.instance;
+        }
+        if (rawType == InetAddress.class) {
+            return InetAddressDeserializer.instance;
+        }
+        if (rawType == Charset.class) {
+            return CharsetDeserializer.instance;
+        }
+        if (rawType == Class.class) {
+            return ClassDeserializer.instance;
+        }
+        if (rawType == StackTraceElement.class) {
+            return StackTraceElementDeserializer.instance;
+        }
+        if (rawType == AtomicBoolean.class) {
+            // (note: AtomicInteger/Long work due to single-arg constructor. For now?
+            return AtomicBooleanDeserializer.instance;
+        }
+        // should never occur
+        throw new IllegalArgumentException("Internal error: can't find deserializer for "+clsName);
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializer implementations: from-String deserializers
+    /**********************************************************
+     */
+    
+    public static class UUIDDeserializer
+        extends FromStringDeserializer<UUID>
+    {
+        public final static UUIDDeserializer instance = new UUIDDeserializer();
+        
+        public UUIDDeserializer() { super(UUID.class); }
+
+        @Override
+        protected UUID _deserialize(String value, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return UUID.fromString(value);
+        }
+    
+        @Override
+        protected UUID _deserializeEmbedded(Object ob, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (ob instanceof byte[]) {
+                byte[] bytes = (byte[]) ob;
+                if (bytes.length != 16) {
+                    ctxt.mappingException("Can only construct UUIDs from 16 byte arrays; got "+bytes.length+" bytes");
+                }
+                // clumsy, but should work for now...
+                DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
+                long l1 = in.readLong();
+                long l2 = in.readLong();
+                return new UUID(l1, l2);
+            }
+            super._deserializeEmbedded(ob, ctxt);
+            return null; // never gets here
+        }
+    }
+
+    public static class URLDeserializer
+        extends FromStringDeserializer<URL>
+    {
+        public final static URLDeserializer instance = new URLDeserializer();
+
+        public URLDeserializer() { super(URL.class); }
+        
+        @Override
+        protected URL _deserialize(String value, DeserializationContext ctxt)
+            throws IOException
+        {
+            return new URL(value);
+        }
+    }
+    
+    public static class URIDeserializer
+        extends FromStringDeserializer<URI>
+    {
+        public final static URIDeserializer instance = new URIDeserializer();
+
+        public URIDeserializer() { super(URI.class); }
+    
+        @Override
+        protected URI _deserialize(String value, DeserializationContext ctxt)
+            throws IllegalArgumentException
+        {
+            return URI.create(value);
+        }
+    }
+    
+    public static class CurrencyDeserializer
+        extends FromStringDeserializer<Currency>
+    {
+        public final static CurrencyDeserializer instance = new CurrencyDeserializer();
+
+        public CurrencyDeserializer() { super(Currency.class); }
+        
+        @Override
+        protected Currency _deserialize(String value, DeserializationContext ctxt)
+            throws IllegalArgumentException
+        {
+            // will throw IAE if unknown:
+            return Currency.getInstance(value);
+        }
+    }
+    
+    public static class PatternDeserializer
+        extends FromStringDeserializer<Pattern>
+    {
+        public final static PatternDeserializer instance = new PatternDeserializer();
+
+        public PatternDeserializer() { super(Pattern.class); }
+        
+        @Override
+        protected Pattern _deserialize(String value, DeserializationContext ctxt)
+            throws IllegalArgumentException
+        {
+            // will throw IAE (or its subclass) if malformed
+            return Pattern.compile(value);
+        }
+    }
+    
+    /**
+     * Kept protected as it's not meant to be extensible at this point
+     */
+    protected static class LocaleDeserializer
+        extends FromStringDeserializer<Locale>
+    {
+        public final static LocaleDeserializer instance = new LocaleDeserializer();
+
+        public LocaleDeserializer() { super(Locale.class); }
+        
+        @Override
+        protected Locale _deserialize(String value, DeserializationContext ctxt)
+            throws IOException
+        {
+            int ix = value.indexOf('_');
+            if (ix < 0) { // single argument
+                return new Locale(value);
+            }
+            String first = value.substring(0, ix);
+            value = value.substring(ix+1);
+            ix = value.indexOf('_');
+            if (ix < 0) { // two pieces
+                return new Locale(first, value);
+            }
+            String second = value.substring(0, ix);
+            return new Locale(first, second, value.substring(ix+1));
+        }
+    }
+    
+    /**
+     * As per [JACKSON-484], also need special handling for InetAddress...
+     */
+    protected static class InetAddressDeserializer
+        extends FromStringDeserializer<InetAddress>
+    {
+        public final static InetAddressDeserializer instance = new InetAddressDeserializer();
+
+        public InetAddressDeserializer() { super(InetAddress.class); }
+    
+        @Override
+        protected InetAddress _deserialize(String value, DeserializationContext ctxt)
+            throws IOException
+        {
+            return InetAddress.getByName(value);
+        }
+    }
+
+    // [JACKSON-789]
+    protected static class CharsetDeserializer
+        extends FromStringDeserializer<Charset>
+    {
+        public final static CharsetDeserializer instance = new CharsetDeserializer();
+
+        public CharsetDeserializer() { super(Charset.class); }
+    
+        @Override
+        protected Charset _deserialize(String value, DeserializationContext ctxt)
+            throws IOException
+        {
+            return Charset.forName(value);
+        }
+    }
+
+    public static class FileDeserializer
+        extends FromStringDeserializer<File>
+    {
+        public final static FileDeserializer instance = new FileDeserializer();
+
+        public FileDeserializer() { super(File.class); }
+        
+        @Override
+        protected File _deserialize(String value, DeserializationContext ctxt)
+        {
+            return new File(value);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* AtomicXxx types
+    /**********************************************************
+     */
+    
+    public static class AtomicReferenceDeserializer
+        extends StdScalarDeserializer<AtomicReference<?>>
+        implements ContextualDeserializer
+    {
+        /**
+         * Type of value that we reference
+         */
+        protected final JavaType _referencedType;
+        
+        protected final JsonDeserializer<?> _valueDeserializer;
+        
+        /**
+         * @param referencedType Parameterization of this reference
+         */
+        public AtomicReferenceDeserializer(JavaType referencedType) {
+            this(referencedType, null);
+        }
+        
+        public AtomicReferenceDeserializer(JavaType referencedType,
+                JsonDeserializer<?> deser)
+        {
+            super(AtomicReference.class);
+            _referencedType = referencedType;
+            _valueDeserializer = deser;
+        }
+        
+        @Override
+        public AtomicReference<?> deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return new AtomicReference<Object>(_valueDeserializer.deserialize(jp, ctxt));
+        }
+        
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property) throws JsonMappingException
+        {
+            JsonDeserializer<?> deser = _valueDeserializer;
+            if (deser != null) {
+                return this;
+            }
+            return new AtomicReferenceDeserializer(_referencedType,
+                    ctxt.findContextualValueDeserializer(_referencedType, property));
+        }
+    }
+
+    public static class AtomicBooleanDeserializer
+        extends StdScalarDeserializer<AtomicBoolean>
+    {
+        public final static AtomicBooleanDeserializer instance = new AtomicBooleanDeserializer();
+
+        public AtomicBooleanDeserializer() { super(AtomicBoolean.class); }
+        
+        @Override
+        public AtomicBoolean deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // 16-Dec-2010, tatu: Should we actually convert null to null AtomicBoolean?
+            return new AtomicBoolean(_parseBooleanPrimitive(jp, ctxt));
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializers for other JDK types
+    /**********************************************************
+     */
+
+    public static class StackTraceElementDeserializer
+        extends StdScalarDeserializer<StackTraceElement>
+    {
+        public final static StackTraceElementDeserializer instance = new StackTraceElementDeserializer();
+
+        public StackTraceElementDeserializer() { super(StackTraceElement.class); }
+    
+        @Override
+        public StackTraceElement deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            // Must get an Object
+            if (t == JsonToken.START_OBJECT) {
+                String className = "", methodName = "", fileName = "";
+                int lineNumber = -1;
+    
+                while ((t = jp.nextValue()) != JsonToken.END_OBJECT) {
+                    String propName = jp.getCurrentName();
+                    if ("className".equals(propName)) {
+                        className = jp.getText();
+                    } else if ("fileName".equals(propName)) {
+                        fileName = jp.getText();
+                    } else if ("lineNumber".equals(propName)) {
+                        if (t.isNumeric()) {
+                            lineNumber = jp.getIntValue();
+                        } else {
+                            throw JsonMappingException.from(jp, "Non-numeric token ("+t+") for property 'lineNumber'");
+                        }
+                    } else if ("methodName".equals(propName)) {
+                        methodName = jp.getText();
+                    } else if ("nativeMethod".equals(propName)) {
+                        // no setter, not passed via constructor: ignore
+                    } else {
+                        handleUnknownProperty(jp, ctxt, _valueClass, propName);
+                    }
+                }
+                return new StackTraceElement(className, methodName, fileName, lineNumber);
+            }
+            throw ctxt.mappingException(_valueClass, t);
+        }
+    }
+}
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
new file mode 100644
index 0000000..5c4496c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/JsonNodeDeserializer.java
@@ -0,0 +1,325 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.node.*;
+
+/**
+ * Deserializer that can build instances of {@link JsonNode} from any
+ * JSON content, using appropriate {@link JsonNode} type.
+ */
+ at SuppressWarnings("serial")
+public class JsonNodeDeserializer
+    extends BaseNodeDeserializer
+{
+    /**
+     * Singleton instance of generic deserializer for {@link JsonNode}.
+     * Only used for types other than JSON Object and Array.
+     */
+    private final static JsonNodeDeserializer instance = new JsonNodeDeserializer();
+
+    protected JsonNodeDeserializer() { }
+
+    /**
+     * Factory method for accessing deserializer for specific node type
+     */
+    public static JsonDeserializer<? extends JsonNode> getDeserializer(Class<?> nodeClass)
+    {
+        if (nodeClass == ObjectNode.class) {
+            return ObjectDeserializer.getInstance();
+        }
+        if (nodeClass == ArrayNode.class) {
+            return ArrayDeserializer.getInstance();
+        }
+        // For others, generic one works fine
+        return instance;
+    }
+    
+    /*
+    /**********************************************************
+    /* Actual deserializer implementations
+    /**********************************************************
+     */
+
+    /**
+     * Implementation that will produce types of any JSON nodes; not just one
+     * deserializer is registered to handle (in case of more specialized handler).
+     * Overridden by typed sub-classes for more thorough checking
+     */
+    @Override
+    public JsonNode deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_OBJECT:
+            return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
+        case START_ARRAY:
+            return deserializeArray(jp, ctxt, ctxt.getNodeFactory());
+        default:
+            return deserializeAny(jp, ctxt, ctxt.getNodeFactory());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Specific instances for more accurate types
+    /**********************************************************
+     */
+
+    final static class ObjectDeserializer
+        extends BaseNodeDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final static ObjectDeserializer _instance = new ObjectDeserializer();
+
+        protected ObjectDeserializer() { }
+
+        public static ObjectDeserializer getInstance() { return _instance; }
+        
+        @Override
+        public ObjectNode deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (jp.getCurrentToken() == JsonToken.START_OBJECT) {
+                jp.nextToken();
+                return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
+            }
+            if (jp.getCurrentToken() == JsonToken.FIELD_NAME) {
+                return deserializeObject(jp, ctxt, ctxt.getNodeFactory());
+            }
+            throw ctxt.mappingException(ObjectNode.class);
+         }
+    }
+        
+    final static class ArrayDeserializer
+        extends BaseNodeDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final static ArrayDeserializer _instance = new ArrayDeserializer();
+
+        protected ArrayDeserializer() { }
+
+        public static ArrayDeserializer getInstance() { return _instance; }
+        
+        @Override
+        public ArrayNode deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (jp.isExpectedStartArrayToken()) {
+                return deserializeArray(jp, ctxt, ctxt.getNodeFactory());
+            }
+            throw ctxt.mappingException(ArrayNode.class);
+        }
+    }
+}
+
+/**
+ * Base class for all actual {@link JsonNode} deserializer
+ * implementations
+ */
+ at SuppressWarnings("serial")
+abstract class BaseNodeDeserializer
+    extends StdDeserializer<JsonNode>
+{
+    public BaseNodeDeserializer()
+    {
+        super(JsonNode.class);
+    }
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        /* Output can be as JSON Object, Array or scalar: no way to know
+         * a priori. So:
+         */
+        return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+    }
+
+    @Override
+    public JsonNode getNullValue() {
+        return NullNode.getInstance();
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable methods
+    /**********************************************************
+     */
+    
+    protected void _reportProblem(JsonParser jp, String msg)
+        throws JsonMappingException
+    {
+        throw new JsonMappingException(msg, jp.getTokenLocation());
+    }
+    
+    /**
+     * Method called when there is a duplicate value for a field.
+     * By default we don't care, and the last value is used.
+     * Can be overridden to provide alternate handling, such as throwing
+     * an exception, or choosing different strategy for combining values
+     * or choosing which one to keep.
+     *
+     * @param fieldName Name of the field for which duplicate value was found
+     * @param objectNode Object node that contains values
+     * @param oldValue Value that existed for the object node before newValue
+     *   was added
+     * @param newValue Newly added value just added to the object node
+     */
+    protected void _handleDuplicateField(String fieldName, ObjectNode objectNode,
+                                         JsonNode oldValue, JsonNode newValue)
+        throws JsonProcessingException
+    {
+        // By default, we don't do anything
+        ;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected final ObjectNode deserializeObject(JsonParser jp, DeserializationContext ctxt,
+            final JsonNodeFactory nodeFactory)            
+        throws IOException, JsonProcessingException
+    {
+        ObjectNode node = nodeFactory.objectNode();
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String fieldName = jp.getCurrentName();
+            JsonNode value;
+            switch (jp.nextToken()) {
+            case START_OBJECT:
+                value = deserializeObject(jp, ctxt, nodeFactory);
+                break;
+            case START_ARRAY:
+                value = deserializeArray(jp, ctxt, nodeFactory);
+                break;
+            case VALUE_STRING:
+                value = nodeFactory.textNode(jp.getText());
+                break;
+            default:
+                value = deserializeAny(jp, ctxt, nodeFactory);
+            }
+            JsonNode old = node.replace(fieldName, value);
+            if (old != null) {
+                _handleDuplicateField(fieldName, node, old, value);
+            }
+        }
+        return node;
+    }
+    
+    protected final ArrayNode deserializeArray(JsonParser jp, DeserializationContext ctxt,
+            final JsonNodeFactory nodeFactory)            
+        throws IOException, JsonProcessingException
+    {
+        ArrayNode node = nodeFactory.arrayNode();
+        while (true) {
+            JsonToken t = jp.nextToken();
+            if (t == null) {
+                throw ctxt.mappingException("Unexpected end-of-input when binding data into ArrayNode");
+            }
+            switch (t) {
+            case START_OBJECT:
+                node.add(deserializeObject(jp, ctxt, nodeFactory));
+                break;
+            case START_ARRAY:
+                node.add(deserializeArray(jp, ctxt, nodeFactory));
+                break;
+            case END_ARRAY:
+                return node;
+            case VALUE_STRING:
+                node.add(nodeFactory.textNode(jp.getText()));
+                break;
+            default:
+                node.add(deserializeAny(jp, ctxt, nodeFactory));
+                break;
+            }
+        }
+    }
+
+    protected final JsonNode deserializeAny(JsonParser jp, DeserializationContext ctxt,
+            final JsonNodeFactory nodeFactory)            
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_OBJECT:
+            return deserializeObject(jp, ctxt, nodeFactory);
+
+        case START_ARRAY:
+            return deserializeArray(jp, ctxt, nodeFactory);
+
+        case FIELD_NAME:
+            return deserializeObject(jp, ctxt, nodeFactory);
+
+        case VALUE_EMBEDDED_OBJECT:
+            // [JACKSON-796]
+            {
+                Object ob = jp.getEmbeddedObject();
+                if (ob == null) { // should this occur?
+                    return nodeFactory.nullNode();
+                }
+                Class<?> type = ob.getClass();
+                if (type == byte[].class) { // most common special case
+                    return nodeFactory.binaryNode((byte[]) ob);
+                }
+                // any other special handling needed?
+                return nodeFactory.pojoNode(ob);
+            }
+
+        case VALUE_STRING:
+            return nodeFactory.textNode(jp.getText());
+
+        case VALUE_NUMBER_INT:
+            {
+                JsonParser.NumberType nt = jp.getNumberType();
+                if (nt == JsonParser.NumberType.BIG_INTEGER
+                    || ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
+                    return nodeFactory.numberNode(jp.getBigIntegerValue());
+                }
+                if (nt == JsonParser.NumberType.INT) {
+                    return nodeFactory.numberNode(jp.getIntValue());
+                }
+                return nodeFactory.numberNode(jp.getLongValue());
+            }
+
+        case VALUE_NUMBER_FLOAT:
+            {
+                JsonParser.NumberType nt = jp.getNumberType();
+                if (nt == JsonParser.NumberType.BIG_DECIMAL
+                    || ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                    return nodeFactory.numberNode(jp.getDecimalValue());
+                }
+                return nodeFactory.numberNode(jp.getDoubleValue());
+            }
+
+        case VALUE_TRUE:
+            return nodeFactory.booleanNode(true);
+
+        case VALUE_FALSE:
+            return nodeFactory.booleanNode(false);
+
+        case VALUE_NULL:
+            return nodeFactory.nullNode();
+            
+            // These states can not be mapped; input stream is
+            // off by an event or two
+
+        //case END_OBJECT:
+        //case END_ARRAY:
+        default:
+            throw ctxt.mappingException(getValueClass());
+        }
+    }
+}
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
new file mode 100644
index 0000000..a1862ba
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/MapDeserializer.java
@@ -0,0 +1,521 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator;
+import com.fasterxml.jackson.databind.deser.impl.PropertyValueBuffer;
+import com.fasterxml.jackson.databind.deser.std.ContainerDeserializerBase;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+
+/**
+ * Basic serializer that can take Json "Object" structure and
+ * construct a {@link java.util.Map} instance, with typed contents.
+ *<p>
+ * Note: for untyped content (one indicated by passing Object.class
+ * as the type), {@link UntypedObjectDeserializer} is used instead.
+ * It can also construct {@link java.util.Map}s, but not with specific
+ * POJO types, only other containers and primitives/wrappers.
+ */
+ at JacksonStdImpl
+public class MapDeserializer
+    extends ContainerDeserializerBase<Map<Object,Object>>
+    implements ContextualDeserializer, ResolvableDeserializer
+{
+    private static final long serialVersionUID = -3378654289961736240L;
+
+    // // Configuration: typing, deserializers
+
+    protected final JavaType _mapType;
+
+    /**
+     * Key deserializer to use; either passed via constructor
+     * (when indicated by annotations), or resolved when
+     * {@link #resolve} is called;
+     */
+    protected final KeyDeserializer _keyDeserializer;
+
+    /**
+     * Flag set to indicate that the key type is
+     * {@link java.lang.String} (or {@link java.lang.Object}, for
+     * which String is acceptable), <b>and</b> that the
+     * default Jackson key deserializer would be used.
+     * If both are true, can optimize handling.
+     */
+    protected boolean _standardStringKey;
+
+    /**
+     * Value deserializer.
+     */
+    protected final JsonDeserializer<Object> _valueDeserializer;
+
+    /**
+     * If value instances have polymorphic type information, this
+     * 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.
+     */
+    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;    
+
+    // // Any properties to ignore if seen?
+    
+    protected HashSet<String> _ignorableProperties;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public MapDeserializer(JavaType mapType, ValueInstantiator valueInstantiator,
+            KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
+            TypeDeserializer valueTypeDeser)
+    {
+        super(Map.class);
+        _mapType = mapType;
+        _keyDeserializer = keyDeser;
+        _valueDeserializer = valueDeser;
+        _valueTypeDeserializer = valueTypeDeser;
+        _valueInstantiator = valueInstantiator;
+        _hasDefaultCreator = valueInstantiator.canCreateUsingDefault();
+        _delegateDeserializer = null;
+        _propertyBasedCreator = null;
+        _standardStringKey = _isStdKeyDeser(mapType, keyDeser);
+    }
+
+    /**
+     * Copy-constructor that can be used by sub-classes to allow
+     * copy-on-write styling copying of settings of an existing instance.
+     */
+    protected MapDeserializer(MapDeserializer src)
+    {
+        super(src._valueClass);
+        _mapType = src._mapType;
+        _keyDeserializer = src._keyDeserializer;
+        _valueDeserializer = src._valueDeserializer;
+        _valueTypeDeserializer = src._valueTypeDeserializer;
+        _valueInstantiator = src._valueInstantiator;
+        _propertyBasedCreator = src._propertyBasedCreator;
+        _delegateDeserializer = src._delegateDeserializer;
+        _hasDefaultCreator = src._hasDefaultCreator;
+        // should we make a copy here?
+        _ignorableProperties = src._ignorableProperties;
+
+        _standardStringKey = src._standardStringKey;
+    }
+
+    protected MapDeserializer(MapDeserializer src,
+            KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
+            TypeDeserializer valueTypeDeser,
+            HashSet<String> ignorable)
+    {
+        super(src._valueClass);
+        _mapType = src._mapType;
+        _keyDeserializer = keyDeser;
+        _valueDeserializer = valueDeser;
+        _valueTypeDeserializer = valueTypeDeser;
+        _valueInstantiator = src._valueInstantiator;
+        _propertyBasedCreator = src._propertyBasedCreator;
+        _delegateDeserializer = src._delegateDeserializer;
+        _hasDefaultCreator = src._hasDefaultCreator;
+        _ignorableProperties = ignorable;
+
+        _standardStringKey = _isStdKeyDeser(_mapType, keyDeser);
+    }
+
+    /**
+     * Fluent factory method used to create a copy with slightly
+     * different settings. When sub-classing, MUST be overridden.
+     */
+    @SuppressWarnings("unchecked")
+    protected MapDeserializer withResolved(KeyDeserializer keyDeser,
+            TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser,
+            HashSet<String> ignorable)
+    {
+        
+        if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser) && (_valueTypeDeserializer == valueTypeDeser)
+                && (_ignorableProperties == ignorable)) {
+            return this;
+        }
+        return new MapDeserializer(this,
+                keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser, ignorable);
+    }
+
+    /**
+     * Helper method used to check whether we can just use the default key
+     * deserialization, where JSON String becomes Java String.
+     */
+    protected final boolean _isStdKeyDeser(JavaType mapType, KeyDeserializer keyDeser)
+    {
+        if (keyDeser == null) {
+            return true;
+        }
+        JavaType keyType = mapType.getKeyType();
+        if (keyType == null) { // assumed to be Object
+            return true;
+        }
+        Class<?> rawKeyType = keyType.getRawClass();
+        return ((rawKeyType == String.class || rawKeyType == Object.class)
+                && isDefaultKeyDeserializer(keyDeser));
+    }
+    
+    public void setIgnorableProperties(String[] ignorable)
+    {
+        _ignorableProperties = (ignorable == null || ignorable.length == 0) ?
+            null : ArrayBuilders.arrayToSet(ignorable);
+    }
+
+    /*
+    /**********************************************************
+    /* 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.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);
+        }
+        if (_valueInstantiator.canCreateFromObjectWith()) {
+            SettableBeanProperty[] creatorProps = _valueInstantiator.getFromObjectArguments(ctxt.getConfig());
+            _propertyBasedCreator = PropertyBasedCreator.construct(ctxt, _valueInstantiator, creatorProps);
+        }
+        _standardStringKey = _isStdKeyDeser(_mapType, _keyDeserializer);
+    }
+
+    /**
+     * Method called to finalize setup of this deserializer,
+     * when it is known for which property deserializer is needed for.
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        KeyDeserializer kd = _keyDeserializer;
+        if (kd == null) {
+            kd = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
+        } else {
+            if (kd instanceof ContextualKeyDeserializer) {
+                kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property);
+            }
+        }
+        JsonDeserializer<?> vd = _valueDeserializer;
+        // #125: May have a content converter
+        vd = findConvertingContentDeserializer(ctxt, property, vd);
+        if (vd == null) {
+            vd = ctxt.findContextualValueDeserializer(_mapType.getContentType(), property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (vd instanceof ContextualDeserializer) {
+                vd = ((ContextualDeserializer) vd).createContextual(ctxt, property);
+            }
+        }
+        TypeDeserializer vtd = _valueTypeDeserializer;
+        if (vtd != null) {
+            vtd = vtd.forProperty(property);
+        }
+        HashSet<String> ignored = _ignorableProperties;
+        AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        if (intr != null && property != null) {
+            String[] moreToIgnore = intr.findPropertiesToIgnore(property.getMember());
+            if (moreToIgnore != null) {
+                ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
+                for (String str : moreToIgnore) {
+                    ignored.add(str);
+                }
+            }
+        }
+        return withResolved(kd, vtd, vd, ignored);
+    }
+    
+    /*
+    /**********************************************************
+    /* ContainerDeserializerBase API
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return _mapType.getContentType();
+    }
+
+    @Override
+    public JsonDeserializer<Object> getContentDeserializer() {
+        return _valueDeserializer;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<Object,Object> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_propertyBasedCreator != null) {
+            return _deserializeUsingCreator(jp, ctxt);
+        }
+        if (_delegateDeserializer != null) {
+            return (Map<Object,Object>) _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (!_hasDefaultCreator) {
+            throw ctxt.instantiationException(getMapClass(), "No default constructor found");
+        }
+        // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
+        JsonToken t = jp.getCurrentToken();
+        if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
+            // [JACKSON-620] (empty) String may be ok however:
+            if (t == JsonToken.VALUE_STRING) {
+                return (Map<Object,Object>) _valueInstantiator.createFromString(ctxt, jp.getText());
+            }
+            throw ctxt.mappingException(getMapClass());
+        }
+        final Map<Object,Object> result = (Map<Object,Object>) _valueInstantiator.createUsingDefault(ctxt);
+        if (_standardStringKey) {
+            _readAndBindStringMap(jp, ctxt, result);
+            return result;
+        }
+        _readAndBind(jp, ctxt, result);
+        return result;
+    }
+
+    @Override
+    public Map<Object,Object> deserialize(JsonParser jp, DeserializationContext ctxt,
+            Map<Object,Object> result)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_OBJECT or FIELD_NAME
+        JsonToken t = jp.getCurrentToken();
+        if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME) {
+            throw ctxt.mappingException(getMapClass());
+        }
+        if (_standardStringKey) {
+            _readAndBindStringMap(jp, ctxt, result);
+            return result;
+        }
+        _readAndBind(jp, ctxt, result);
+        return result;
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Other public accessors
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public final Class<?> getMapClass() { return (Class<Map<Object,Object>>) _mapType.getRawClass(); }
+
+    @Override public JavaType getValueType() { return _mapType; }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected final void _readAndBind(JsonParser jp, DeserializationContext ctxt,
+            Map<Object,Object> result)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        final KeyDeserializer keyDes = _keyDeserializer;
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            // Must point to field name
+            String fieldName = jp.getCurrentName();
+            Object key = keyDes.deserializeKey(fieldName, ctxt);
+            // And then the value...
+            t = jp.nextToken();
+            if (_ignorableProperties != null && _ignorableProperties.contains(fieldName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // Note: must handle null explicitly here; value deserializers won't
+            Object value;            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            /* !!! 23-Dec-2008, tatu: should there be an option to verify
+             *   that there are no duplicate field names? (and/or what
+             *   to do, keep-first or keep-last)
+             */
+            result.put(key, value);
+        }
+    }
+
+    /**
+     * Optimized method used when keys can be deserialized as plain old
+     * {@link java.lang.String}s, and there is no custom deserialized
+     * specified.
+     */
+    protected final void _readAndBindStringMap(JsonParser jp, DeserializationContext ctxt,
+            Map<Object,Object> result)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            // Must point to field name
+            String fieldName = jp.getCurrentName();
+            // And then the value...
+            t = jp.nextToken();
+            if (_ignorableProperties != null && _ignorableProperties.contains(fieldName)) {
+                jp.skipChildren();
+                continue;
+            }
+            // Note: must handle null explicitly here; value deserializers won't
+            Object value;            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            result.put(fieldName, value);
+        }
+    }
+    
+    @SuppressWarnings("unchecked") 
+    public Map<Object,Object> _deserializeUsingCreator(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        final PropertyBasedCreator creator = _propertyBasedCreator;
+        // null -> no ObjectIdReader for Maps (yet?)
+        PropertyValueBuffer buffer = creator.startBuilding(jp, ctxt, null);
+
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        final JsonDeserializer<Object> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _valueTypeDeserializer;
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            t = jp.nextToken(); // to get to value
+            if (_ignorableProperties != null && _ignorableProperties.contains(propName)) {
+                jp.skipChildren(); // and skip it (in case of array/object)
+                continue;
+            }
+            // creator property?
+            SettableBeanProperty prop = creator.findCreatorProperty(propName);
+            if (prop != null) {
+                // Last property to set?
+                Object value = prop.deserialize(jp, ctxt);
+                if (buffer.assignParameter(prop.getCreatorIndex(), value)) {
+                    jp.nextToken();
+                    Map<Object,Object> result;
+                    try {
+                        result = (Map<Object,Object>)creator.build(ctxt, buffer);
+                    } catch (Exception e) {
+                        wrapAndThrow(e, _mapType.getRawClass());
+                        return null;
+                    }
+                    _readAndBind(jp, ctxt, result);
+                    return result;
+                }
+                continue;
+            }
+            // other property? needs buffering
+            String fieldName = jp.getCurrentName();
+            Object key = _keyDeserializer.deserializeKey(fieldName, ctxt);
+            Object value;            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = valueDes.deserialize(jp, ctxt);
+            } else {
+                value = valueDes.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            buffer.bufferMapProperty(key, value);
+        }
+        // end of JSON object?
+        // if so, can just construct and leave...
+        try {
+            return (Map<Object,Object>)creator.build(ctxt, buffer);
+        } catch (Exception e) {
+            wrapAndThrow(e, _mapType.getRawClass());
+            return null;
+        }
+    }
+
+    // note: copied form BeanDeserializer; should try to share somehow...
+    protected void wrapAndThrow(Throwable t, Object ref)
+        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;
+        }
+        // ... except for mapping exceptions
+        if (t instanceof IOException && !(t instanceof JsonMappingException)) {
+            throw (IOException) t;
+        }
+        throw JsonMappingException.wrapWithPath(t, ref, null);
+    }
+}
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
new file mode 100644
index 0000000..196a1c5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NullifyingDeserializer.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Bogus deserializer that will simply skip all content there is to map
+ * and returns Java null reference.
+ * 
+ * @since 2.2
+ */
+public class NullifyingDeserializer
+    extends StdDeserializer<Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    public final static NullifyingDeserializer instance = new NullifyingDeserializer();
+    
+    public NullifyingDeserializer() { super(Object.class); }
+
+    /*
+    /**********************************************************
+    /* Deserializer API
+    /**********************************************************
+     */
+    
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        jp.skipChildren();
+        return null;
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // Not sure if we need to bother but:
+
+        JsonToken t = jp.getCurrentToken();
+        switch (t) {
+        case START_ARRAY:
+        case START_OBJECT:
+        case FIELD_NAME:
+            return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+        default:
+            return null;
+        }
+    }
+}
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
new file mode 100644
index 0000000..20a5399
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/NumberDeserializers.java
@@ -0,0 +1,594 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashSet;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Container class for deserializers that handle core JDK primitive
+ * (and matching wrapper) types, as well as standard "big" numeric types.
+ * Note that this includes types such as {@link java.lang.Boolean}
+ * and {@link java.lang.Character} which are not strictly numeric,
+ * but are part of primitive/wrapper types.
+ */
+public class NumberDeserializers
+{
+    private final static HashSet<String> _classNames = new HashSet<String>();
+    static {
+        // note: can skip primitive types; other ways to check them:
+        Class<?>[] numberTypes = new Class<?>[] {
+            Boolean.class,
+            Byte.class,
+            Short.class,
+            Character.class,
+            Integer.class,
+            Long.class,
+            Float.class,
+            Double.class,
+            // and more generic ones
+            Number.class, BigDecimal.class, BigInteger.class
+        };
+        for (Class<?> cls : numberTypes) {
+            _classNames.add(cls.getName());
+        }
+    }
+
+    /**
+     * @deprecated Since 2.2 -- use {@link #find} instead.
+     */
+    @Deprecated
+    public static StdDeserializer<?>[] all()
+    {
+        return new StdDeserializer<?>[] {
+                // primitive-wrappers (simple):
+                new BooleanDeserializer(Boolean.class, null),
+                new ByteDeserializer(Byte.class, null),
+                new ShortDeserializer(Short.class, null),
+                new CharacterDeserializer(Character.class, null),
+                new IntegerDeserializer(Integer.class, null),
+                new LongDeserializer(Long.class, null),
+                new FloatDeserializer(Float.class, null),
+                new DoubleDeserializer(Double.class, null),
+
+                /* And actual primitives: difference is the way nulls are to be
+                 * handled...
+                 */
+                new BooleanDeserializer(Boolean.TYPE, Boolean.FALSE),
+                new ByteDeserializer(Byte.TYPE, Byte.valueOf((byte)(0))),
+                new ShortDeserializer(Short.TYPE, Short.valueOf((short)0)),
+                new CharacterDeserializer(Character.TYPE, Character.valueOf('\0')),
+                new IntegerDeserializer(Integer.TYPE, Integer.valueOf(0)),
+                new LongDeserializer(Long.TYPE, Long.valueOf(0L)),
+                new FloatDeserializer(Float.TYPE, Float.valueOf(0.0f)),
+                new DoubleDeserializer(Double.TYPE, Double.valueOf(0.0)),
+                
+                // and related
+                new NumberDeserializer(),
+                new BigDecimalDeserializer(),
+                new BigIntegerDeserializer()
+        };
+    }
+    
+    public static JsonDeserializer<?> find(Class<?> rawType, String clsName)
+    {
+        if (rawType.isPrimitive()) {
+            if (rawType == Integer.TYPE) {
+                return IntegerDeserializer.primitiveInstance;
+            }
+            if (rawType == Boolean.TYPE) {
+                return BooleanDeserializer.primitiveInstance;
+            }
+            if (rawType == Long.TYPE) {
+                return LongDeserializer.primitiveInstance;
+            }
+            if (rawType == Double.TYPE) {
+                return DoubleDeserializer.primitiveInstance;
+            }
+            if (rawType == Character.TYPE) {
+                return CharacterDeserializer.primitiveInstance;
+            }
+            if (rawType == Byte.TYPE) {
+                return ByteDeserializer.primitiveInstance;
+            }
+            if (rawType == Short.TYPE) {
+                return ShortDeserializer.primitiveInstance;
+            }
+            if (rawType == Float.TYPE) {
+                return FloatDeserializer.primitiveInstance;
+            }
+        } else if (_classNames.contains(clsName)) {
+            // Start with most common types; int, boolean, long, double
+            if (rawType == Integer.class) {
+                return IntegerDeserializer.wrapperInstance;
+            }
+            if (rawType == Boolean.class) {
+                return BooleanDeserializer.wrapperInstance;
+            }
+            if (rawType == Long.class) {
+                return LongDeserializer.wrapperInstance;
+            }
+            if (rawType == Double.class) {
+                return DoubleDeserializer.wrapperInstance;
+            }
+            if (rawType == Character.class) {
+                return CharacterDeserializer.wrapperInstance;
+            }
+            if (rawType == Byte.class) {
+                return ByteDeserializer.wrapperInstance;
+            }
+            if (rawType == Short.class) {
+                return ShortDeserializer.wrapperInstance;
+            }
+            if (rawType == Float.class) {
+                return FloatDeserializer.wrapperInstance;
+            }
+            if (rawType == Number.class) {
+                return NumberDeserializer.instance;
+            }
+            if (rawType == BigDecimal.class) {
+                return BigDecimalDeserializer.instance;
+            }
+            if (rawType == BigInteger.class) {
+                return BigIntegerDeserializer.instance;
+            }
+        } else {
+            return null;
+        }
+        // should never occur
+        throw new IllegalArgumentException("Internal error: can't find deserializer for "+rawType.getName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Then one intermediate base class for things that have
+    /* both primitive and wrapper types
+    /**********************************************************
+     */
+
+    protected abstract static class PrimitiveOrWrapperDeserializer<T>
+        extends StdScalarDeserializer<T>
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final T _nullValue;
+        
+        protected PrimitiveOrWrapperDeserializer(Class<T> vc, T nvl)
+        {
+            super(vc);
+            _nullValue = nvl;
+        }
+        
+        @Override
+        public final T getNullValue() {
+            return _nullValue;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Then primitive/wrapper types
+    /**********************************************************
+     */
+
+    @JacksonStdImpl
+    public final static class BooleanDeserializer
+        extends PrimitiveOrWrapperDeserializer<Boolean>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static BooleanDeserializer primitiveInstance = new BooleanDeserializer(Boolean.class, Boolean.FALSE);
+        private final static BooleanDeserializer wrapperInstance = new BooleanDeserializer(Boolean.TYPE, null);
+        
+        public BooleanDeserializer(Class<Boolean> cls, Boolean nvl)
+        {
+            super(cls, nvl);
+        }
+        
+        @Override
+        public Boolean deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseBoolean(jp, ctxt);
+        }
+
+        // 1.6: since we can never have type info ("natural type"; String, Boolean, Integer, Double):
+        // (is it an error to even call this version?)
+        @Override
+        public Boolean deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+                TypeDeserializer typeDeserializer)
+            throws IOException, JsonProcessingException
+        {
+            return _parseBoolean(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class ByteDeserializer
+        extends PrimitiveOrWrapperDeserializer<Byte>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static ByteDeserializer primitiveInstance = new ByteDeserializer(Byte.TYPE, (byte) 0);
+        private final static ByteDeserializer wrapperInstance = new ByteDeserializer(Byte.class, null);
+        
+        public ByteDeserializer(Class<Byte> cls, Byte nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Byte deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseByte(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class ShortDeserializer
+        extends PrimitiveOrWrapperDeserializer<Short>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static ShortDeserializer primitiveInstance = new ShortDeserializer(Short.class, Short.valueOf((short)0));
+        private final static ShortDeserializer wrapperInstance = new ShortDeserializer(Short.TYPE, null);
+        
+        public ShortDeserializer(Class<Short> cls, Short nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Short deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseShort(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class CharacterDeserializer
+        extends PrimitiveOrWrapperDeserializer<Character>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static CharacterDeserializer primitiveInstance = new CharacterDeserializer(Character.class, '\0');
+        private final static CharacterDeserializer wrapperInstance = new CharacterDeserializer(Character.TYPE, null);
+        
+        public CharacterDeserializer(Class<Character> cls, Character nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Character deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            int value;
+
+            if (t == JsonToken.VALUE_NUMBER_INT) { // ok iff ascii value
+                value = jp.getIntValue();
+                if (value >= 0 && value <= 0xFFFF) {
+                    return Character.valueOf((char) value);
+                }
+            } else if (t == JsonToken.VALUE_STRING) { // this is the usual type
+                // But does it have to be exactly one char?
+                String text = jp.getText();
+                if (text.length() == 1) {
+                    return Character.valueOf(text.charAt(0));
+                }
+                // actually, empty should become null?
+                if (text.length() == 0) {
+                    return (Character) getEmptyValue();
+                }
+            }
+            throw ctxt.mappingException(_valueClass, t);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class IntegerDeserializer
+        extends PrimitiveOrWrapperDeserializer<Integer>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static IntegerDeserializer primitiveInstance = new IntegerDeserializer(Integer.class, 0);
+        private final static IntegerDeserializer wrapperInstance = new IntegerDeserializer(Integer.TYPE, null);
+        
+        public IntegerDeserializer(Class<Integer> cls, Integer nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Integer deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseInteger(jp, ctxt);
+        }
+
+        // 1.6: since we can never have type info ("natural type"; String, Boolean, Integer, Double):
+        // (is it an error to even call this version?)
+        @Override
+        public Integer deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+                TypeDeserializer typeDeserializer)
+            throws IOException, JsonProcessingException
+        {
+            return _parseInteger(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class LongDeserializer
+        extends PrimitiveOrWrapperDeserializer<Long>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static LongDeserializer primitiveInstance = new LongDeserializer(Long.class, Long.valueOf(0L));
+        private final static LongDeserializer wrapperInstance = new LongDeserializer(Long.TYPE, null);
+        
+        public LongDeserializer(Class<Long> cls, Long nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Long deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseLong(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class FloatDeserializer
+        extends PrimitiveOrWrapperDeserializer<Float>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static FloatDeserializer primitiveInstance = new FloatDeserializer(Float.class, 0.f);
+        private final static FloatDeserializer wrapperInstance = new FloatDeserializer(Float.TYPE, null);
+        
+        public FloatDeserializer(Class<Float> cls, Float nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Float deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            /* 22-Jan-2009, tatu: Bounds/range checks would be tricky
+             *   here, so let's not bother even trying...
+             */
+            return _parseFloat(jp, ctxt);
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class DoubleDeserializer
+        extends PrimitiveOrWrapperDeserializer<Double>
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static DoubleDeserializer primitiveInstance = new DoubleDeserializer(Double.class, 0.d);
+        private final static DoubleDeserializer wrapperInstance = new DoubleDeserializer(Double.TYPE, null);
+        
+        public DoubleDeserializer(Class<Double> cls, Double nvl)
+        {
+            super(cls, nvl);
+        }
+
+        @Override
+        public Double deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return _parseDouble(jp, ctxt);
+        }
+
+        // 1.6: 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,
+                TypeDeserializer typeDeserializer)
+            throws IOException, JsonProcessingException
+        {
+            return _parseDouble(jp, ctxt);
+        }
+    }
+
+    /**
+     * For type <code>Number.class</code>, we can just rely on type
+     * mappings that plain {@link JsonParser#getNumberValue} returns.
+     *<p>
+     * Since 1.5, there is one additional complication: some numeric
+     * types (specifically, int/Integer and double/Double) are "non-typed";
+     * meaning that they will NEVER be output with type information.
+     * But other numeric types may need such type information.
+     * This is why {@link #deserializeWithType} must be overridden.
+     */
+    @SuppressWarnings("serial")
+    @JacksonStdImpl
+    public final static class NumberDeserializer
+        extends StdScalarDeserializer<Number>
+    {
+        public final static NumberDeserializer instance = new NumberDeserializer();
+        
+        public NumberDeserializer() { super(Number.class); }
+
+        @Override
+        public Number deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
+                    return jp.getBigIntegerValue();
+                }
+                return jp.getNumberValue();
+            } else if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+                /* [JACKSON-72]: need to allow overriding the behavior
+                 * regarding which type to use
+                 */
+                if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                    return jp.getDecimalValue();
+                }
+                return Double.valueOf(jp.getDoubleValue());
+            }
+
+            /* Textual values are more difficult... not parsing itself, but figuring
+             * out 'minimal' type to use 
+             */
+            if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+                String text = jp.getText().trim();
+                try {
+                    if (text.indexOf('.') >= 0) { // floating point
+                        // as per [JACKSON-72]:
+                        if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                            return new BigDecimal(text);
+                        }
+                        return new Double(text);
+                    }
+                    // as per [JACKSON-100]:
+                    if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
+                        return new BigInteger(text);
+                    }
+                    long value = Long.parseLong(text);
+                    if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
+                        return Integer.valueOf((int) value);
+                    }
+                    return Long.valueOf(value);
+                } catch (IllegalArgumentException iae) {
+                    throw ctxt.weirdStringException(text, _valueClass, "not a valid number");
+                }
+            }
+            // Otherwise, no can do:
+            throw ctxt.mappingException(_valueClass, t);
+        }
+
+        /**
+         * As mentioned in class Javadoc, there is additional complexity in
+         * handling potentially mixed type information here. Because of this,
+         * we must actually check for "raw" integers and doubles first, before
+         * calling type deserializer.
+         */
+        @SuppressWarnings("incomplete-switch")
+        @Override
+        public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+                                          TypeDeserializer typeDeserializer)
+            throws IOException, JsonProcessingException
+        {
+            switch (jp.getCurrentToken()) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+            case VALUE_STRING:
+                // can not point to type information: hence must be non-typed (int/double)
+                return deserialize(jp, ctxt);
+            }
+            return typeDeserializer.deserializeTypedFromScalar(jp, ctxt);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* And then bit more complicated (but non-structured) number
+    /* types
+    /**********************************************************
+     */
+    
+    /**
+     * This is bit trickier to implement efficiently, while avoiding
+     * overflow problems.
+     */
+    @SuppressWarnings("serial")
+    @JacksonStdImpl
+    public static class BigIntegerDeserializer
+        extends StdScalarDeserializer<BigInteger>
+    {
+        public final static BigIntegerDeserializer instance = new BigIntegerDeserializer();
+
+        public BigIntegerDeserializer() { super(BigInteger.class); }
+
+        @SuppressWarnings("incomplete-switch")
+        @Override
+        public BigInteger deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            String text;
+
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                switch (jp.getNumberType()) {
+                case INT:
+                case LONG:
+                    return BigInteger.valueOf(jp.getLongValue());
+                }
+            } else if (t == JsonToken.VALUE_NUMBER_FLOAT) {
+                /* Whether to fail if there's non-integer part?
+                 * Could do by calling BigDecimal.toBigIntegerExact()
+                 */
+                return jp.getDecimalValue().toBigInteger();
+            } else if (t != JsonToken.VALUE_STRING) { // let's do implicit re-parse
+                // String is ok too, can easily convert; otherwise, no can do:
+                throw ctxt.mappingException(_valueClass, t);
+            }
+            text = jp.getText().trim();
+            if (text.length() == 0) {
+                return null;
+            }
+            try {
+                return new BigInteger(text);
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
+            }
+        }
+    }
+    
+    @SuppressWarnings("serial")
+    @JacksonStdImpl
+    public static class BigDecimalDeserializer
+        extends StdScalarDeserializer<BigDecimal>
+    {
+        public final static BigDecimalDeserializer instance = new BigDecimalDeserializer();
+ 
+        public BigDecimalDeserializer() { super(BigDecimal.class); }
+
+        @Override
+        public BigDecimal deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+                return jp.getDecimalValue();
+            }
+            // String is ok too, can easily convert
+            if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+                String text = jp.getText().trim();
+                if (text.length() == 0) {
+                    return null;
+                }
+                try {
+                    return new BigDecimal(text);
+                } catch (IllegalArgumentException iae) {
+                    throw ctxt.weirdStringException(text, _valueClass, "not a valid representation");
+                }
+            }
+            // Otherwise, no can do:
+            throw ctxt.mappingException(_valueClass, t);
+        }
+    }
+
+
+}
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
new file mode 100644
index 0000000..4c49dcb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ObjectArrayDeserializer.java
@@ -0,0 +1,248 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+
+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.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.util.ObjectBuffer;
+
+/**
+ * Basic serializer that can serialize non-primitive arrays.
+ */
+ at JacksonStdImpl
+public class ObjectArrayDeserializer
+    extends ContainerDeserializerBase<Object[]>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    // // 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.
+     */
+    protected final boolean _untyped;
+
+    /**
+     * Type of contained elements: needed for constructing actual
+     * result array
+     */
+    protected final Class<?> _elementClass;
+
+    /**
+     * Element deserializer
+     */
+    protected JsonDeserializer<Object> _elementDeserializer;
+
+    /**
+     * If element instances have polymorphic type information, this
+     * is the type deserializer that can handle it
+     */
+    protected final TypeDeserializer _elementTypeDeserializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public ObjectArrayDeserializer(ArrayType arrayType,
+            JsonDeserializer<Object> elemDeser, TypeDeserializer elemTypeDeser)
+    {
+        super(Object[].class);
+        _arrayType = arrayType;
+        _elementClass = arrayType.getContentType().getRawClass();
+        _untyped = (_elementClass == Object.class);
+        _elementDeserializer = elemDeser;
+        _elementTypeDeserializer = elemTypeDeser;
+    }
+
+    /**
+     * Overridable fluent-factory method used to create contextual instances
+     */
+    @SuppressWarnings("unchecked")
+    public ObjectArrayDeserializer withDeserializer(TypeDeserializer elemTypeDeser,
+            JsonDeserializer<?> elemDeser)
+    {
+        if ((elemDeser == _elementDeserializer) && (elemTypeDeser == _elementTypeDeserializer)) {
+            return this;
+        }
+        return new ObjectArrayDeserializer(_arrayType,
+                (JsonDeserializer<Object>) elemDeser, elemTypeDeser);
+    }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<?> deser = _elementDeserializer;
+        // #125: May have a content converter
+        deser = findConvertingContentDeserializer(ctxt, property, deser);
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(_arrayType.getContentType(), property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (deser instanceof ContextualDeserializer) {
+                deser = ((ContextualDeserializer) deser).createContextual(ctxt, property);
+            }
+        }
+        TypeDeserializer elemTypeDeser = _elementTypeDeserializer;
+        if (elemTypeDeser != null) {
+            elemTypeDeser = elemTypeDeser.forProperty(property);
+        }
+        return withDeserializer(elemTypeDeser, deser);
+    }
+    
+    /*
+    /**********************************************************
+    /* ContainerDeserializerBase API
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return _arrayType.getContentType();
+    }
+
+    @Override
+    public JsonDeserializer<Object> getContentDeserializer() {
+        return _elementDeserializer;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+    
+    @Override
+    public Object[] deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!jp.isExpectedStartArrayToken()) {
+            return handleNonArray(jp, ctxt);
+        }
+
+        final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        Object[] chunk = buffer.resetAndStart();
+        int ix = 0;
+        JsonToken t;
+        final TypeDeserializer typeDeser = _elementTypeDeserializer;
+
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            // Note: must handle null explicitly here; value deserializers won't
+            Object value;
+            
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else if (typeDeser == null) {
+                value = _elementDeserializer.deserialize(jp, ctxt);
+            } else {
+                value = _elementDeserializer.deserializeWithType(jp, ctxt, typeDeser);
+            }
+            if (ix >= chunk.length) {
+                chunk = buffer.appendCompletedChunk(chunk);
+                ix = 0;
+            }
+            chunk[ix++] = value;
+        }
+
+        Object[] result;
+
+        if (_untyped) {
+            result = buffer.completeAndClearBuffer(chunk, ix);
+        } else {
+            result = buffer.completeAndClearBuffer(chunk, ix, _elementClass);
+        }
+        ctxt.returnObjectBuffer(buffer);
+        return result;
+    }
+
+    @Override
+    public Object[] deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        /* Should there be separate handling for base64 stuff?
+         * for now this should be enough:
+         */
+        return (Object[]) typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    protected Byte[] deserializeFromBase64(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // First same as what PrimitiveArrayDeserializers.ByteDeser does:
+        byte[] b = jp.getBinaryValue(ctxt.getBase64Variant());
+        // But then need to convert to wrappers
+        Byte[] result = new Byte[b.length];
+        for (int i = 0, len = b.length; i < len; ++i) {
+            result[i] = Byte.valueOf(b[i]);
+        }
+        return result;
+    }
+
+    private final Object[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-620] Empty String can become null...
+        if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+            String str = jp.getText();
+            if (str.length() == 0) {
+                return null;
+            }
+        }
+        
+        // Can we do implicit coercion to a single-element array still?
+        if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+            /* 04-Oct-2009, tatu: One exception; byte arrays are generally
+             *   serialized as base64, so that should be handled
+             */
+            if (jp.getCurrentToken() == JsonToken.VALUE_STRING
+                && _elementClass == Byte.class) {
+                return deserializeFromBase64(jp, ctxt);
+            }
+            throw ctxt.mappingException(_arrayType.getRawClass());
+        }
+        JsonToken t = jp.getCurrentToken();
+        Object value;
+        
+        if (t == JsonToken.VALUE_NULL) {
+            value = null;
+        } else if (_elementTypeDeserializer == null) {
+            value = _elementDeserializer.deserialize(jp, ctxt);
+        } else {
+            value = _elementDeserializer.deserializeWithType(jp, ctxt, _elementTypeDeserializer);
+        }
+        // Ok: bit tricky, since we may want T[], not just Object[]
+        Object[] result;
+
+        if (_untyped) {
+            result = new Object[1];
+        } else {
+            result = (Object[]) Array.newInstance(_elementClass, 1);
+        }
+        result[0] = value;
+        return result;
+    }
+}
+
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
new file mode 100644
index 0000000..49b707f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/PrimitiveArrayDeserializers.java
@@ -0,0 +1,514 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+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.ArrayBuilders;
+
+/**
+ * Container for deserializers used for instantiating "primitive arrays",
+ * arrays that contain non-object java primitive types.
+ */
+ at SuppressWarnings("serial")
+public abstract class PrimitiveArrayDeserializers<T> extends StdDeserializer<T>
+{
+    protected PrimitiveArrayDeserializers(Class<T> cls) {
+        super(cls);
+    }
+
+    public static JsonDeserializer<?> forType(Class<?> rawType)
+    {
+        // Start with more common types...
+        if (rawType == Integer.TYPE) {
+            return IntDeser.instance;
+        }
+        if (rawType == Long.TYPE) {
+            return LongDeser.instance;
+        }
+        
+        if (rawType == Byte.TYPE) {
+            return new ByteDeser();
+        }
+        if (rawType == Short.TYPE) {
+            return new ShortDeser();
+        }
+        if (rawType == Float.TYPE) {
+            return new FloatDeser();
+        }
+        if (rawType == Double.TYPE) {
+            return new DoubleDeser();
+        }
+        if (rawType == Boolean.TYPE) {
+            return new BooleanDeser();
+        }
+        if (rawType == Character.TYPE) {
+            return new CharDeser();
+        }
+        throw new IllegalStateException();
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        /* Should there be separate handling for base64 stuff?
+         * for now this should be enough:
+         */
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+    
+    /*
+    /********************************************************
+    /* Actual deserializers: efficient String[], char[] deserializers
+    /********************************************************
+    */
+
+    @JacksonStdImpl
+    final static class CharDeser
+        extends PrimitiveArrayDeserializers<char[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public CharDeser() { super(char[].class); }
+
+        @Override
+        public char[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            /* Won't take arrays, must get a String (could also
+             * convert other tokens to Strings... but let's not bother
+             * yet, doesn't seem to make sense)
+             */
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.VALUE_STRING) {
+                // note: can NOT return shared internal buffer, must copy:
+                char[] buffer = jp.getTextCharacters();
+                int offset = jp.getTextOffset();
+                int len = jp.getTextLength();
+    
+                char[] result = new char[len];
+                System.arraycopy(buffer, offset, result, 0, len);
+                return result;
+            }
+            if (jp.isExpectedStartArrayToken()) {
+                // Let's actually build as a String, then get chars
+                StringBuilder sb = new StringBuilder(64);
+                while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+                    if (t != JsonToken.VALUE_STRING) {
+                        throw ctxt.mappingException(Character.TYPE);
+                    }
+                    String str = jp.getText();
+                    if (str.length() != 1) {
+                        throw JsonMappingException.from(jp, "Can not convert a JSON String of length "+str.length()+" into a char element of char array");
+                    }
+                    sb.append(str.charAt(0));
+                }
+                return sb.toString().toCharArray();
+            }
+            // or, maybe an embedded object?
+            if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                Object ob = jp.getEmbeddedObject();
+                if (ob == null) return null;
+                if (ob instanceof char[]) {
+                    return (char[]) ob;
+                }
+                if (ob instanceof String) {
+                    return ((String) ob).toCharArray();
+                }
+                // 04-Feb-2011, tatu: byte[] can be converted; assuming base64 is wanted
+                if (ob instanceof byte[]) {
+                    return Base64Variants.getDefaultVariant().encode((byte[]) ob, false).toCharArray();
+                }
+                // not recognized, just fall through
+            }
+            throw ctxt.mappingException(_valueClass);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Actual deserializers: primivate array desers
+    /**********************************************************
+     */
+
+    @JacksonStdImpl
+    final static class BooleanDeser
+        extends PrimitiveArrayDeserializers<boolean[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public BooleanDeser() { super(boolean[].class); }
+
+        @Override
+        public boolean[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.BooleanBuilder builder = ctxt.getArrayBuilders().getBooleanBuilder();
+            boolean[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                // whether we should allow truncating conversions?
+                boolean value = _parseBooleanPrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final boolean[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new boolean[] { _parseBooleanPrimitive(jp, ctxt) };
+        }
+    }
+
+    /**
+     * When dealing with byte arrays we have one more alternative (compared
+     * to int/long/shorts): base64 encoded data.
+     */
+    @JacksonStdImpl
+    final static class ByteDeser
+        extends PrimitiveArrayDeserializers<byte[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public ByteDeser() { super(byte[].class); }
+
+        @Override
+        public byte[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            JsonToken t = jp.getCurrentToken();
+            
+            // Most likely case: base64 encoded String?
+            if (t == JsonToken.VALUE_STRING) {
+                return jp.getBinaryValue(ctxt.getBase64Variant());
+            }
+            // 31-Dec-2009, tatu: Also may be hidden as embedded Object
+            if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                Object ob = jp.getEmbeddedObject();
+                if (ob == null) return null;
+                if (ob instanceof byte[]) {
+                    return (byte[]) ob;
+                }
+            }
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.ByteBuilder builder = ctxt.getArrayBuilders().getByteBuilder();
+            byte[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+                // whether we should allow truncating conversions?
+                byte value;
+                if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+                    // should we catch overflow exceptions?
+                    value = jp.getByteValue();
+                } else {
+                    // [JACKSON-79]: should probably accept nulls as 0
+                    if (t != JsonToken.VALUE_NULL) {
+                        throw ctxt.mappingException(_valueClass.getComponentType());
+                    }
+                    value = (byte) 0;
+                }
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final byte[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            byte value;
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+                // should we catch overflow exceptions?
+                value = jp.getByteValue();
+            } else {
+                // [JACKSON-79]: should probably accept nulls as 'false'
+                if (t != JsonToken.VALUE_NULL) {
+                    throw ctxt.mappingException(_valueClass.getComponentType());
+                }
+                value = (byte) 0;
+            }
+            return new byte[] { value };
+        }
+    }
+
+    @JacksonStdImpl
+    final static class ShortDeser
+        extends PrimitiveArrayDeserializers<short[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public ShortDeser() { super(short[].class); }
+
+        @Override
+        public short[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.ShortBuilder builder = ctxt.getArrayBuilders().getShortBuilder();
+            short[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                short value = _parseShortPrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final short[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new short[] { _parseShortPrimitive(jp, ctxt) };
+        }
+    }
+
+    @JacksonStdImpl
+    final static class IntDeser
+        extends PrimitiveArrayDeserializers<int[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public final static IntDeser instance = new IntDeser();
+        
+        public IntDeser() { super(int[].class); }
+
+        @Override
+        public int[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.IntBuilder builder = ctxt.getArrayBuilders().getIntBuilder();
+            int[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                // whether we should allow truncating conversions?
+                int value = _parseIntPrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final int[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new int[] { _parseIntPrimitive(jp, ctxt) };
+        }
+    }
+
+    @JacksonStdImpl
+    final static class LongDeser
+        extends PrimitiveArrayDeserializers<long[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public final static LongDeser instance = new LongDeser();
+        
+        public LongDeser() { super(long[].class); }
+
+        @Override
+        public long[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.LongBuilder builder = ctxt.getArrayBuilders().getLongBuilder();
+            long[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                long value = _parseLongPrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final long[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new long[] { _parseLongPrimitive(jp, ctxt) };
+        }
+    }
+
+    @JacksonStdImpl
+    final static class FloatDeser
+        extends PrimitiveArrayDeserializers<float[]>
+    {
+        private static final long serialVersionUID = 1L;
+
+        public FloatDeser() { super(float[].class); }
+
+        @Override
+        public float[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.FloatBuilder builder = ctxt.getArrayBuilders().getFloatBuilder();
+            float[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                // whether we should allow truncating conversions?
+                float value = _parseFloatPrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final float[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new float[] { _parseFloatPrimitive(jp, ctxt) };
+        }
+    }
+
+    @JacksonStdImpl
+    final static class DoubleDeser
+        extends PrimitiveArrayDeserializers<double[]>
+    {
+        private static final long serialVersionUID = 1L;
+        
+        public DoubleDeser() { super(double[].class); }
+
+        @Override
+        public double[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (!jp.isExpectedStartArrayToken()) {
+                return handleNonArray(jp, ctxt);
+            }
+            ArrayBuilders.DoubleBuilder builder = ctxt.getArrayBuilders().getDoubleBuilder();
+            double[] chunk = builder.resetAndStart();
+            int ix = 0;
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                double value = _parseDoublePrimitive(jp, ctxt);
+                if (ix >= chunk.length) {
+                    chunk = builder.appendCompletedChunk(chunk, ix);
+                    ix = 0;
+                }
+                chunk[ix++] = value;
+            }
+            return builder.completeAndClearBuffer(chunk, ix);
+        }
+
+        private final double[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                if (jp.getText().length() == 0) {
+                    return null;
+                }
+            }
+            if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+                throw ctxt.mappingException(_valueClass);
+            }
+            return new double[] { _parseDoublePrimitive(jp, ctxt) };
+        }
+    }
+}
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
new file mode 100644
index 0000000..52cea72
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDelegatingDeserializer.java
@@ -0,0 +1,185 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.*;
+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.Converter;
+
+/**
+ * Deserializer implementation where given Java type is first deserialized
+ * by a standard Jackson deserializer into a delegate type; and then
+ * this delegate type is converted using a configured
+ * {@link Converter} into desired target type.
+ * Common delegate types to use are {@link java.util.Map}
+ * and {@link com.fasterxml.jackson.databind.JsonNode}.
+ *<p>
+ * Note that although types (delegate, target) may be related, they must not be same; trying
+ * to do this will result in an exception.
+ * 
+ * @param <T> Target type to convert to, from delegate type
+ * 
+ * @since 2.1
+ */
+public class StdDelegatingDeserializer<T>
+    extends StdDeserializer<T>
+    implements ContextualDeserializer, ResolvableDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final Converter<Object,T> _converter;
+    
+    /**
+     * Fully resolved delegate type, with generic information if any available.
+     */
+    protected final JavaType _delegateType;
+    
+    /**
+     * Underlying serializer for type <code>T<.code>.
+     */
+    protected final JsonDeserializer<Object> _delegateDeserializer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public StdDelegatingDeserializer(Converter<?,T> converter)
+    {
+        super(Object.class);
+        _converter = (Converter<Object,T>)converter;
+        _delegateType = null;
+        _delegateDeserializer = null;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public StdDelegatingDeserializer(Converter<Object,T> converter,
+            JavaType delegateType, JsonDeserializer<?> delegateDeserializer)
+    {
+        super(delegateType);
+        _converter = converter;
+        _delegateType = delegateType;
+        _delegateDeserializer = (JsonDeserializer<Object>) delegateDeserializer;
+    }
+
+    /**
+     * Method used for creating resolved contextual instances. Must be
+     * overridden when sub-classing.
+     */
+    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'");
+        }
+        return new StdDelegatingDeserializer<T>(converter, delegateType, delegateDeserializer);
+    }
+
+    /*
+    /**********************************************************
+    /* Contextualization
+    /**********************************************************
+     */
+
+    @Override
+    public void resolve(DeserializationContext ctxt)
+        throws JsonMappingException
+    {
+        if (_delegateDeserializer != null && _delegateDeserializer instanceof ResolvableDeserializer) {
+            ((ResolvableDeserializer) _delegateDeserializer).resolve(ctxt);
+        }
+    }
+    
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
+        throws JsonMappingException
+    {
+        // First: if already got serializer to delegate to, contextualize it:
+        if (_delegateDeserializer != null) {
+            if (_delegateDeserializer instanceof ContextualDeserializer) {
+                JsonDeserializer<?> deser = ((ContextualDeserializer)_delegateDeserializer).createContextual(ctxt, property);
+                if (deser != _delegateDeserializer) {
+                    return withDelegate(_converter, _delegateType, deser);
+                }
+            }
+            return this;
+        }
+        // Otherwise: figure out what is the fully generic delegate type, then find deserializer
+        JavaType delegateType = _converter.getInputType(ctxt.getTypeFactory());
+        return withDelegate(_converter, delegateType,
+                ctxt.findContextualValueDeserializer(delegateType, property));
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    @Override
+    public JsonDeserializer<?> getDelegatee() {
+        return _delegateDeserializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public T deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        Object delegateValue = _delegateDeserializer.deserialize(jp, ctxt);
+        if (delegateValue == null) {
+            return null;
+        }
+        return convertValue(delegateValue);
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        /* 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now,
+         *    let's give it a chance?
+         */
+        Object delegateValue = _delegateDeserializer.deserializeWithType(jp, ctxt,
+                typeDeserializer);
+        if (delegateValue == null) {
+            return null;
+        }
+        return convertValue(delegateValue);
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to convert from "delegate value" (which was deserialized
+     * from JSON using standard Jackson deserializer for delegate type)
+     * into desired target type.
+     *<P>
+     * The default implementation uses configured {@link Converter} to do
+     * conversion.
+     * 
+     * @param delegateValue
+     * 
+     * @return Result of conversion
+     */
+    protected T convertValue(Object delegateValue) {
+        return _converter.convert(delegateValue);
+    }
+}
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
new file mode 100644
index 0000000..8cefaf4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdDeserializer.java
@@ -0,0 +1,715 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonParser.NumberType;
+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.Converter;
+
+/**
+ * Base class for common deserializers. Contains shared
+ * base functionality for dealing with primitive values, such
+ * as (re)parsing from String.
+ */
+public abstract class StdDeserializer<T>
+    extends JsonDeserializer<T>
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Type of values this deserializer handles: sometimes
+     * exact types, other time most specific supertype of
+     * types deserializer handles (which may be as generic
+     * as {@link Object} in some case)
+     */
+    final protected Class<?> _valueClass;
+
+    protected StdDeserializer(Class<?> vc) {
+        _valueClass = vc;
+    }
+
+    protected StdDeserializer(JavaType valueType) {
+        _valueClass = (valueType == null) ? null : valueType.getRawClass();
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    public Class<?> getValueClass() { return _valueClass; }
+
+    /**
+     * Exact structured type deserializer handles, if known.
+     *<p>
+     * Default implementation just returns null.
+     */
+    public JavaType getValueType() { return null; }
+
+    /**
+     * Method that can be called to determine if given deserializer is the default
+     * deserializer Jackson uses; as opposed to a custom deserializer installed by
+     * a module or calling application. Determination is done using
+     * {@link JacksonStdImpl} annotation on deserializer class.
+     */
+    protected boolean isDefaultDeserializer(JsonDeserializer<?> deserializer) {
+        return (deserializer != null && deserializer.getClass().getAnnotation(JacksonStdImpl.class) != null);
+    }
+
+    protected boolean isDefaultKeyDeserializer(KeyDeserializer keyDeser) {
+        return (keyDeser != null && keyDeser.getClass().getAnnotation(JacksonStdImpl.class) != null);
+    }
+    
+    /*
+    /**********************************************************
+    /* Partial JsonDeserializer implementation 
+    /**********************************************************
+     */
+    
+    /**
+     * Base implementation that does not assume specific type
+     * inclusion mechanism. Sub-classes are expected to override
+     * this method if they are to handle type information.
+     */
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods for sub-classes, parsing: while mostly
+    /* useful for numeric types, can be also useful for dealing
+    /* with things serialized as numbers (such as Dates).
+    /**********************************************************
+     */
+
+    protected final boolean _parseBooleanPrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_TRUE) {
+            return true;
+        }
+        if (t == JsonToken.VALUE_FALSE) {
+            return false;
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return false;
+        }
+        // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            // 11-Jan-2012, tatus: May be outside of int...
+            if (jp.getNumberType() == NumberType.INT) {
+                return (jp.getIntValue() != 0);
+            }
+            return _parseBooleanFromNumber(jp, ctxt);
+        }
+        // And finally, let's allow Strings to be converted too
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if ("true".equals(text)) {
+                return true;
+            }
+            if ("false".equals(text) || text.length() == 0) {
+                return Boolean.FALSE;
+            }
+            throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final Boolean _parseBoolean(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_TRUE) {
+            return Boolean.TRUE;
+        }
+        if (t == JsonToken.VALUE_FALSE) {
+            return Boolean.FALSE;
+        }
+        // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            // 11-Jan-2012, tatus: May be outside of int...
+            if (jp.getNumberType() == NumberType.INT) {
+                return (jp.getIntValue() == 0) ? Boolean.FALSE : Boolean.TRUE;
+            }
+            return Boolean.valueOf(_parseBooleanFromNumber(jp, ctxt));
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Boolean) getNullValue();
+        }
+        // And finally, let's allow Strings to be converted too
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if ("true".equals(text)) {
+                return Boolean.TRUE;
+            }
+            if ("false".equals(text)) {
+                return Boolean.FALSE;
+            }
+            if (text.length() == 0) {
+                return (Boolean) getEmptyValue();
+            }
+            throw ctxt.weirdStringException(text, _valueClass, "only \"true\" or \"false\" recognized");
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final boolean _parseBooleanFromNumber(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        if (jp.getNumberType() == NumberType.LONG) {
+            return (jp.getLongValue() == 0L) ? Boolean.FALSE : Boolean.TRUE;
+        }
+        // no really good logic; let's actually resort to textual comparison
+        String str = jp.getText();
+        if ("0.0".equals(str) || "0".equals(str)) {
+            return Boolean.FALSE;
+        }
+        return Boolean.TRUE;
+    }
+
+    protected Byte _parseByte(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getByteValue();
+        }
+        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+            String text = jp.getText().trim();
+            int value;
+            try {
+                int len = text.length();
+                if (len == 0) {
+                    return (Byte) getEmptyValue();
+                }
+                value = NumberInput.parseInt(text);
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(text, _valueClass, "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) {
+                throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 8-bit value");
+            }
+            return Byte.valueOf((byte) value);
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Byte) getNullValue();
+        }
+        throw ctxt.mappingException(_valueClass, t);
+    }
+    
+    protected Short _parseShort(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getShortValue();
+        }
+        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+            String text = jp.getText().trim();
+            int value;
+            try {
+                int len = text.length();
+                if (len == 0) {
+                    return (Short) getEmptyValue();
+                }
+                value = NumberInput.parseInt(text);
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(text, _valueClass, "not a valid Short value");
+            }
+            // So far so good: but does it fit?
+            if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+                throw ctxt.weirdStringException(text, _valueClass, "overflow, value can not be represented as 16-bit value");
+            }
+            return Short.valueOf((short) value);
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Short) getNullValue();
+        }
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final short _parseShortPrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        int value = _parseIntPrimitive(jp, ctxt);
+        // So far so good: but does it fit?
+        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+            throw ctxt.weirdStringException(String.valueOf(value),
+                    _valueClass, "overflow, value can not be represented as 16-bit value");
+        }
+        return (short) value;
+    }
+    
+    protected final int _parseIntPrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+
+        // Int works as is, coercing fine as well
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getIntValue();
+        }
+        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+            /* 31-Dec-2009, tatus: Should improve handling of overflow
+             *   values... but this'll have to do for now
+             */
+            String text = jp.getText().trim();
+            try {
+                int len = text.length();
+                if (len > 9) {
+                    long l = Long.parseLong(text);
+                    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
+                        throw ctxt.weirdStringException(text, _valueClass,
+                            "Overflow: numeric value ("+text+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+                    }
+                    return (int) l;
+                }
+                if (len == 0) {
+                    return 0;
+                }
+                return NumberInput.parseInt(text);
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(text, _valueClass, "not a valid int value");
+            }
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return 0;
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final Integer _parseInteger(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return Integer.valueOf(jp.getIntValue());
+        }
+        if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
+            String text = jp.getText().trim();
+            try {
+                int len = text.length();
+                if (len > 9) {
+                    long l = Long.parseLong(text);
+                    if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
+                        throw ctxt.weirdStringException(text, _valueClass,
+                            "Overflow: numeric value ("+text+") out of range of Integer ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+                    }
+                    return Integer.valueOf((int) l);
+                }
+                if (len == 0) {
+                    return (Integer) getEmptyValue();
+                }
+                return Integer.valueOf(NumberInput.parseInt(text));
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(text, _valueClass, "not a valid Integer value");
+            }
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Integer) getNullValue();
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final Long _parseLong(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+    
+        // it should be ok to coerce (although may fail, too)
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+            return jp.getLongValue();
+        }
+        // let's allow Strings to be converted too
+        if (t == JsonToken.VALUE_STRING) {
+            // !!! 05-Jan-2009, tatu: Should we try to limit value space, JDK is too lenient?
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return (Long) getEmptyValue();
+            }
+            try {
+                return Long.valueOf(NumberInput.parseLong(text));
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid Long value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Long) getNullValue();
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final long _parseLongPrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) {
+            return jp.getLongValue();
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return 0L;
+            }
+            try {
+                return NumberInput.parseLong(text);
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid long value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return 0L;
+        }
+        throw ctxt.mappingException(_valueClass, t);
+    }
+    
+    protected final Float _parseFloat(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // We accept couple of different types; obvious ones first:
+        JsonToken t = jp.getCurrentToken();
+        
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getFloatValue();
+        }
+        // And finally, let's allow Strings to be converted too
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return (Float) getEmptyValue();
+            }
+            switch (text.charAt(0)) {
+            case 'I':
+                if ("Infinity".equals(text) || "INF".equals(text)) {
+                    return Float.POSITIVE_INFINITY;
+                }
+                break;
+            case 'N':
+                if ("NaN".equals(text)) {
+                    return Float.NaN;
+                }
+                break;
+            case '-':
+                if ("-Infinity".equals(text) || "-INF".equals(text)) {
+                    return Float.NEGATIVE_INFINITY;
+                }
+                break;
+            }
+            try {
+                return Float.parseFloat(text);
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid Float value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Float) getNullValue();
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final float _parseFloatPrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getFloatValue();
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return 0.0f;
+            }
+            switch (text.charAt(0)) {
+            case 'I':
+                if ("Infinity".equals(text) || "INF".equals(text)) {
+                    return Float.POSITIVE_INFINITY;
+                }
+                break;
+            case 'N':
+                if ("NaN".equals(text)) {
+                    return Float.NaN;
+                }
+                break;
+            case '-':
+                if ("-Infinity".equals(text) || "-INF".equals(text)) {
+                    return Float.NEGATIVE_INFINITY;
+                }
+                break;
+            }
+            try {
+                return Float.parseFloat(text);
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid float value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return 0.0f;
+        }
+        // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final Double _parseDouble(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getDoubleValue();
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return (Double) getEmptyValue();
+            }
+            switch (text.charAt(0)) {
+            case 'I':
+                if ("Infinity".equals(text) || "INF".equals(text)) {
+                    return Double.POSITIVE_INFINITY;
+                }
+                break;
+            case 'N':
+                if ("NaN".equals(text)) {
+                    return Double.NaN;
+                }
+                break;
+            case '-':
+                if ("-Infinity".equals(text) || "-INF".equals(text)) {
+                    return Double.NEGATIVE_INFINITY;
+                }
+                break;
+            }
+            try {
+                return parseDouble(text);
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid Double value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (Double) getNullValue();
+        }
+            // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected final double _parseDoublePrimitive(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // We accept couple of different types; obvious ones first:
+        JsonToken t = jp.getCurrentToken();
+        
+        if (t == JsonToken.VALUE_NUMBER_INT || t == JsonToken.VALUE_NUMBER_FLOAT) { // coercing should work too
+            return jp.getDoubleValue();
+        }
+        // And finally, let's allow Strings to be converted too
+        if (t == JsonToken.VALUE_STRING) {
+            String text = jp.getText().trim();
+            if (text.length() == 0) {
+                return 0.0;
+            }
+            switch (text.charAt(0)) {
+            case 'I':
+                if ("Infinity".equals(text) || "INF".equals(text)) {
+                    return Double.POSITIVE_INFINITY;
+                }
+                break;
+            case 'N':
+                if ("NaN".equals(text)) {
+                    return Double.NaN;
+                }
+                break;
+            case '-':
+                if ("-Infinity".equals(text) || "-INF".equals(text)) {
+                    return Double.NEGATIVE_INFINITY;
+                }
+                break;
+            }
+            try {
+                return parseDouble(text);
+            } catch (IllegalArgumentException iae) { }
+            throw ctxt.weirdStringException(text, _valueClass, "not a valid double value");
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return 0.0;
+        }
+            // Otherwise, no can do:
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    protected java.util.Date _parseDate(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            return new java.util.Date(jp.getLongValue());
+        }
+        if (t == JsonToken.VALUE_NULL) {
+            return (java.util.Date) getNullValue();
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String value = null;
+            try {
+                // As per [JACKSON-203], take empty Strings to mean
+                value = jp.getText().trim();
+                if (value.length() == 0) {
+                    return (Date) getEmptyValue();
+                }
+                return ctxt.parseDate(value);
+            } catch (IllegalArgumentException iae) {
+                throw ctxt.weirdStringException(value, _valueClass,
+                        "not a valid representation (error: "+iae.getMessage()+")");
+            }
+        }
+        throw ctxt.mappingException(_valueClass, t);
+    }
+
+    /**
+     * Helper method for encapsulating calls to low-level double value parsing; single place
+     * just because we need a work-around that must be applied to all calls.
+     */
+    protected final static double parseDouble(String numStr) throws NumberFormatException
+    {
+        // [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
+        // for now, MIN_VALUE, since MIN_NORMAL is JDK 1.6
+        if (NumberInput.NASTY_SMALL_DOUBLE.equals(numStr)) {
+            return Double.MIN_VALUE;
+        }
+        return Double.parseDouble(numStr);
+    }
+    
+    /**
+     * Helper method used for accessing String value, if possible, doing
+     * necessary conversion or throwing exception as necessary.
+     * 
+     * @since 2.1
+     */
+    protected final String _parseString(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        String value = jp.getValueAsString();
+        if (value != null) {
+            return value;
+        }
+        throw ctxt.mappingException(String.class, jp.getCurrentToken());
+    }
+    
+    /*
+    /****************************************************
+    /* Helper methods for sub-classes, resolving dependencies
+    /****************************************************
+     */
+
+    /**
+     * Helper method used to locate deserializers for properties the
+     * type this deserializer handles contains (usually for properties of
+     * bean types)
+     * 
+     * @param type Type of property to deserialize
+     * @param property Actual property object (field, method, constuctor parameter) used
+     *     for passing deserialized values; provided so deserializer can be contextualized if necessary (since 1.7)
+     */
+    protected JsonDeserializer<Object> findDeserializer(DeserializationContext ctxt,
+            JavaType type, BeanProperty property)
+        throws JsonMappingException
+    {
+        return ctxt.findContextualValueDeserializer(type, property);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for sub-classes, 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
+     * of structured types; array/List/Map values)
+     * 
+     * @param existingDeserializer (optional) configured content
+     *    serializer if one already exists.
+     * 
+     * @since 2.2
+     */
+    protected JsonDeserializer<?> findConvertingContentDeserializer(DeserializationContext ctxt,
+            BeanProperty prop, JsonDeserializer<?> existingDeserializer)
+        throws JsonMappingException
+    {
+        final AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
+        if (intr != null && prop != null) {
+            Object convDef = intr.findDeserializationContentConverter(prop.getMember());
+            if (convDef != null) {
+                Converter<Object,Object> conv = ctxt.converterInstance(prop.getMember(), convDef);
+                JavaType delegateType = conv.getInputType(ctxt.getTypeFactory());
+                if (existingDeserializer == null) {
+                    existingDeserializer = ctxt.findContextualValueDeserializer(delegateType, prop);
+                }
+                return new StdDelegatingDeserializer<Object>(conv, delegateType, existingDeserializer);
+            }
+        }
+        return existingDeserializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for sub-classes, problem reporting
+    /**********************************************************
+     */
+
+    /**
+     * Method called to deal with a property that did not map to a known
+     * Bean property. Method can deal with the problem as it sees fit (ignore,
+     * throw exception); but if it does return, it has to skip the matching
+     * Json content parser has.
+     *<p>
+     * NOTE: method signature was changed in version 1.5; explicit JsonParser
+     * <b>must</b> be passed since it may be something other than what
+     * context has. Prior versions did not include the first parameter.
+     *
+     * @param jp Parser that points to value of the unknown property
+     * @param ctxt Context for deserialization; allows access to the parser,
+     *    error reporting functionality
+     * @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
+     */
+    protected void handleUnknownProperty(JsonParser jp, DeserializationContext ctxt,
+            Object instanceOrClass, String propName)
+        throws IOException, JsonProcessingException
+    {
+        if (instanceOrClass == null) {
+            instanceOrClass = getValueClass();
+        }
+        // Maybe we have configured handler(s) to take care of it?
+        if (ctxt.handleUnknownProperty(jp, this, instanceOrClass, propName)) {
+            return;
+        }
+        // Nope, not handled. Potentially that's a problem...
+        ctxt.reportUnknownProperty(instanceOrClass, propName, this);
+
+        /* But if we do get this far, need to skip whatever value we
+         * are pointing to now.
+         */
+        jp.skipChildren();
+    }
+}
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
new file mode 100644
index 0000000..206b12f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializer.java
@@ -0,0 +1,450 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Locale;
+import java.util.UUID;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.io.NumberInput;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.EnumResolver;
+
+/**
+ * Base class for simple key deserializers.
+ */
+public abstract class StdKeyDeserializer
+    extends KeyDeserializer
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    final protected Class<?> _keyClass;
+
+    protected StdKeyDeserializer(Class<?> cls) { _keyClass = cls; }
+
+    @Override
+    public final Object deserializeKey(String key, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (key == null) { // is this even legal call?
+            return null;
+        }
+        try {
+            Object result = _parse(key, ctxt);
+            if (result != null) {
+                return result;
+            }
+        } catch (Exception re) {
+            throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage());
+        }
+        if (_keyClass.isEnum() && ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+            return null;
+        }
+        throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation");
+    }
+
+    public Class<?> getKeyClass() { return _keyClass; }
+
+    protected abstract Object _parse(String key, DeserializationContext ctxt) throws Exception;
+
+    /*
+    /**********************************************************
+    /* Helper methods for sub-classes
+    /**********************************************************
+     */
+
+    protected int _parseInt(String key) throws IllegalArgumentException
+    {
+        return Integer.parseInt(key);
+    }
+
+    protected long _parseLong(String key) throws IllegalArgumentException
+    {
+        return Long.parseLong(key);
+    }
+
+    protected double _parseDouble(String key) throws IllegalArgumentException
+    {
+        return NumberInput.parseDouble(key);
+    }
+
+    /*
+    /**********************************************************
+    /* First: the standard "String as String" deserializer
+    /**********************************************************
+     */
+
+    @JacksonStdImpl
+    final static class StringKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final static StringKD sString = new StringKD(String.class);
+        private final static StringKD sObject = new StringKD(Object.class);
+        
+        private StringKD(Class<?> nominalType) { super(nominalType); }
+
+        public static StringKD forType(Class<?> nominalType)
+        {
+            if (nominalType == String.class) {
+                return sString;
+            }
+            if (nominalType == Object.class) {
+                return sObject;
+            }
+            return new StringKD(nominalType);
+        }
+        
+        @Override
+        public String _parse(String key, DeserializationContext ctxt) throws JsonMappingException {
+            return key;
+        }
+    }    
+    
+    /*
+    /**********************************************************
+    /* Key deserializer implementations; wrappers
+    /**********************************************************
+     */
+
+    @JacksonStdImpl
+    final static class BoolKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        BoolKD() { super(Boolean.class); }
+
+        @Override
+        public Boolean _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            if ("true".equals(key)) {
+                return Boolean.TRUE;
+            }
+            if ("false".equals(key)) {
+                return Boolean.FALSE;
+            }
+            throw ctxt.weirdKeyException(_keyClass, key, "value not 'true' or 'false'");
+        }
+    }
+
+    @JacksonStdImpl
+    final static class ByteKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        ByteKD() { super(Byte.class); }
+
+        @Override
+		public Byte _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            int value = _parseInt(key);
+            // as per [JACKSON-804], allow range up to 255, inclusive
+            if (value < Byte.MIN_VALUE || value > 255) {
+                throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 8-bit value");
+            }
+            return Byte.valueOf((byte) value);
+        }
+    }
+
+    @JacksonStdImpl
+    final static class ShortKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        ShortKD() { super(Integer.class); }
+
+        @Override
+		public Short _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            int value = _parseInt(key);
+            if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+                throw ctxt.weirdKeyException(_keyClass, key, "overflow, value can not be represented as 16-bit value");
+            }
+            return Short.valueOf((short) value);
+        }
+    }
+
+    /**
+     * Dealing with Characters is bit trickier: let's assume it must be a String, and that
+     * Unicode numeric value is never used.
+     */
+    @JacksonStdImpl
+    final static class CharKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        CharKD() { super(Character.class); }
+
+        @Override
+		public Character _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            if (key.length() == 1) {
+                return Character.valueOf(key.charAt(0));
+            }
+            throw ctxt.weirdKeyException(_keyClass, key, "can only convert 1-character Strings");
+        }
+    }
+
+    @JacksonStdImpl
+    final static class IntKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        IntKD() { super(Integer.class); }
+
+        @Override
+		public Integer _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            return _parseInt(key);
+        }
+    }
+
+    @JacksonStdImpl
+    final static class LongKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        LongKD() { super(Long.class); }
+
+        @Override
+        public Long _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            return _parseLong(key);
+        }
+    }
+
+    @JacksonStdImpl
+    final static class DoubleKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        DoubleKD() { super(Double.class); }
+
+        @Override
+        public Double _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            return _parseDouble(key);
+        }
+    }
+
+    @JacksonStdImpl
+    final static class FloatKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        FloatKD() { super(Float.class); }
+
+        @Override
+        public Float _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            /* 22-Jan-2009, tatu: Bounds/range checks would be tricky
+             *   here, so let's not bother even trying...
+             */
+            return Float.valueOf((float) _parseDouble(key));
+        }
+    }
+
+    @JacksonStdImpl
+    final static class LocaleKD extends StdKeyDeserializer {
+        private static final long serialVersionUID = 1L;
+
+        protected JdkDeserializers.LocaleDeserializer _localeDeserializer;
+
+        LocaleKD() { super(Locale.class); _localeDeserializer = new JdkDeserializers.LocaleDeserializer();}
+
+        @Override
+        protected Locale _parse(String key, DeserializationContext ctxt) throws JsonMappingException {
+            try {
+                return _localeDeserializer._deserialize(key,ctxt);
+            } catch (IOException e) {
+                throw ctxt.weirdKeyException(_keyClass, key, "unable to parse key as locale");
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Key deserializer implementations; other
+    /**********************************************************
+     */
+
+    /**
+     * Key deserializer that wraps a "regular" deserializer (but one
+     * that must recognize FIELD_NAMEs as text!) to reuse existing
+     * handlers as key handlers.
+     */
+    final static class DelegatingKD
+        extends KeyDeserializer // note: NOT the std one
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        final protected Class<?> _keyClass;
+
+        protected final JsonDeserializer<?> _delegate;
+        
+        protected DelegatingKD(Class<?> cls, JsonDeserializer<?> deser) {
+            _keyClass = cls;
+            _delegate = deser;
+        }
+
+        @Override
+        public final Object deserializeKey(String key, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (key == null) { // is this even legal call?
+                return null;
+            }
+            try {
+                // Ugh... should not have to give parser which may or may not be correct one...
+                Object result = _delegate.deserialize(ctxt.getParser(), ctxt);
+                if (result != null) {
+                    return result;
+                }
+            } catch (Exception re) {
+                throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation: "+re.getMessage());
+            }
+            throw ctxt.weirdKeyException(_keyClass, key, "not a valid representation");
+        }
+
+        public Class<?> getKeyClass() { return _keyClass; }
+    }
+     
+    @JacksonStdImpl
+    final static class EnumKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final EnumResolver<?> _resolver;
+
+        protected final AnnotatedMethod _factory;
+
+        protected EnumKD(EnumResolver<?> er, AnnotatedMethod factory) {
+            super(er.getEnumClass());
+            _resolver = er;
+            _factory = factory;
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt) throws JsonMappingException
+        {
+            if (_factory != null) {
+                try {
+                    return _factory.call1(key);
+                } catch (Exception e) {
+                    ClassUtil.unwrapAndThrowAsIAE(e);
+                }
+            }
+            Enum<?> e = _resolver.findEnum(key);
+            if (e == null && !ctxt.getConfig().isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
+                throw ctxt.weirdKeyException(_keyClass, key, "not one of values for Enum class");
+            }
+            return e;
+        }
+    }
+    
+    /**
+     * Key deserializer that calls a single-string-arg constructor
+     * to instantiate desired key type.
+     */
+    final static class StringCtorKeyDeserializer extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected final Constructor<?> _ctor;
+
+        public StringCtorKeyDeserializer(Constructor<?> ctor) {
+            super(ctor.getDeclaringClass());
+            _ctor = ctor;
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt) throws Exception
+        {
+            return _ctor.newInstance(key);
+        }
+    }
+
+    /**
+     * Key deserializer that calls a static no-args factory method
+     * to instantiate desired key type.
+     */
+    final static class StringFactoryKeyDeserializer extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        final Method _factoryMethod;
+
+        public StringFactoryKeyDeserializer(Method fm) {
+            super(fm.getDeclaringClass());
+            _factoryMethod = fm;
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt) throws Exception
+        {
+            return _factoryMethod.invoke(null, key);
+        }
+    }
+
+    // as per [JACKSON-657]
+    @JacksonStdImpl
+    final static class DateKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected DateKD() {
+            super(java.util.Date.class);
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt)
+            throws IllegalArgumentException, JsonMappingException
+        {
+            return ctxt.parseDate(key);
+        }
+    }
+        
+    // as per [JACKSON-657]
+    @JacksonStdImpl
+    final static class CalendarKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected CalendarKD() {
+            super(java.util.Calendar.class);
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt)
+            throws IllegalArgumentException, JsonMappingException
+        {
+            java.util.Date date = ctxt.parseDate(key);
+            return (date == null)  ? null : ctxt.constructCalendar(date);
+        }
+    }
+
+    // as per [JACKSON-726]
+    @JacksonStdImpl
+    final static class UuidKD extends StdKeyDeserializer
+    {
+        private static final long serialVersionUID = 1L;
+
+        protected UuidKD() {
+            super(UUID.class);
+        }
+
+        @Override
+        public Object _parse(String key, DeserializationContext ctxt)
+            throws IllegalArgumentException, JsonMappingException
+        {
+            return UUID.fromString(key);
+        }
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java
new file mode 100644
index 0000000..1e22efc
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdKeyDeserializers.java
@@ -0,0 +1,149 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.KeyDeserializers;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.EnumResolver;
+
+/**
+ * Helper class used to contain simple/well-known key deserializers.
+ * Following kinds of Objects can be handled currently:
+ *<ul>
+ * <li>Primitive wrappers (Boolean, Byte, Char, Short, Integer, Float, Long, Double)</li>
+ * <li>Enums (usually not needed, since EnumMap doesn't call us)</li>
+ * <li>{@link java.util.Date}</li>
+ * <li>{@link java.util.Calendar}</li>
+ * <li>{@link java.util.UUID}</li>
+ * <li>{@link java.util.Locale}</li>
+ * <li>Anything with constructor that takes a single String arg
+ *   (if not explicitly @JsonIgnore'd)</li>
+ * <li>Anything with {@code static T valueOf(String)} factory method
+ *   (if not explicitly @JsonIgnore'd)</li>
+ *</ul>
+ */
+public class StdKeyDeserializers
+    implements KeyDeserializers, java.io.Serializable
+{
+    private static final long serialVersionUID = 923268084968181479L;
+
+    /**
+     * @deprecated Since 2.2, just call <code>StdKeyDeserializer.StringKD</code> directly
+     */
+    @Deprecated
+    public static KeyDeserializer constructStringKeyDeserializer(DeserializationConfig config,
+            JavaType type) {
+        return StdKeyDeserializer.StringKD.forType(type.getRawClass());
+    }
+    
+    public static KeyDeserializer constructEnumKeyDeserializer(EnumResolver<?> enumResolver) {
+        return new StdKeyDeserializer.EnumKD(enumResolver, null);
+    }
+
+    public static KeyDeserializer constructEnumKeyDeserializer(EnumResolver<?> enumResolver,
+            AnnotatedMethod factory) {
+        return new StdKeyDeserializer.EnumKD(enumResolver, factory);
+    }
+    
+    public static KeyDeserializer constructDelegatingKeyDeserializer(DeserializationConfig config,
+            JavaType type, JsonDeserializer<?> deser)
+    {
+        return new StdKeyDeserializer.DelegatingKD(type.getRawClass(), deser);
+    }
+    
+    public static KeyDeserializer findStringBasedKeyDeserializer(DeserializationConfig config,
+            JavaType type)
+    {
+        /* We don't need full deserialization information, just need to
+         * know creators.
+         */
+        BeanDescription beanDesc = config.introspect(type);
+        // Ok, so: can we find T(String) constructor?
+        Constructor<?> ctor = beanDesc.findSingleArgConstructor(String.class);
+        if (ctor != null) {
+            if (config.canOverrideAccessModifiers()) {
+                ClassUtil.checkAndFixAccess(ctor);
+            }
+            return new StdKeyDeserializer.StringCtorKeyDeserializer(ctor);
+        }
+        /* or if not, "static T valueOf(String)" (or equivalent marked
+         * with @JsonCreator annotation?)
+         */
+        Method m = beanDesc.findFactoryMethod(String.class);
+        if (m != null){
+            if (config.canOverrideAccessModifiers()) {
+                ClassUtil.checkAndFixAccess(m);
+            }
+            return new StdKeyDeserializer.StringFactoryKeyDeserializer(m);
+        }
+        // nope, no such luck...
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* KeyDeserializers implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public KeyDeserializer findKeyDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+        // First, common types; String/Object/UUID, Int/Long, Dates
+        if (raw == String.class || raw == Object.class) {
+            return StdKeyDeserializer.StringKD.forType(raw);
+        }
+        if (raw == UUID.class) {
+            return new StdKeyDeserializer.UuidKD();
+        }
+        
+        // 23-Apr-2013, tatu: Map primitive types, just in case one was given
+        if (raw.isPrimitive()) {
+            raw = ClassUtil.wrapperType(raw);
+        }
+        
+        if (raw == Integer.class) {
+            return new StdKeyDeserializer.IntKD();
+        }
+        if (raw == Long.class) {
+            return new StdKeyDeserializer.LongKD();
+        }
+        if (raw == Date.class) {
+            return new StdKeyDeserializer.DateKD();
+        }
+        if (raw == Calendar.class) {
+            return new StdKeyDeserializer.CalendarKD();
+        }
+        
+        // then less common ones...
+        if (raw == Boolean.class) {
+            return new StdKeyDeserializer.BoolKD();
+        }
+        if (raw == Byte.class) {
+            return new StdKeyDeserializer.ByteKD();
+        }
+        if (raw == Character.class) {
+            return new StdKeyDeserializer.CharKD();
+        }
+        if (raw == Short.class) {
+            return new StdKeyDeserializer.ShortKD();
+        }
+        if (raw == Float.class) {
+            return new StdKeyDeserializer.FloatKD();
+        }
+        if (raw == Double.class) {
+            return new StdKeyDeserializer.DoubleKD();
+        }
+        if (raw == Locale.class) {
+            return new StdKeyDeserializer.LocaleKD();
+        }
+        return null;
+    }
+}
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
new file mode 100644
index 0000000..0e50b0e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdScalarDeserializer.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Base class for deserializers that handle types that are serialized
+ * as JSON scalars (non-structured, i.e. non-Object, non-Array, values).
+ */
+public abstract class StdScalarDeserializer<T> extends StdDeserializer<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    protected StdScalarDeserializer(Class<?> vc) {
+        super(vc);
+    }
+
+    protected StdScalarDeserializer(JavaType valueType) {
+        super(valueType);
+    }
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return typeDeserializer.deserializeTypedFromScalar(jp, ctxt);
+    }
+}
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
new file mode 100644
index 0000000..550ce52
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StdValueInstantiator.java
@@ -0,0 +1,444 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
+import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+
+/**
+ * Default {@link ValueInstantiator} implementation, which supports
+ * Creator methods that can be indicated by standard Jackson
+ * annotations.
+ */
+public class StdValueInstantiator
+    extends ValueInstantiator
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Type of values that are instantiated; used
+     * for error reporting purposes.
+     */
+    protected final String _valueTypeDesc;
+
+    /**
+     * Are we allowed to convert empty Strings to null objects?
+     */
+    protected final boolean _cfgEmptyStringsAsObjects;
+    
+    // // // Default (no-args) construction
+
+    /**
+     * Default (no-argument) constructor to use for instantiation
+     * (with {@link #createUsingDefault})
+     */
+    protected AnnotatedWithParams _defaultCreator;
+
+    // // // With-args (property-based) construction
+
+    protected AnnotatedWithParams _withArgsCreator;
+    protected CreatorProperty[] _constructorArguments;
+
+    // // // Delegate construction
+    
+    protected JavaType _delegateType;
+    protected AnnotatedWithParams _delegateCreator;
+    protected CreatorProperty[] _delegateArguments;
+    
+    // // // Scalar construction
+
+    protected AnnotatedWithParams _fromStringCreator;
+    protected AnnotatedWithParams _fromIntCreator;
+    protected AnnotatedWithParams _fromLongCreator;
+    protected AnnotatedWithParams _fromDoubleCreator;
+    protected AnnotatedWithParams _fromBooleanCreator;
+
+    // // // Incomplete creator
+    protected AnnotatedParameter  _incompleteParameter;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public StdValueInstantiator(DeserializationConfig config, Class<?> valueType)
+    {
+        _cfgEmptyStringsAsObjects = (config == null) ? false
+                : config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+        _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.getName();
+    }
+    
+    public StdValueInstantiator(DeserializationConfig config, JavaType valueType)
+    {
+        _cfgEmptyStringsAsObjects = (config == null) ? false
+                : config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+        _valueTypeDesc = (valueType == null) ? "UNKNOWN TYPE" : valueType.toString();
+    }
+
+    /**
+     * Copy-constructor that sub-classes can use when creating new instances
+     * by fluent-style construction
+     */
+    protected StdValueInstantiator(StdValueInstantiator src)
+    {
+        _cfgEmptyStringsAsObjects = src._cfgEmptyStringsAsObjects;
+        _valueTypeDesc = src._valueTypeDesc;
+
+        _defaultCreator = src._defaultCreator;
+
+        _constructorArguments = src._constructorArguments;
+        _withArgsCreator = src._withArgsCreator;
+
+        _delegateType = src._delegateType;
+        _delegateCreator = src._delegateCreator;
+        _delegateArguments = src._delegateArguments;
+        
+        _fromStringCreator = src._fromStringCreator;
+        _fromIntCreator = src._fromIntCreator;
+        _fromLongCreator = src._fromLongCreator;
+        _fromDoubleCreator = src._fromDoubleCreator;
+        _fromBooleanCreator = src._fromBooleanCreator;
+    }
+
+    /**
+     * Method for setting properties related to instantiating values
+     * from JSON Object. We will choose basically only one approach (out of possible
+     * three), and clear other properties
+     */
+    public void configureFromObjectSettings(AnnotatedWithParams defaultCreator,
+            AnnotatedWithParams delegateCreator, JavaType delegateType, CreatorProperty[] delegateArgs,
+            AnnotatedWithParams withArgsCreator, CreatorProperty[] constructorArgs)
+    {
+        _defaultCreator = defaultCreator;
+        _delegateCreator = delegateCreator;
+        _delegateType = delegateType;
+        _delegateArguments = delegateArgs;
+        _withArgsCreator = withArgsCreator;
+        _constructorArguments = constructorArgs;
+    }
+
+    public void configureFromStringCreator(AnnotatedWithParams creator) {
+        _fromStringCreator = creator;
+    }
+
+    public void configureFromIntCreator(AnnotatedWithParams creator) {
+        _fromIntCreator = creator;
+    }
+
+    public void configureFromLongCreator(AnnotatedWithParams creator) {
+        _fromLongCreator = creator;
+    }
+
+    public void configureFromDoubleCreator(AnnotatedWithParams creator) {
+        _fromDoubleCreator = creator;
+    }
+
+    public void configureFromBooleanCreator(AnnotatedWithParams creator) {
+        _fromBooleanCreator = creator;
+    }
+
+    public void configureIncompleteParameter(AnnotatedParameter parameter) {
+        _incompleteParameter = parameter;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API implementation; metadata
+    /**********************************************************
+     */
+
+    @Override
+    public String getValueTypeDesc() {
+        return _valueTypeDesc;
+    }
+    
+    @Override
+    public boolean canCreateFromString() {
+        return (_fromStringCreator != null);
+    }
+
+    @Override
+    public boolean canCreateFromInt() {
+        return (_fromIntCreator != null);
+    }
+
+    @Override
+    public boolean canCreateFromLong() {
+        return (_fromLongCreator != null);
+    }
+
+    @Override
+    public boolean canCreateFromDouble() {
+        return (_fromDoubleCreator != null);
+    }
+
+    @Override
+    public boolean canCreateFromBoolean() {
+        return (_fromBooleanCreator != null);
+    }
+    
+    @Override
+    public boolean canCreateUsingDefault() {
+        return (_defaultCreator != null);
+    }
+
+    @Override
+    public boolean canCreateUsingDelegate() {
+        return _delegateType != null;
+    }
+    
+    @Override
+    public boolean canCreateFromObjectWith() {
+        return (_withArgsCreator != null);
+    }
+
+    @Override
+    public JavaType getDelegateType(DeserializationConfig config) {
+        return _delegateType;
+    }
+
+    @Override
+    public SettableBeanProperty[] getFromObjectArguments(DeserializationConfig config) {
+        return _constructorArguments;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API implementation; instantiation from JSON Object
+    /**********************************************************
+     */
+    
+    @Override
+    public Object createUsingDefault(DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_defaultCreator == null) { // sanity-check; caller should check
+            throw new IllegalStateException("No default constructor for "+getValueTypeDesc());
+        }
+        try {
+            return _defaultCreator.call();
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        } catch (Exception e) {
+            throw wrapException(e);
+        }
+    }
+    
+    @Override
+    public Object createFromObjectWith(DeserializationContext ctxt, Object[] args)
+        throws IOException, JsonProcessingException
+    {
+        if (_withArgsCreator == null) { // sanity-check; caller should check
+            throw new IllegalStateException("No with-args constructor for "+getValueTypeDesc());
+        }
+        try {
+            return _withArgsCreator.call(args);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        } catch (Exception e) {
+            throw wrapException(e);
+        }
+    }
+
+    @Override
+    public Object createUsingDelegate(DeserializationContext ctxt, Object delegate)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateCreator == null) { // sanity-check; caller should check
+            throw new IllegalStateException("No delegate constructor for "+getValueTypeDesc());
+        }
+        try {
+            // First simple case: just delegate, no injectables
+            if (_delegateArguments == null) {
+                return _delegateCreator.call1(delegate);
+            }
+            // And then the case with at least one injectable...
+            final int len = _delegateArguments.length;
+            Object[] args = new Object[len];
+            for (int i = 0; i < len; ++i) {
+                CreatorProperty prop = _delegateArguments[i];
+                if (prop == null) { // delegate
+                    args[i] = delegate;
+                } else { // nope, injectable:
+                    args[i] = ctxt.findInjectableValue(prop.getInjectableValueId(), prop, null);
+                }
+            }
+            // and then try calling with full set of arguments
+            return _delegateCreator.call(args);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        } catch (Exception e) {
+            throw wrapException(e);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API implementation; instantiation from JSON scalars
+    /**********************************************************
+     */
+    
+    @Override
+    public Object createFromString(DeserializationContext ctxt, String value)
+            throws IOException, JsonProcessingException
+    {
+        if (_fromStringCreator != null) {
+            try {
+                return _fromStringCreator.call1(value);
+            } catch (Exception e) {
+                throw wrapException(e);
+            } catch (ExceptionInInitializerError e) {
+                throw wrapException(e);
+            }
+        }
+        return _createFromStringFallbacks(ctxt, value);
+    }
+    
+    @Override
+    public Object createFromInt(DeserializationContext ctxt, int value)
+            throws IOException, JsonProcessingException
+    {
+        try {
+            // First: "native" int methods work best:
+            if (_fromIntCreator != null) {
+                return _fromIntCreator.call1(Integer.valueOf(value));
+            }
+            // but if not, can do widening conversion
+            if (_fromLongCreator != null) {
+                return _fromLongCreator.call1(Long.valueOf(value));
+            }
+        } catch (Exception e) {
+            throw wrapException(e);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        }
+        throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc()
+                +" from Integral number; no single-int-arg constructor/factory method");
+    }
+
+    @Override
+    public Object createFromLong(DeserializationContext ctxt, long value)
+            throws IOException, JsonProcessingException
+    {
+        try {
+            if (_fromLongCreator != null) {
+                return _fromLongCreator.call1(Long.valueOf(value));
+            }
+        } catch (Exception e) {
+            throw wrapException(e);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        }
+        throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc()
+                +" from Long integral number; no single-long-arg constructor/factory method");
+    }
+
+    @Override
+    public Object createFromDouble(DeserializationContext ctxt, double value)
+            throws IOException, JsonProcessingException
+    {
+        try {
+            if (_fromDoubleCreator != null) {
+                return _fromDoubleCreator.call1(Double.valueOf(value));
+            }
+        } catch (Exception e) {
+            throw wrapException(e);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        }
+        throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc()
+                +" from Floating-point number; no one-double/Double-arg constructor/factory method");
+    }
+
+    @Override
+    public Object createFromBoolean(DeserializationContext ctxt, boolean value)
+            throws IOException, JsonProcessingException
+    {
+        try {
+            if (_fromBooleanCreator != null) {
+                return _fromBooleanCreator.call1(Boolean.valueOf(value));
+            }
+        } catch (Exception e) {
+            throw wrapException(e);
+        } catch (ExceptionInInitializerError e) {
+            throw wrapException(e);
+        }
+        throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc()
+                +" from Boolean value; no single-boolean/Boolean-arg constructor/factory method");
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API: configuration mutators, accessors
+    /**********************************************************
+     */
+
+    @Override
+    public AnnotatedWithParams getDelegateCreator() {
+        return _delegateCreator;
+    }
+
+    @Override
+    public AnnotatedWithParams getDefaultCreator() {
+        return _defaultCreator;
+    }
+
+    @Override
+    public AnnotatedWithParams getWithArgsCreator() {
+        return _withArgsCreator;
+    }
+
+    @Override
+    public AnnotatedParameter getIncompleteParameter() {
+        return _incompleteParameter;
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected Object _createFromStringFallbacks(DeserializationContext ctxt, String value)
+            throws IOException, JsonProcessingException
+    {
+        /* 28-Sep-2011, tatu: Ok this is not clean at all; but since there are legacy
+         *   systems that expect conversions in some cases, let's just add a minimal
+         *   patch (note: same could conceivably be used for numbers too).
+         */
+        if (_fromBooleanCreator != null) {
+            String str = value.trim();
+            if ("true".equals(str)) {
+                return createFromBoolean(ctxt, true);
+            }
+            if ("false".equals(str)) {
+                return createFromBoolean(ctxt, false);
+            }
+        }
+        
+        // and finally, empty Strings might be accepted as null Object...
+        if (_cfgEmptyStringsAsObjects && value.length() == 0) {
+            return null;
+        }
+        throw new JsonMappingException("Can not instantiate value of type "+getValueTypeDesc()
+                +" from String value; no single-String constructor/factory method");
+    }
+    
+    protected JsonMappingException wrapException(Throwable t)
+    {
+        while (t.getCause() != null) {
+            t = t.getCause();
+        }
+        if (t instanceof JsonMappingException) {
+            return (JsonMappingException) t;
+        }
+        return new JsonMappingException("Instantiation of "+getValueTypeDesc()+" value failed: "+t.getMessage(), t);
+    }
+}
+
+
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
new file mode 100644
index 0000000..3080a50
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringArrayDeserializer.java
@@ -0,0 +1,162 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+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.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ObjectBuffer;
+
+/**
+ * Separate implementation for serializing String arrays (instead of
+ * using {@link ObjectArrayDeserializer}.
+ * Used if (and only if) no custom value deserializers are used.
+ */
+ at JacksonStdImpl
+public final class StringArrayDeserializer
+    extends StdDeserializer<String[]>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = -7589512013334920693L;
+
+    public final static StringArrayDeserializer instance = new StringArrayDeserializer();
+    
+    /**
+     * Value serializer to use, if not the standard one (which is inlined)
+     */
+    protected JsonDeserializer<String> _elementDeserializer;
+
+    public StringArrayDeserializer() {
+        super(String[].class);
+        _elementDeserializer = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected StringArrayDeserializer(JsonDeserializer<?> deser) {
+        super(String[].class);
+        _elementDeserializer = (JsonDeserializer<String>) deser;
+    }
+   
+    @Override
+    public String[] deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_ARRAY (or equivalent)
+        if (!jp.isExpectedStartArrayToken()) {
+            return handleNonArray(jp, ctxt);
+        }
+        if (_elementDeserializer != null) {
+            return _deserializeCustom(jp, ctxt);
+        }
+
+        final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        Object[] chunk = buffer.resetAndStart();
+        
+        int ix = 0;
+        JsonToken t;
+        
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            // Ok: no need to convert Strings, but must recognize nulls
+            String value;
+            if (t == JsonToken.VALUE_STRING) {
+                value = jp.getText();
+            } else if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else {
+                value = _parseString(jp, ctxt);
+            }
+            if (ix >= chunk.length) {
+                chunk = buffer.appendCompletedChunk(chunk);
+                ix = 0;
+            }
+            chunk[ix++] = value;
+        }
+        String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
+        ctxt.returnObjectBuffer(buffer);
+        return result;
+    }
+
+    /**
+     * Offlined version used when we do not use the default deserialization method.
+     */
+    protected final String[] _deserializeCustom(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        Object[] chunk = buffer.resetAndStart();
+        final JsonDeserializer<String> deser = _elementDeserializer;
+        
+        int ix = 0;
+        JsonToken t;
+        
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            // Ok: no need to convert Strings, but must recognize nulls
+            String value = (t == JsonToken.VALUE_NULL) ? null : deser.deserialize(jp, ctxt);
+            if (ix >= chunk.length) {
+                chunk = buffer.appendCompletedChunk(chunk);
+                ix = 0;
+            }
+            chunk[ix++] = value;
+        }
+        String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
+        ctxt.returnObjectBuffer(buffer);
+        return result;
+    }
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+        TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+
+    private final String[] handleNonArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-526]: implicit arrays from single values?
+        if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+            // [JACKSON-620] Empty String can become null...
+            if ((jp.getCurrentToken() == JsonToken.VALUE_STRING)
+                    && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
+                String str = jp.getText();
+                if (str.length() == 0) {
+                    return null;
+                }
+            }
+            throw ctxt.mappingException(_valueClass);
+        }
+        return new String[] { (jp.getCurrentToken() == JsonToken.VALUE_NULL) ? null : _parseString(jp, ctxt) };
+    }
+
+    /**
+     * Contextualization is needed to see whether we can "inline" deserialization
+     * of String values, or if we have to use separate value deserializer.
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<?> deser = _elementDeserializer;
+        // #125: May have a content converter
+        deser = findConvertingContentDeserializer(ctxt, property, deser);
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(ctxt.constructType(String.class), property);
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (deser instanceof ContextualDeserializer) {
+                deser = ((ContextualDeserializer) deser).createContextual(ctxt, property);
+            }
+        }
+        // Ok ok: if all we got is the default String deserializer, can just forget about it
+        if (deser != null && this.isDefaultDeserializer(deser)) {
+            deser = null;
+        }
+        if (_elementDeserializer != deser) {
+            return new StringArrayDeserializer(deser);
+        }
+        return this;
+    }
+}
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
new file mode 100644
index 0000000..881d5c0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringCollectionDeserializer.java
@@ -0,0 +1,234 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.Collection;
+
+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.ValueInstantiator;
+import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+/**
+ * Specifically optimized version for {@link java.util.Collection}s
+ * that contain String values; reason is that this is a very common
+ * type and we can make use of the fact that Strings are final.
+ */
+ at JacksonStdImpl
+public final class StringCollectionDeserializer
+    extends ContainerDeserializerBase<Collection<String>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    // // Configuration
+
+    protected final JavaType _collectionType;
+    
+    /**
+     * Value deserializer to use, if NOT the standard one
+     * (if it is, will be null).
+     */
+    protected final JsonDeserializer<String> _valueDeserializer;
+
+    // // Instance construction settings:
+    
+    /**
+     * Instantiator used in case custom handling is needed for creation.
+     */
+    protected final ValueInstantiator _valueInstantiator;
+
+    /**
+     * Deserializer that is used iff delegate-based creator is
+     * to be used for deserializing from JSON Object.
+     */
+    protected final JsonDeserializer<Object> _delegateDeserializer;
+
+    // NOTE: no PropertyBasedCreator, as JSON Arrays have no properties
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public StringCollectionDeserializer(JavaType collectionType,
+            JsonDeserializer<?> valueDeser, ValueInstantiator valueInstantiator)
+    {
+        this(collectionType, valueInstantiator, null, valueDeser);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected StringCollectionDeserializer(JavaType collectionType,
+            ValueInstantiator valueInstantiator, JsonDeserializer<?> delegateDeser,
+            JsonDeserializer<?> valueDeser)
+    {
+        super(collectionType.getRawClass());
+        _collectionType = collectionType;
+        _valueDeserializer = (JsonDeserializer<String>) valueDeser;
+        _valueInstantiator = valueInstantiator;
+        _delegateDeserializer = (JsonDeserializer<Object>) delegateDeser;
+    }
+
+    protected StringCollectionDeserializer withResolved(JsonDeserializer<?> delegateDeser,
+            JsonDeserializer<?> valueDeser)
+    {
+        if ((_valueDeserializer == valueDeser) && (_delegateDeserializer == delegateDeser)) {
+            return this;
+        }
+        return new StringCollectionDeserializer(_collectionType,
+                _valueInstantiator, delegateDeser, valueDeser);
+    }
+    
+    /*
+    /**********************************************************
+    /* Validation, post-processing
+    /**********************************************************
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        // May need to resolve types for delegate-based creators:
+        JsonDeserializer<Object> delegate = null;
+        if (_valueInstantiator != null) {
+            AnnotatedWithParams delegateCreator = _valueInstantiator.getDelegateCreator();
+            if (delegateCreator != null) {
+                JavaType delegateType = _valueInstantiator.getDelegateType(ctxt.getConfig());
+                delegate = findDeserializer(ctxt, delegateType, property);
+            }
+        }
+        JsonDeserializer<?> valueDeser = _valueDeserializer;
+        if (valueDeser == null) {
+            // #125: May have a content converter
+            valueDeser = findConvertingContentDeserializer(ctxt, property, valueDeser);
+            if (valueDeser == null) {
+            // And we may also need to get deserializer for String
+                valueDeser = ctxt.findContextualValueDeserializer( _collectionType.getContentType(), property);
+            }
+        } else { // if directly assigned, probably not yet contextual, so:
+            if (valueDeser instanceof ContextualDeserializer) {
+                valueDeser = ((ContextualDeserializer) valueDeser).createContextual(ctxt, property);
+            }
+        } 
+        if (isDefaultDeserializer(valueDeser)) {
+            valueDeser = null;
+        }
+        return withResolved(delegate, valueDeser);
+    }
+    
+    /*
+    /**********************************************************
+    /* ContainerDeserializerBase API
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return _collectionType.getContentType();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public JsonDeserializer<Object> getContentDeserializer() {
+        JsonDeserializer<?> deser = _valueDeserializer;
+        return (JsonDeserializer<Object>) deser;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonDeserializer API
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    public Collection<String> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (_delegateDeserializer != null) {
+            return (Collection<String>) _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        final Collection<String> result = (Collection<String>) _valueInstantiator.createUsingDefault(ctxt);
+        return deserialize(jp, ctxt, result);
+    }
+
+    @Override
+    public Collection<String> deserialize(JsonParser jp, DeserializationContext ctxt,
+                                          Collection<String> result)
+        throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_ARRAY
+        if (!jp.isExpectedStartArrayToken()) {
+            return handleNonArray(jp, ctxt, result);
+        }
+
+        if (_valueDeserializer != null) {
+            return deserializeUsingCustom(jp, ctxt, result, _valueDeserializer);
+        }
+        JsonToken t;
+
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            result.add((t == JsonToken.VALUE_NULL) ? null : _parseString(jp, ctxt));
+        }
+        return result;
+    }
+    
+    private Collection<String> deserializeUsingCustom(JsonParser jp, DeserializationContext ctxt,
+            Collection<String> result, final JsonDeserializer<String> deser)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t;
+        while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
+            String value;
+
+            if (t == JsonToken.VALUE_NULL) {
+                value = null;
+            } else {
+                value = deser.deserialize(jp, ctxt);
+            }
+            result.add(value);
+        }
+        return result;
+    }
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        // In future could check current token... for now this should be enough:
+        return typeDeserializer.deserializeTypedFromArray(jp, ctxt);
+    }
+
+    /**
+     * Helper method called when current token is no START_ARRAY. Will either
+     * throw an exception, or try to handle value as if member of implicit
+     * array, depending on configuration.
+     */
+    private final Collection<String> handleNonArray(JsonParser jp, DeserializationContext ctxt,
+            Collection<String> result)
+        throws IOException, JsonProcessingException
+    {
+        // [JACKSON-526]: implicit arrays from single values?
+        if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+            throw ctxt.mappingException(_collectionType.getRawClass());
+        }
+        // Strings are one of "native" (intrinsic) types, so there's never type deserializer involved
+        JsonDeserializer<String> valueDes = _valueDeserializer;
+        JsonToken t = jp.getCurrentToken();
+
+        String value;
+        
+        if (t == JsonToken.VALUE_NULL) {
+            value = null;
+        } else {
+            value = (valueDes == null) ? _parseString(jp, ctxt) : valueDes.deserialize(jp, ctxt);
+        }
+        result.add(value);
+        return result;
+    }
+}
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
new file mode 100644
index 0000000..fe0070e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/StringDeserializer.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+ at JacksonStdImpl
+public final class StringDeserializer
+    extends StdScalarDeserializer<String>
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * @since 2.2
+     */
+    public final static StringDeserializer instance = new StringDeserializer();
+    
+    public StringDeserializer() { super(String.class); }
+
+    @Override
+    public String deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // 22-Sep-2012, tatu: For 2.1, use this new method, may force coercion:
+        String text = jp.getValueAsString();
+        if (text != null) {
+            return text;
+        }
+        // [JACKSON-330]: need to gracefully handle byte[] data, as base64
+        JsonToken curr = jp.getCurrentToken();
+        if (curr == JsonToken.VALUE_EMBEDDED_OBJECT) {
+            Object ob = jp.getEmbeddedObject();
+            if (ob == null) {
+                return null;
+            }
+            if (ob instanceof byte[]) {
+                return Base64Variants.getDefaultVariant().encode((byte[]) ob, false);
+            }
+            // otherwise, try conversion using toString()...
+            return ob.toString();
+        }
+        throw ctxt.mappingException(_valueClass, curr);
+    }
+
+    // 1.6: 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 jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return deserialize(jp, ctxt);
+    }
+}
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
new file mode 100644
index 0000000..c7faa5c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/ThrowableDeserializer.java
@@ -0,0 +1,164 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.BeanDeserializer;
+import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Deserializer that builds on basic {@link BeanDeserializer} but
+ * override some aspects like instance construction.
+ */
+public class ThrowableDeserializer
+    extends BeanDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final static String PROP_NAME_MESSAGE = "message";
+
+    /*
+    /************************************************************
+    /* Construction
+    /************************************************************
+     */
+
+    public ThrowableDeserializer(BeanDeserializer baseDeserializer)
+    {
+        super(baseDeserializer);
+        // need to disable this, since we do post-processing
+        _vanillaProcessing = false;
+    }
+
+    /**
+     * Alternative constructor used when creating "unwrapping" deserializers
+     */
+    protected ThrowableDeserializer(BeanDeserializer src, NameTransformer unwrapper)
+    {
+        super(src, unwrapper);
+    }
+    
+    @Override
+    public JsonDeserializer<Object> unwrappingDeserializer(NameTransformer unwrapper)
+    {
+        if (getClass() != ThrowableDeserializer.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 ThrowableDeserializer(this, unwrapper);
+    }
+
+    
+    /*
+    /************************************************************
+    /* Overridden methods
+    /************************************************************
+     */
+
+    @Override
+    public Object deserializeFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // 30-Sep-2010, tatu: Need to allow use of @JsonCreator, so:
+        if (_propertyBasedCreator != null) { // proper @JsonCreator
+            return _deserializeUsingPropertyBased(jp, ctxt);
+        }
+        if (_delegateDeserializer != null) {
+            return _valueInstantiator.createUsingDelegate(ctxt,
+                    _delegateDeserializer.deserialize(jp, ctxt));
+        }
+        if (_beanType.isAbstract()) { // for good measure, check this too
+            throw JsonMappingException.from(jp, "Can not instantiate abstract type "+_beanType
+                    +" (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) {
+            throw new JsonMappingException("Can not deserialize Throwable of type "+_beanType
+                    +" without having a default contructor, a single-String-arg constructor; or explicit @JsonCreator");
+        }
+        
+        Object throwable = null;
+        Object[] pending = null;
+        int pendingIx = 0;
+
+        for (; jp.getCurrentToken() != JsonToken.END_OBJECT; jp.nextToken()) {
+            String propName = jp.getCurrentName();
+            SettableBeanProperty prop = _beanProperties.find(propName);
+            jp.nextToken(); // to point to field value
+
+            if (prop != null) { // normal case
+                if (throwable != null) {
+                    prop.deserializeAndSet(jp, ctxt, throwable);
+                    continue;
+                }
+                // nope; need to defer
+                if (pending == null) {
+                    int len = _beanProperties.size();
+                    pending = new Object[len + len];
+                }
+                pending[pendingIx++] = prop;
+                pending[pendingIx++] = prop.deserialize(jp, ctxt);
+                continue;
+            }
+
+            // Maybe it's "message"?
+            if (PROP_NAME_MESSAGE.equals(propName)) {
+                if (hasStringCreator) {
+                    throwable = _valueInstantiator.createFromString(ctxt, jp.getText());
+                    // any pending values?
+                    if (pending != null) {
+                        for (int i = 0, len = pendingIx; i < len; i += 2) {
+                            prop = (SettableBeanProperty)pending[i];
+                            prop.set(throwable, pending[i+1]);
+                        }
+                        pending = null;
+                    }
+                    continue;
+                }
+            }
+            /* As per [JACKSON-313], things marked as ignorable should not be
+             * passed to any setter
+             */
+            if (_ignorableProps != null && _ignorableProps.contains(propName)) {
+                jp.skipChildren();
+                continue;
+            }
+            if (_anySetter != null) {
+                _anySetter.deserializeAndSet(jp, ctxt, throwable, propName);
+                continue;
+            }
+            // Unknown: let's call handler method
+            handleUnknownProperty(jp, ctxt, throwable, propName);
+        }
+        // Sanity check: did we find "message"?
+        if (throwable == null) {
+            /* 15-Oct-2010, tatu: Can't assume missing message is an error, since it may be
+             *   suppressed during serialization, as per [JACKSON-388].
+             *   
+             *   Should probably allow use of default constructor, too...
+             */
+            //throw new JsonMappingException("No 'message' property found: could not deserialize "+_beanType);
+            if (hasStringCreator) {
+                throwable = _valueInstantiator.createFromString(ctxt, null);
+            } else {
+                throwable = _valueInstantiator.createUsingDefault(ctxt);
+            }
+            // any pending values?
+            if (pending != null) {
+                for (int i = 0, len = pendingIx; i < len; i += 2) {
+                    SettableBeanProperty prop = (SettableBeanProperty)pending[i];
+                    prop.set(throwable, pending[i+1]);
+                }
+            }
+        }
+        return throwable;
+    }
+}
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
new file mode 100644
index 0000000..b472dd0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/UntypedObjectDeserializer.java
@@ -0,0 +1,253 @@
+package com.fasterxml.jackson.databind.deser.std;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.util.ObjectBuffer;
+
+/**
+ * Deserializer implementation that is used if it is necessary to bind content of
+ * "unknown" type; something declared as basic {@link java.lang.Object}
+ * (either explicitly, or due to type erasure).
+ * If so, "natural" mapping is used to convert JSON values to their natural
+ * Java object matches: JSON arrays to Java {@link java.util.List}s (or, if configured,
+ * Object[]), JSON objects to {@link java.util.Map}s, numbers to
+ * {@link java.lang.Number}s, booleans to {@link java.lang.Boolean}s and
+ * strings to {@link java.lang.String} (and nulls to nulls).
+ */
+ at JacksonStdImpl
+public class UntypedObjectDeserializer
+    extends StdDeserializer<Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    private final static Object[] NO_OBJECTS = new Object[0];
+
+    /**
+     * @since 2.2
+     */
+    public final static UntypedObjectDeserializer instance = new UntypedObjectDeserializer();
+    
+    public UntypedObjectDeserializer() { super(Object.class); }
+
+    /*
+    /**********************************************************
+    /* Deserializer API
+    /**********************************************************
+     */
+    
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_OBJECT:
+            return mapObject(jp, ctxt);
+        case START_ARRAY:
+            return mapArray(jp, ctxt);
+        case FIELD_NAME:
+            return mapObject(jp, ctxt);
+        case VALUE_EMBEDDED_OBJECT:
+            return jp.getEmbeddedObject();
+        case VALUE_STRING:
+            return jp.getText();
+
+        case VALUE_NUMBER_INT:
+            /* [JACKSON-100]: caller may want to get all integral values
+             * returned as BigInteger, for consistency
+             */
+            if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
+                return jp.getBigIntegerValue(); // should be optimal, whatever it is
+            }
+            return jp.getNumberValue(); // should be optimal, whatever it is
+
+        case VALUE_NUMBER_FLOAT:
+            /* [JACKSON-72]: need to allow overriding the behavior regarding
+             *   which type to use
+             */
+            if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                return jp.getDecimalValue();
+            }
+            return Double.valueOf(jp.getDoubleValue());
+
+        case VALUE_TRUE:
+            return Boolean.TRUE;
+        case VALUE_FALSE:
+            return Boolean.FALSE;
+
+        case VALUE_NULL: // should not get this but...
+            return null;
+
+        case END_ARRAY: // invalid
+        case END_OBJECT: // invalid
+        default:
+            throw ctxt.mappingException(Object.class);
+        }
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        switch (t) {
+        // First: does it look like we had type id wrapping of some kind?
+        case START_ARRAY:
+        case START_OBJECT:
+        case FIELD_NAME:
+            /* Output can be as JSON Object, Array or scalar: no way to know
+             * a this point:
+             */
+            return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+
+        /* Otherwise we probably got a "native" type (ones that map
+         * naturally and thus do not need or use type ids)
+         */
+        case VALUE_STRING:
+            return jp.getText();
+
+        case VALUE_NUMBER_INT:
+            // For [JACKSON-100], see above:
+            if (ctxt.isEnabled(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS)) {
+                return jp.getBigIntegerValue();
+            }
+            /* and as per [JACKSON-839], allow "upgrade" to bigger types: out-of-range
+             * entries can not be produced without type, so this should "just work",
+             * even if it is bit unclean
+             */
+            return jp.getNumberValue();
+
+        case VALUE_NUMBER_FLOAT:
+            // For [JACKSON-72], see above
+            if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
+                return jp.getDecimalValue();
+            }
+            return Double.valueOf(jp.getDoubleValue());
+
+        case VALUE_TRUE:
+            return Boolean.TRUE;
+        case VALUE_FALSE:
+            return Boolean.FALSE;
+        case VALUE_EMBEDDED_OBJECT:
+            return jp.getEmbeddedObject();
+
+        case VALUE_NULL: // should not get this far really but...
+            return null;
+        default:
+            throw ctxt.mappingException(Object.class);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to map a JSON Array into a Java value.
+     */
+    protected Object mapArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (ctxt.isEnabled(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)) {
+            return mapArrayToArray(jp, ctxt);
+        }
+        // Minor optimization to handle small lists (default size for ArrayList is 10)
+        if (jp.nextToken()  == JsonToken.END_ARRAY) {
+            return new ArrayList<Object>(4);
+        }
+        ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        Object[] values = buffer.resetAndStart();
+        int ptr = 0;
+        int totalSize = 0;
+        do {
+            Object value = deserialize(jp, ctxt);
+            ++totalSize;
+            if (ptr >= values.length) {
+                values = buffer.appendCompletedChunk(values);
+                ptr = 0;
+            }
+            values[ptr++] = value;
+        } while (jp.nextToken() != JsonToken.END_ARRAY);
+        // let's create almost full array, with 1/8 slack
+        ArrayList<Object> result = new ArrayList<Object>(totalSize + (totalSize >> 3) + 1);
+        buffer.completeAndClearBuffer(values, ptr, result);
+        return result;
+    }
+
+    /**
+     * Method called to map a JSON Object into a Java value.
+     */
+    protected Object mapObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        }
+        // 1.6: minor optimization; let's handle 1 and 2 entry cases separately
+        if (t != JsonToken.FIELD_NAME) { // and empty one too
+            // empty map might work; but caller may want to modify... so better just give small modifiable
+            return new LinkedHashMap<String,Object>(4);
+        }
+        String field1 = jp.getText();
+        jp.nextToken();
+        Object value1 = deserialize(jp, ctxt);
+        if (jp.nextToken() != JsonToken.FIELD_NAME) { // single entry; but we want modifiable
+            LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
+            result.put(field1, value1);
+            return result;
+        }
+        String field2 = jp.getText();
+        jp.nextToken();
+        Object value2 = deserialize(jp, ctxt);
+        if (jp.nextToken() != JsonToken.FIELD_NAME) {
+            LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4);
+            result.put(field1, value1);
+            result.put(field2, value2);
+            return result;
+        }
+        // And then the general case; default map size is 16
+        LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>();
+        result.put(field1, value1);
+        result.put(field2, value2);
+        do {
+            String fieldName = jp.getText();
+            jp.nextToken();
+            result.put(fieldName, deserialize(jp, ctxt));
+        } while (jp.nextToken() != JsonToken.END_OBJECT);
+        return result;
+    }
+
+    /**
+     * Method called to map a JSON Array into a Java Object array (Object[]).
+     */
+    protected Object[] mapArrayToArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // Minor optimization to handle small lists (default size for ArrayList is 10)
+        if (jp.nextToken()  == JsonToken.END_ARRAY) {
+            return NO_OBJECTS;
+        }
+        ObjectBuffer buffer = ctxt.leaseObjectBuffer();
+        Object[] values = buffer.resetAndStart();
+        int ptr = 0;
+        do {
+            Object value = deserialize(jp, ctxt);
+            if (ptr >= values.length) {
+                values = buffer.appendCompletedChunk(values);
+                ptr = 0;
+            }
+            values[ptr++] = value;
+        } while (jp.nextToken() != JsonToken.END_ARRAY);
+        return buffer.completeAndClearBuffer(values, ptr);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/deser/std/package-info.java b/src/main/java/com/fasterxml/jackson/databind/deser/std/package-info.java
new file mode 100644
index 0000000..0ac0149
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/deser/std/package-info.java
@@ -0,0 +1,13 @@
+/**
+ * Contains public standard implementations of abstraction that
+ * Jackson uses. This means that they are not merely implementation
+ * details, but part of semi-public interface where project
+ * tries to maintain backwards compatibility at higher level
+ * than for 'impl' types (although less so than with fully
+ * public interfaces).
+ *<p>
+ * Note that since this package was only added relatively late
+ * in development cycle, not all classes that belong here are
+ * included. Plan is to move more classes over time.
+ */
+package com.fasterxml.jackson.databind.deser.std;
diff --git a/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java
new file mode 100644
index 0000000..b0ba8f2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/InvalidFormatException.java
@@ -0,0 +1,84 @@
+package com.fasterxml.jackson.databind.exc;
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Specialized sub-class of {@link JsonMappingException}
+ * 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
+{
+    private static final long serialVersionUID = 1L; // silly Eclipse, warnings
+
+    /**
+     * Underlying value that could not be deserialized into
+     * target type, if available.
+     */
+    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
+    /**********************************************************
+     */
+    
+    public InvalidFormatException(String msg,
+            Object value, Class<?> targetType)
+    {
+        super(msg);
+        _value = value;
+        _targetType = targetType;
+    }
+
+    public InvalidFormatException(String msg, JsonLocation loc,
+            Object value, Class<?> targetType)
+    {
+        super(msg, loc);
+        _value = value;
+        _targetType = targetType;
+    }
+    
+    public static InvalidFormatException from(JsonParser jp, String msg,
+            Object value, Class<?> targetType)
+    {
+        return new InvalidFormatException(msg, jp.getTokenLocation(),
+                value, targetType);
+    }
+
+    /*
+    /**********************************************************
+    /* Additional accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for checking source value (String, Number usually) that could not
+     * be deserialized into target type ({@link #getTargetType}).
+     * Note that value may not be available, depending on who throws the exception
+     * and when.
+     */
+    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/UnrecognizedPropertyException.java b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java
new file mode 100644
index 0000000..2f964d6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/exc/UnrecognizedPropertyException.java
@@ -0,0 +1,155 @@
+package com.fasterxml.jackson.databind.exc;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.JsonLocation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Specialized {@link JsonMappingException} sub-class specifically used
+ * to indicate problems due to encountering a JSON property that could
+ * not be mapped to an Object property (via getter, constructor argument
+ * or field).
+ */
+public class UnrecognizedPropertyException
+    extends JsonMappingException
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Class that does not contain mapping for the unrecognized property.
+     */
+    protected final Class<?> _referringClass;
+    
+    /**
+     *<p>
+     * Note: redundant information since it is also included in the
+     * reference path.
+     */
+    protected final String _unrecognizedPropertyName;
+    
+    /**
+     * Set of ids of properties that are known for the type, if this
+     * can be statically determined.
+     */
+    protected final Collection<Object> _propertyIds;
+
+    /**
+     * Lazily constructed description of known properties, used for
+     * constructing actual message if and as needed.
+     */
+    protected transient String _propertiesAsString;
+    
+    public UnrecognizedPropertyException(String msg, JsonLocation loc,
+            Class<?> referringClass, String propName,
+            Collection<Object> propertyIds)
+    {
+        
+        super(msg, loc);
+        _referringClass = referringClass;
+        _unrecognizedPropertyName = propName;
+        _propertyIds = propertyIds;
+    }
+
+    /**
+     * Factory method used for constructing instances of this exception type.
+     * 
+     * @param jp Underlying parser used for reading input being used for data-binding
+     * @param fromObjectOrClass Reference to either instance of problematic type (
+     *    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.
+     */
+    public static UnrecognizedPropertyException from(JsonParser jp,
+            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";
+        UnrecognizedPropertyException e = new UnrecognizedPropertyException(msg,
+                jp.getCurrentLocation(), ref, propertyName, propertyIds);
+        // but let's also ensure path includes this last (missing) segment
+        e.prependPath(fromObjectOrClass, propertyName);
+        return e;
+    }
+
+    /*
+    /**********************************************************
+    /* Overrides
+    /**********************************************************
+     */
+
+    private final static int MAX_DESC_LENGTH = 200;
+    
+    @Override
+    public String getMessageSuffix()
+    {
+        String suffix = _propertiesAsString;
+        if (suffix == null && _propertyIds != null) {
+            StringBuilder sb = new StringBuilder(100);
+            int len = _propertyIds.size();
+            if (len == 1) {
+                sb.append(" (one known property: \"");
+                sb.append(String.valueOf(_propertyIds.iterator().next()));
+                sb.append('"');
+            } else {
+                sb.append(" (").append(len).append(" known properties: ");
+                Iterator<Object> it = _propertyIds.iterator();
+                while (it.hasNext()) {
+                    sb.append(", \"");
+                    sb.append(String.valueOf(it.next()));
+                    sb.append('"');
+                    // one other thing: limit max length
+                    if (sb.length() > MAX_DESC_LENGTH) {
+                        sb.append(" [truncated]");
+                        break;
+                    }
+                }
+            }
+            sb.append("])");
+            _propertiesAsString = suffix = sb.toString();
+        }
+        return suffix;
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing type (class) that is missing definition to allow
+     * binding of the unrecognized property.
+     */
+    public Class<?> getReferringClass() {
+        return _referringClass;
+    }
+    
+    /**
+     * Convenience method for accessing logical property name that could
+     * not be mapped. Note that it is the last path reference in the
+     * underlying path.
+     */
+    public String getUnrecognizedPropertyName() {
+        return _unrecognizedPropertyName;
+    }    
+    
+    public Collection<Object> getKnownPropertyIds()
+    {
+        if (_propertyIds == null) {
+            return null;
+        }
+        return Collections.unmodifiableCollection(_propertyIds);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java
new file mode 100644
index 0000000..0d46e31
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLDeserializers.java
@@ -0,0 +1,120 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.IOException;
+import java.util.*;
+
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.Deserializers;
+import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+
+/**
+ * Container deserializers that handle "core" XML types: ones included in standard
+ * JDK 1.5. Types are directly needed by JAXB, but may be unavailable on some
+ * limited platforms; hence separate out from basic deserializer factory.
+ */
+public class CoreXMLDeserializers
+    extends Deserializers.Base
+{
+    /**
+     * Data type factories are thread-safe after instantiation (and
+     * configuration, if any); and since instantion (esp. implementation
+     * introspection) can be expensive we better reuse the instance.
+     */
+    final static DatatypeFactory _dataTypeFactory;
+    static {
+        try {
+            _dataTypeFactory = DatatypeFactory.newInstance();
+        } catch (DatatypeConfigurationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public JsonDeserializer<?> findBeanDeserializer(JavaType type,
+        DeserializationConfig config, BeanDescription beanDesc)
+    {
+        Class<?> raw = type.getRawClass();
+        if (raw == QName.class) {
+            return QNameDeserializer.instance;
+        }
+        if (raw == XMLGregorianCalendar.class) {
+            return GregorianCalendarDeserializer.instance;
+        }
+        if (raw == Duration.class) {
+            return DurationDeserializer.instance;
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Concrete deserializers
+    /**********************************************************
+     */
+
+    public static class DurationDeserializer
+        extends FromStringDeserializer<Duration>
+    {
+        private static final long serialVersionUID = 1L;
+        public final static DurationDeserializer instance = new DurationDeserializer();
+        public DurationDeserializer() { super(Duration.class); }
+    
+        @Override
+        protected Duration _deserialize(String value, DeserializationContext ctxt)
+            throws IllegalArgumentException
+        {
+            return _dataTypeFactory.newDuration(value);
+        }
+    }
+
+    public static class GregorianCalendarDeserializer
+        extends StdScalarDeserializer<XMLGregorianCalendar>
+    {
+        private static final long serialVersionUID = 1L;
+        public final static GregorianCalendarDeserializer instance = new GregorianCalendarDeserializer();
+        public GregorianCalendarDeserializer() { super(XMLGregorianCalendar.class); }
+        
+        @Override
+        public XMLGregorianCalendar deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            Date d = _parseDate(jp, ctxt);
+            if (d == null) {
+                return null;
+            }
+            GregorianCalendar calendar = new GregorianCalendar();
+            calendar.setTime(d);
+            TimeZone tz = ctxt.getTimeZone();
+            if (tz != null) {
+                calendar.setTimeZone(tz);
+            }
+            return _dataTypeFactory.newXMLGregorianCalendar(calendar);
+        }
+    }
+
+    public static class QNameDeserializer
+        extends FromStringDeserializer<QName>
+    {
+        private static final long serialVersionUID = 1L;
+        public final static QNameDeserializer instance = new QNameDeserializer();
+        
+        
+        public QNameDeserializer() { super(QName.class); }
+        
+        @Override
+        protected QName _deserialize(String value, DeserializationContext ctxt)
+            throws IllegalArgumentException
+        {
+            return QName.valueOf(value);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java
new file mode 100644
index 0000000..2b2e57c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/CoreXMLSerializers.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.ser.std.CalendarSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+/**
+ * Provider for serializers of XML types that are part of full JDK 1.5, but
+ * that some alleged 1.5 platforms are missing (Android, GAE).
+ * And for this reason these are added using more dynamic mechanism.
+ *<p>
+ * Note: since many of classes defined are abstract, caller must take
+ * care not to just use straight equivalency check but rather consider
+ * subclassing as well.
+ */
+public class CoreXMLSerializers extends Serializers.Base
+{
+    @Override
+    public JsonSerializer<?> findSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc)
+    {
+        Class<?> raw = type.getRawClass();
+        if (Duration.class.isAssignableFrom(raw) || QName.class.isAssignableFrom(raw)) {
+            return ToStringSerializer.instance;
+        }
+        if (XMLGregorianCalendar.class.isAssignableFrom(raw)) {
+            return XMLGregorianCalendarSerializer.instance;
+        }
+        return null;
+    }
+
+    public static class XMLGregorianCalendarSerializer extends StdSerializer<XMLGregorianCalendar>
+    {
+        public final static XMLGregorianCalendarSerializer instance = new XMLGregorianCalendarSerializer();
+        
+        public XMLGregorianCalendarSerializer() {
+            super(XMLGregorianCalendar.class);
+        }
+
+        @Override
+        public void serialize(XMLGregorianCalendar value, JsonGenerator jgen, SerializerProvider provider)
+                throws IOException, JsonGenerationException {
+            CalendarSerializer.instance.serialize(value.toGregorianCalendar(), jgen, provider);
+        }
+        
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
+            return CalendarSerializer.instance.getSchema(provider, typeHint);
+        }
+
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            CalendarSerializer.instance.acceptJsonFormatVisitor(visitor, null);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java
new file mode 100644
index 0000000..206b37c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/DOMDeserializer.java
@@ -0,0 +1,69 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
+
+/**
+ * Base for serializers that allows parsing DOM Documents from JSON Strings.
+ * Nominal type can be either {@link org.w3c.dom.Node} or
+ * {@link org.w3c.dom.Document}.
+ */
+public abstract class DOMDeserializer<T> extends FromStringDeserializer<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    private final static DocumentBuilderFactory _parserFactory;
+    static {
+        _parserFactory = DocumentBuilderFactory.newInstance();
+        // yup, only cave men do XML without recognizing namespaces...
+        _parserFactory.setNamespaceAware(true);
+    }
+
+    protected DOMDeserializer(Class<T> cls) { super(cls); }
+
+    @Override
+    public abstract T _deserialize(String value, DeserializationContext ctxt);
+
+    protected final Document parse(String value) throws IllegalArgumentException
+    {
+        try {
+            return _parserFactory.newDocumentBuilder().parse(new InputSource(new StringReader(value)));
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Failed to parse JSON String as XML: "+e.getMessage(), e);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Concrete deserializers
+    /**********************************************************
+     */
+    
+    public static class NodeDeserializer extends DOMDeserializer<Node>
+    {
+        private static final long serialVersionUID = 1L;
+        public NodeDeserializer() { super(Node.class); }
+        @Override
+        public Node _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException {
+            return parse(value);
+        }
+    }    
+
+    public static class DocumentDeserializer extends DOMDeserializer<Document>
+    {
+        private static final long serialVersionUID = 1L;
+        public DocumentDeserializer() { super(Document.class); }
+        @Override
+        public Document _deserialize(String value, DeserializationContext ctxt) throws IllegalArgumentException {
+            return parse(value);
+        }
+    }    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java
new file mode 100644
index 0000000..032d708
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/DOMSerializer.java
@@ -0,0 +1,57 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.IOException;
+import org.w3c.dom.Node;
+import  org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import  org.w3c.dom.ls.DOMImplementationLS;
+import  org.w3c.dom.ls.LSSerializer;
+
+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.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+public class DOMSerializer
+    extends StdSerializer<Node>
+{
+    protected final DOMImplementationLS _domImpl;
+
+    public DOMSerializer()
+    {
+        super(Node.class);
+        DOMImplementationRegistry registry;
+        try {
+            registry = DOMImplementationRegistry.newInstance();
+        } catch (Exception e) {
+            throw new IllegalStateException("Could not instantiate DOMImplementationRegistry: "+e.getMessage(), e);
+        }
+        _domImpl = (DOMImplementationLS)registry.getDOMImplementation("LS");
+    }
+    
+    @Override
+    public void serialize(Node value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_domImpl == null) throw new IllegalStateException("Could not find DOM LS");    	
+        LSSerializer writer = _domImpl.createLSSerializer();
+        jgen.writeString(writer.writeToString(value));
+    }
+
+	@Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint)
+    {
+        // Well... it is serialized as String
+        return createSchemaNode("string", true);
+    }
+
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        if (visitor != null) visitor.expectAnyFormat(typeHint);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
new file mode 100644
index 0000000..68a5aaa
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/OptionalHandlerFactory.java
@@ -0,0 +1,174 @@
+package com.fasterxml.jackson.databind.ext;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.Deserializers;
+import com.fasterxml.jackson.databind.ser.Serializers;
+
+/**
+ * Helper class used for isolating details of handling optional+external types
+ * (javax.xml classes) from standard factories that offer them.
+ * 
+ * @author tatu
+ */
+public class OptionalHandlerFactory
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1;
+
+    /* 1.6.1+ To make 2 main "optional" handler groups (javax.xml.stream)
+     * more dynamic, we better only figure out handlers completely dynamically, if and
+     * when they are needed. To do this we need to assume package prefixes.
+     */
+    private final static String PACKAGE_PREFIX_JAVAX_XML = "javax.xml.";
+
+    private final static String SERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLSerializers";
+    private final static String DESERIALIZERS_FOR_JAVAX_XML = "com.fasterxml.jackson.databind.ext.CoreXMLDeserializers";
+
+    // Plus we also have a single serializer for DOM Node:
+    private final static String CLASS_NAME_DOM_NODE = "org.w3c.dom.Node";
+    private final static String CLASS_NAME_DOM_DOCUMENT = "org.w3c.dom.Node";
+    private final static String SERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMSerializer";
+    private final static String DESERIALIZER_FOR_DOM_DOCUMENT = "com.fasterxml.jackson.databind.ext.DOMDeserializer$DocumentDeserializer";
+    private final static String DESERIALIZER_FOR_DOM_NODE = "com.fasterxml.jackson.databind.ext.DOMDeserializer$NodeDeserializer";
+    
+    public final static OptionalHandlerFactory instance = new OptionalHandlerFactory();
+    
+    protected OptionalHandlerFactory() { }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+    
+    public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type,
+            BeanDescription beanDesc)
+    {
+        Class<?> rawType = type.getRawClass();
+        String className = rawType.getName();
+        String factoryName;
+        
+        if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
+                || hasSupertypeStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
+            factoryName = SERIALIZERS_FOR_JAVAX_XML;
+        } else if (doesImplement(rawType, CLASS_NAME_DOM_NODE)) {
+            return (JsonSerializer<?>) instantiate(SERIALIZER_FOR_DOM_NODE);
+        } else {
+            return null;
+        }
+
+        Object ob = instantiate(factoryName);
+        if (ob == null) { // could warn, if we had logging system (j.u.l?)
+            return null;
+        }
+        return ((Serializers) ob).findSerializer(config, type, beanDesc);
+    }
+
+    public JsonDeserializer<?> findDeserializer(JavaType type, DeserializationConfig config,
+            BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        Class<?> rawType = type.getRawClass();
+        String className = rawType.getName();
+        String factoryName;
+        
+        if (className.startsWith(PACKAGE_PREFIX_JAVAX_XML)
+                || hasSupertypeStartingWith(rawType, PACKAGE_PREFIX_JAVAX_XML)) {
+            factoryName = DESERIALIZERS_FOR_JAVAX_XML;
+        } else if (doesImplement(rawType, CLASS_NAME_DOM_DOCUMENT)) {
+            return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_DOCUMENT);
+        } else if (doesImplement(rawType, CLASS_NAME_DOM_NODE)) {
+            return (JsonDeserializer<?>) instantiate(DESERIALIZER_FOR_DOM_NODE);
+        } else {
+            return null;
+        }
+        Object ob = instantiate(factoryName);
+        if (ob == null) { // could warn, if we had logging system (j.u.l?)
+            return null;
+        }
+        return ((Deserializers) ob).findBeanDeserializer(type, config, beanDesc);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal helper methods
+    /**********************************************************
+     */
+
+    private Object instantiate(String className)
+    {
+        try {
+            return Class.forName(className).newInstance();
+        }
+        catch (LinkageError e) { }
+        // too many different kinds to enumerate here:
+        catch (Exception e) { }
+        return null;
+    }
+    
+    private boolean doesImplement(Class<?> actualType, String classNameToImplement)
+    {
+        for (Class<?> type = actualType; type != null; type = type.getSuperclass()) {
+            if (type.getName().equals(classNameToImplement)) {
+                return true;
+            }
+            // or maybe one of super-interfaces
+            if (hasInterface(type, classNameToImplement)) {
+                return true;
+            }
+        }
+        return false;
+    }
+        
+    private boolean hasInterface(Class<?> type, String interfaceToImplement)
+    {
+        Class<?>[] interfaces = type.getInterfaces();
+        for (Class<?> iface : interfaces) {
+            if (iface.getName().equals(interfaceToImplement)) {
+                return true;
+            }
+        }
+        // maybe super-interface?
+        for (Class<?> iface : interfaces) {
+            if (hasInterface(iface, interfaceToImplement)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasSupertypeStartingWith(Class<?> rawType, String prefix)
+    {
+        // first, superclasses
+        for (Class<?> supertype = rawType.getSuperclass(); supertype != null; supertype = supertype.getSuperclass()) {
+            if (supertype.getName().startsWith(prefix)) {
+                return true;
+            }
+        }
+        // then interfaces
+        for (Class<?> cls = rawType; cls != null; cls = cls.getSuperclass()) {
+            if (hasInterfaceStartingWith(cls, prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasInterfaceStartingWith(Class<?> type, String prefix)
+    {
+        Class<?>[] interfaces = type.getInterfaces();
+        for (Class<?> iface : interfaces) {
+            if (iface.getName().startsWith(prefix)) {
+                return true;
+            }
+        }
+        // maybe super-interface?
+        for (Class<?> iface : interfaces) {
+            if (hasInterfaceStartingWith(iface, prefix)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ext/package-info.java b/src/main/java/com/fasterxml/jackson/databind/ext/package-info.java
new file mode 100644
index 0000000..05c2989
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ext/package-info.java
@@ -0,0 +1,23 @@
+/**
+Contains extended support for "external" packages: things that
+may or may not be present in runtime environment, but that are
+commonly enough used so that explicit support can be added.
+<p>
+Currently supported extensions include:
+<ul>
+ <li>Support for Java 1.5 core XML datatypes: the reason these are
+considered "external" is that some platforms that claim to be 1.5 conformant
+are only partially so (Google Android, GAE) and do not included these
+ types.
+  </li>
+ <li>Joda time. This package has superior date/time handling functionality,
+and is thus supported. However, to minimize forced dependencies this
+support is added as extension so that Joda is not needed by Jackson
+itself: but if it is present, its core types are supported to some
+degree
+  </li>
+</ul>
+
+*/
+
+package com.fasterxml.jackson.databind.ext;
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
new file mode 100644
index 0000000..34dd247
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/Annotated.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+
+/**
+ * Shared base class used for anything on which annotations (included
+ * within a {@link AnnotationMap}).
+ */
+public abstract class Annotated
+{
+    protected Annotated() { }
+    
+    public abstract <A extends Annotation> A getAnnotation(Class<A> acls);
+
+    public final <A extends Annotation> boolean hasAnnotation(Class<A> acls)
+    {
+        return getAnnotation(acls) != null;
+    }
+
+    /**
+     * 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
+     * represents. It is non-null, except for method/constructor parameters
+     * which do not have a JDK counterpart.
+     */
+    public abstract AnnotatedElement getAnnotated();
+
+    protected abstract int getModifiers();
+
+    public final boolean isPublic() {
+        return Modifier.isPublic(getModifiers());
+    }
+
+    public abstract String getName();
+
+    /**
+     * Full generic type of the annotated element; definition
+     * of what exactly this means depends on sub-class.
+     */
+    public JavaType getType(TypeBindings context) {
+        return context.resolveType(getGenericType());
+    }
+
+    /**
+     * Full generic type of the annotated element; definition
+     * of what exactly this means depends on sub-class.
+     */
+    public abstract Type getGenericType();
+
+    /**
+     * "Raw" type (type-erased class) of the annotated element; definition
+     * of what exactly this means depends on sub-class.
+     */
+    public abstract Class<?> getRawType();
+
+    protected abstract AnnotationMap getAllAnnotations();
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
new file mode 100644
index 0000000..e238907
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedClass.java
@@ -0,0 +1,1018 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector.MixInResolver;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public final class AnnotatedClass
+    extends Annotated
+{
+    private final static AnnotationMap[] NO_ANNOTATION_MAPS = new AnnotationMap[0];
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Class for which annotations apply, and that owns other
+     * components (constructors, methods)
+     */
+    final protected Class<?> _class;
+
+    /**
+     * Ordered set of super classes and interfaces of the
+     * class itself: included in order of precedence
+     */
+    final protected List<Class<?>> _superTypes;
+
+    /**
+     * Filter used to determine which annotations to gather; used
+     * to optimize things so that unnecessary annotations are
+     * ignored.
+     */
+    final protected AnnotationIntrospector _annotationIntrospector;
+
+    /**
+     * Object that knows mapping of mix-in classes (ones that contain
+     * annotations to add) with their target classes (ones that
+     * get these additional annotations "mixed in").
+     */
+    final protected MixInResolver _mixInResolver;
+
+    /**
+     * Primary mix-in class; one to use for the annotated class
+     * itself. Can be null.
+     */
+    final protected Class<?> _primaryMixIn;
+
+    /*
+    /**********************************************************
+    /* Gathered information
+    /**********************************************************
+     */
+
+    /**
+     * Combined list of Jackson annotations that the class has,
+     * including inheritable ones from super classes and interfaces
+     */
+    protected AnnotationMap _classAnnotations;
+
+    /**
+     * Flag to indicate whether creator information has been resolved
+     * or not.
+     */
+    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;
+
+    /**
+     * Member methods of interest; for now ones with 0 or 1 arguments
+     * (just optimization, since others won't be used now)
+     */
+    protected AnnotatedMethodMap  _memberMethods;
+
+    /**
+     * Member fields of interest: ones that are either public,
+     * or have at least one annotation.
+     */
+    protected List<AnnotatedField> _fields;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * Constructor will not do any initializations, to allow for
+     * configuring instances differently depending on use cases
+     */
+    private AnnotatedClass(Class<?> cls, List<Class<?>> superTypes,
+            AnnotationIntrospector aintr, MixInResolver mir,
+            AnnotationMap classAnnotations)
+    {
+        _class = cls;
+        _superTypes = superTypes;
+        _annotationIntrospector = aintr;
+        _mixInResolver = mir;
+        _primaryMixIn = (_mixInResolver == null) ? null
+            : _mixInResolver.findMixInClassFor(_class);
+        _classAnnotations = classAnnotations;
+    }
+
+    @Override
+    public AnnotatedClass withAnnotations(AnnotationMap ann) {
+        return new AnnotatedClass(_class, _superTypes,
+                _annotationIntrospector, _mixInResolver, ann);
+    }
+    
+    /**
+     * Factory method that instantiates an instance. Returned instance
+     * will only be initialized with class annotations, but not with
+     * any method information.
+     */
+    public static AnnotatedClass construct(Class<?> cls,
+            AnnotationIntrospector aintr, MixInResolver mir)
+    {
+        return new AnnotatedClass(cls,
+                ClassUtil.findSuperTypes(cls, null), aintr, mir, null);
+    }
+
+    /**
+     * 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,
+            AnnotationIntrospector aintr, MixInResolver mir)
+    {
+        return new AnnotatedClass(cls,
+                Collections.<Class<?>>emptyList(), aintr, mir, null);
+    }
+    
+    /*
+    /**********************************************************
+    /* Annotated impl 
+    /**********************************************************
+     */
+
+    @Override
+    public Class<?> getAnnotated() { return _class; }
+
+    @Override
+    public int getModifiers() { return _class.getModifiers(); }
+
+    @Override
+    public String getName() { return _class.getName(); }
+
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls)
+    {
+        if (_classAnnotations == null) {
+            resolveClassAnnotations();
+        }
+        return _classAnnotations.get(acls);
+    }
+
+    @Override
+    public Type getGenericType() {
+        return _class;
+    }
+
+    @Override
+    public Class<?> getRawType() {
+        return _class;
+    }
+
+    @Override
+    protected AnnotationMap getAllAnnotations() {
+        if (_classAnnotations == null) {
+            resolveClassAnnotations();
+        }
+        return _classAnnotations;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, generic accessors
+    /**********************************************************
+     */
+
+    public Annotations getAnnotations() {
+        if (_classAnnotations == null) {
+            resolveClassAnnotations();
+        }
+        return _classAnnotations;
+    }
+    
+    public boolean hasAnnotations() {
+        if (_classAnnotations == null) {
+            resolveClassAnnotations();
+        }
+        return _classAnnotations.size() > 0;
+    }
+
+    public AnnotatedConstructor getDefaultConstructor()
+    {
+        if (!_creatorsResolved) {
+            resolveCreators();
+        }
+        return _defaultConstructor;
+    }
+
+    public List<AnnotatedConstructor> getConstructors()
+    {
+        if (!_creatorsResolved) {
+            resolveCreators();
+        }
+        return _constructors;
+    }
+
+    public List<AnnotatedMethod> getStaticMethods()
+    {
+        if (!_creatorsResolved) {
+            resolveCreators();
+        }
+        return _creatorMethods;
+    }
+
+    public Iterable<AnnotatedMethod> memberMethods()
+    {
+        if (_memberMethods == null) {
+            resolveMemberMethods();
+        }
+        return _memberMethods;
+    }
+
+    public int getMemberMethodCount()
+    {
+        if (_memberMethods == null) {
+            resolveMemberMethods();
+        }
+        return _memberMethods.size();
+    }
+
+    public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes)
+    {
+        if (_memberMethods == null) {
+            resolveMemberMethods();
+        }
+        return _memberMethods.find(name, paramTypes);
+    }
+
+    public int getFieldCount() {
+        if (_fields == null) {
+            resolveFields();
+        }
+        return _fields.size();
+    }
+
+    public Iterable<AnnotatedField> fields()
+    {
+        if (_fields == null) {
+            resolveFields();
+        }
+        return _fields;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, main-level resolution methods
+    /**********************************************************
+     */
+
+    /**
+     * Initialization method that will recursively collect Jackson
+     * annotations for this class and all super classes and
+     * interfaces.
+     */
+    private void resolveClassAnnotations()
+    {
+        _classAnnotations = new AnnotationMap();
+        // [JACKSON-659] Should skip processing if annotation processing disabled
+        if (_annotationIntrospector != null) {
+            // add mix-in annotations first (overrides)
+            if (_primaryMixIn != null) {
+                _addClassMixIns(_classAnnotations, _class, _primaryMixIn);
+            }
+            // first, annotations from the class itself:
+            _addAnnotationsIfNotPresent(_classAnnotations, _class.getDeclaredAnnotations());
+    
+            // and then from super types
+            for (Class<?> cls : _superTypes) {
+                // and mix mix-in annotations in-between
+                _addClassMixIns(_classAnnotations, cls);
+                _addAnnotationsIfNotPresent(_classAnnotations, cls.getDeclaredAnnotations());
+            }
+            /* 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(_classAnnotations, Object.class);
+        }
+    }
+    
+    /**
+     * Initialization method that will find out all constructors
+     * and potential static factory methods the class has.
+     */
+    private void resolveCreators()
+    {
+        // Then see which constructors we have
+        List<AnnotatedConstructor> constructors = null;
+        Constructor<?>[] declaredCtors = _class.getDeclaredConstructors();
+        for (Constructor<?> ctor : declaredCtors) {
+            if (ctor.getParameterTypes().length == 0) {
+                _defaultConstructor = _constructConstructor(ctor, true);
+            } else {
+                if (constructors == null) {
+                    constructors = new ArrayList<AnnotatedConstructor>(Math.max(10, declaredCtors.length));
+                }
+                constructors.add(_constructConstructor(ctor, false));
+            }
+        }
+        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.
+         */
+        // 14-Feb-2011, tatu: 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 : _class.getDeclaredMethods()) {
+            if (!Modifier.isStatic(m.getModifiers())) {
+                continue;
+            }
+            // all factory methods are fine, as per [JACKSON-850]
+            //int argCount = m.getParameterTypes().length;
+            if (creatorMethods == null) {
+                creatorMethods = new ArrayList<AnnotatedMethod>(8);
+            }
+            creatorMethods.add(_constructCreatorMethod(m));
+        }
+        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 = new AnnotatedMethodMap();
+        AnnotatedMethodMap mixins = new AnnotatedMethodMap();
+        // first: methods from the class itself
+        _addMemberMethods(_class, _memberMethods, _primaryMixIn, mixins);
+
+        // and then augment these with annotations from super-types:
+        for (Class<?> cls : _superTypes) {
+            Class<?> mixin = (_mixInResolver == null) ? null : _mixInResolver.findMixInClassFor(cls);         
+            _addMemberMethods(cls, _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, see [JACKSON-140])
+         */
+        // 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) {
+                            AnnotatedMethod am = _constructMethod(m);
+                            _addMixOvers(mixIn.getAnnotated(), am, false);
+                            _memberMethods.add(am);
+                        }
+                    } catch (Exception e) { }
+                }
+            }
+        }
+    }
+    
+    /**
+     * 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(_class, null);
+        if (foundFields == null || foundFields.size() == 0) {
+            _fields = Collections.emptyList();
+        } else {
+            _fields = new ArrayList<AnnotatedField>(foundFields.size());
+            _fields.addAll(foundFields.values());
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* 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, Class<?> toMask)
+    {
+        if (_mixInResolver != null) {
+            _addClassMixIns(annotations, toMask, _mixInResolver.findMixInClassFor(toMask));
+        }
+    }
+
+    protected void _addClassMixIns(AnnotationMap annotations, Class<?> toMask,
+                                   Class<?> mixin)
+    {
+        if (mixin == null) {
+            return;
+        }
+        // Ok, first: annotations from mix-in class itself:
+        _addAnnotationsIfNotPresent(annotations, mixin.getDeclaredAnnotations());
+
+        /* 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.findSuperTypes(mixin, toMask)) {
+            _addAnnotationsIfNotPresent(annotations, parent.getDeclaredAnnotations());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for populating creator (ctor, factory) information
+    /**********************************************************
+     */
+
+    protected void _addConstructorMixIns(Class<?> mixin)
+    {
+        MemberKey[] ctorKeys = null;
+        int ctorCount = (_constructors == null) ? 0 : _constructors.size();
+        for (Constructor<?> ctor : mixin.getDeclaredConstructors()) {
+            if (ctor.getParameterTypes().length == 0) {
+                if (_defaultConstructor != null) {
+                    _addMixOvers(ctor, _defaultConstructor, false);
+                }
+            } 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;
+                }
+            }
+        }
+    }
+
+    protected void _addFactoryMixIns(Class<?> mixin)
+    {
+        MemberKey[] methodKeys = null;
+        int methodCount = _creatorMethods.size();
+
+        for (Method m : mixin.getDeclaredMethods()) {
+            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, 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 : cls.getDeclaredMethods()) {
+            if (!_isIncludableMemberMethod(m)) {
+                continue;
+            }
+            AnnotatedMethod old = methods.find(m);
+            if (old == null) {
+                AnnotatedMethod newM = _constructMethod(m);
+                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);
+                }
+            } 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));
+                }
+            }
+        }
+    }
+
+    protected void _addMethodMixIns(Class<?> targetClass, AnnotatedMethodMap methods,
+            Class<?> mixInCls, AnnotatedMethodMap mixIns)
+    {
+        List<Class<?>> parents = new ArrayList<Class<?>>();
+        parents.add(mixInCls);
+        ClassUtil.findSuperTypes(mixInCls, targetClass, parents);
+        for (Class<?> mixin : parents) {
+            for (Method m : mixin.getDeclaredMethods()) {
+                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 {
+                    mixIns.add(_constructMethod(m));
+                }
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for populating field information
+    /**********************************************************
+     */
+
+    protected Map<String,AnnotatedField> _findFields(Class<?> c, 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.
+         */
+        Class<?> parent = c.getSuperclass();
+        if (parent != null) {
+            // 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, fields);
+            for (Field f : c.getDeclaredFields()) {
+                // static fields not included, nor transient
+                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));
+            }
+            // And then... any mix-in overrides?
+            if (_mixInResolver != null) {
+                Class<?> mixin = _mixInResolver.findMixInClassFor(c);
+                if (mixin != null) {
+                    _addFieldMixIns(parent, mixin, 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<?> targetClass, Class<?> mixInCls,
+            Map<String,AnnotatedField> fields)
+    {
+        List<Class<?>> parents = new ArrayList<Class<?>>();
+        parents.add(mixInCls);
+        ClassUtil.findSuperTypes(mixInCls, targetClass, parents);
+        for (Class<?> mixin : parents) {
+            for (Field mixinField : mixin.getDeclaredFields()) {
+                // 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)
+    {
+        /* 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(m, _emptyAnnotationMap(), null);
+        }
+        return new AnnotatedMethod(m, _collectRelevantAnnotations(m.getDeclaredAnnotations()), null);
+    }
+
+    protected AnnotatedConstructor _constructConstructor(Constructor<?> ctor, boolean defaultCtor)
+    {
+        if (_annotationIntrospector == null) { // when annotation processing is disabled
+            return new AnnotatedConstructor(ctor, _emptyAnnotationMap(), _emptyAnnotationMaps(ctor.getParameterTypes().length));
+        }
+        if (defaultCtor) {
+            return new AnnotatedConstructor(ctor, _collectRelevantAnnotations(ctor.getDeclaredAnnotations()), null);
+        }
+        Annotation[][] paramAnns = ctor.getParameterAnnotations();
+        int paramCount = ctor.getParameterTypes().length;
+        /* [JACKSON-701]: 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...
+         */
+        // Also: [JACKSON-757] (enum value constructors)
+        AnnotationMap[] resolvedAnnotations = null;
+        if (paramCount != paramAnns.length) {
+            // Limits of the work-around (to avoid hiding real errors):
+            // first, only applicable for member classes and then either:
+
+            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(ctor, _collectRelevantAnnotations(ctor.getDeclaredAnnotations()),
+                resolvedAnnotations);
+    }
+
+    protected AnnotatedMethod _constructCreatorMethod(Method m)
+    {
+        if (_annotationIntrospector == null) { // when annotation processing is disabled
+            return new AnnotatedMethod(m, _emptyAnnotationMap(), _emptyAnnotationMaps(m.getParameterTypes().length));
+        }
+        return new AnnotatedMethod(m, _collectRelevantAnnotations(m.getDeclaredAnnotations()),
+                                   _collectRelevantAnnotations(m.getParameterAnnotations()));
+    }
+
+    protected AnnotatedField _constructField(Field f)
+    {
+        if (_annotationIntrospector == null) { // when annotation processing is disabled
+            return new AnnotatedField(f, _emptyAnnotationMap());
+        }
+        return new AnnotatedField(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)
+    {
+        /* I'm pretty sure synthetic fields are to be skipped...
+         * (methods definitely are)
+         */
+        if (f.isSynthetic()) {
+            return false;
+        }
+        // Static fields are never included, nor transient
+        int mods = f.getModifiers();
+        if (Modifier.isStatic(mods) || Modifier.isTransient(mods)) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+    /**********************************************************
+    /* 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)
+    {
+        AnnotationMap annMap = new AnnotationMap();
+        _addAnnotationsIfNotPresent(annMap, anns);
+        return annMap;
+    }
+    
+    /* 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 void _addAnnotationsIfNotPresent(AnnotationMap result, Annotation[] anns)
+    {
+        if (anns != null) {
+            List<Annotation[]> bundles = null;
+            for (Annotation ann : anns) { // first: direct annotations
+                if (_isAnnotationBundle(ann)) {
+                    if (bundles == null) {
+                        bundles = new LinkedList<Annotation[]>();
+                    }
+                    bundles.add(ann.annotationType().getDeclaredAnnotations());
+                } else { // note: we will NOT filter out non-Jackson anns any more
+                    result.addIfNotPresent(ann);
+                }
+            }
+            if (bundles != null) { // and secondarily handle bundles, if any found: precedence important
+                for (Annotation[] annotations : bundles) {
+                    _addAnnotationsIfNotPresent(result, annotations);
+                }
+            }
+        }
+    }
+
+    private void _addAnnotationsIfNotPresent(AnnotatedMember target, Annotation[] anns)
+    {
+        if (anns != null) {
+            List<Annotation[]> bundles = null;
+            for (Annotation ann : anns) { // first: direct annotations
+                if (_isAnnotationBundle(ann)) {
+                    if (bundles == null) {
+                        bundles = new LinkedList<Annotation[]>();
+                    }
+                    bundles.add(ann.annotationType().getDeclaredAnnotations());
+                } else { // note: we will NOT filter out non-Jackson anns any more
+                    target.addIfNotPresent(ann);
+                }
+            }
+            if (bundles != null) { // and secondarily handle bundles, if any found: precedence important
+                for (Annotation[] annotations : bundles) {
+                    _addAnnotationsIfNotPresent(target, annotations);
+                }
+            }
+        }
+    }
+    
+    private void _addOrOverrideAnnotations(AnnotatedMember target, Annotation[] anns)
+    {
+        if (anns != null) {
+            List<Annotation[]> bundles = null;
+            for (Annotation ann : anns) { // first: direct annotations
+                if (_isAnnotationBundle(ann)) {
+                    if (bundles == null) {
+                        bundles = new LinkedList<Annotation[]>();
+                    }
+                    bundles.add(ann.annotationType().getDeclaredAnnotations());
+                } else { // note: no filtering by jackson-annotations
+                    target.addOrOverride(ann);
+                }
+            }
+            if (bundles != null) { // and then bundles, if any: important for precedence
+                for (Annotation[] annotations : bundles) {
+                    _addOrOverrideAnnotations(target, annotations);
+                }
+            }
+        }
+    }
+    
+    /**
+     * @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);
+   }
+   
+    /*
+    /**********************************************************
+    /* Other methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[AnnotedClass "+_class.getName()+"]";
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
new file mode 100644
index 0000000..d94c091
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedConstructor.java
@@ -0,0 +1,214 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.*;
+
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public final class AnnotatedConstructor
+    extends AnnotatedWithParams
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final Constructor<?> _constructor;
+
+    /**
+     * Field that is used to make JDK serialization work with this
+     * object.
+     * 
+     * @since 2.1
+     */
+    protected Serialization _serialization;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public AnnotatedConstructor(Constructor<?> constructor,
+            AnnotationMap classAnn, AnnotationMap[] paramAnn)
+    {
+        super(classAnn, paramAnn);
+        if (constructor == null) {
+            throw new IllegalArgumentException("Null constructor not allowed");
+        }
+        _constructor = constructor;
+    }
+
+    /**
+     * Method used for JDK serialization support
+     * @since 2.1
+     */
+    protected AnnotatedConstructor(Serialization ser)
+    {
+        super(null, null);
+        _constructor = null;
+        _serialization = ser;
+    }
+    
+    @Override
+    public AnnotatedConstructor withAnnotations(AnnotationMap ann) {
+        return new AnnotatedConstructor(_constructor, ann, _paramAnnotations);
+    }
+    
+    /*
+    /**********************************************************
+    /* Annotated impl
+    /**********************************************************
+     */
+
+    @Override
+    public Constructor<?> getAnnotated() { return _constructor; }
+
+    @Override
+    public int getModifiers() { return _constructor.getModifiers(); }
+
+    @Override
+    public String getName() { return _constructor.getName(); }
+
+    @Override
+    public Type getGenericType() {
+        return getRawType();
+    }
+
+    @Override
+    public Class<?> getRawType() {
+        return _constructor.getDeclaringClass();
+    }
+
+    // note: copied verbatim from AnnotatedMethod; hard to generalize
+    /**
+     * As per [JACKSON-468], we need to also allow declaration of local
+     * type bindings; mostly it will allow defining bounds.
+     */
+    @Override
+    public JavaType getType(TypeBindings bindings)
+    {
+        return getType(bindings, _constructor.getTypeParameters());
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    @Override
+    public int getParameterCount() {
+        return _constructor.getParameterTypes().length;
+    }
+
+    @Override
+    public Class<?> getRawParameterType(int index)
+    {
+        Class<?>[] types = _constructor.getParameterTypes();
+        return (index >= types.length) ? null : types[index];
+    }
+
+    @Override
+    public Type getGenericParameterType(int index)
+    {
+        Type[] types = _constructor.getGenericParameterTypes();
+        return (index >= types.length) ? null : types[index];
+    }
+
+    @Override
+    public final Object call() throws Exception {
+        return _constructor.newInstance();
+    }
+
+    @Override
+    public final Object call(Object[] args) throws Exception {
+        return _constructor.newInstance(args);
+    }
+
+    @Override
+    public final Object call1(Object arg) throws Exception {
+        return _constructor.newInstance(arg);
+    }
+    
+    /*
+    /**********************************************************
+    /* AnnotatedMember impl
+    /**********************************************************
+     */
+
+    @Override
+    public Class<?> getDeclaringClass() { return _constructor.getDeclaringClass(); }
+
+    @Override
+    public Member getMember() { return _constructor; }
+
+    @Override
+    public void setValue(Object pojo, Object value)
+        throws UnsupportedOperationException
+    {
+        throw new UnsupportedOperationException("Cannot call setValue() on constructor of "
+                +getDeclaringClass().getName());
+    }
+
+    @Override
+    public Object getValue(Object pojo)
+        throws UnsupportedOperationException
+    {
+        throw new UnsupportedOperationException("Cannot call getValue() on constructor of "
+                +getDeclaringClass().getName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API, specific annotations
+    /**********************************************************
+     */
+
+    @Override
+    public String toString() {
+        return "[constructor for "+getName()+", annotations: "+_annotations+"]";
+    }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object writeReplace() {
+        return new AnnotatedConstructor(new Serialization(_constructor));
+    }
+
+    Object readResolve() {
+        Class<?> clazz = _serialization.clazz;
+        try {
+            Constructor<?> ctor = clazz.getDeclaredConstructor(_serialization.args);
+            // 06-Oct-2012, tatu: Has "lost" its security override, must force back
+            if (!ctor.isAccessible()) {
+                ClassUtil.checkAndFixAccess(ctor);
+            }
+            return new AnnotatedConstructor(ctor, null, null);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Could not find constructor with "
+                    +_serialization.args.length+" args from Class '"+clazz.getName());
+        }
+    }
+    
+    /**
+     * Helper class that is used as the workaround to persist
+     * Field references. It basically just stores declaring class
+     * and field name.
+     */
+    private final static class Serialization
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+        protected Class<?> clazz;
+        protected Class<?>[] args;
+
+        public Serialization(Constructor<?> ctor) {
+            clazz = ctor.getDeclaringClass();
+            args = ctor.getParameterTypes();
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
new file mode 100644
index 0000000..a0ce14a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedField.java
@@ -0,0 +1,187 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Object that represents non-static (and usually non-transient/volatile)
+ * fields of a class.
+ */
+public final class AnnotatedField
+    extends AnnotatedMember
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 7364428299211355871L;
+
+    /**
+     * Actual {@link Field} used for access.
+     *<p>
+     * Transient since it can not be persisted directly using
+     * JDK serialization
+     */
+    protected final transient Field _field;
+
+    /**
+     * Temporary field required for JDK serialization support
+     */
+    protected Serialization _serialization;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public AnnotatedField(Field field, AnnotationMap annMap)
+    {
+        super(annMap);
+        _field = field;
+    }
+    
+    @Override
+    public AnnotatedField withAnnotations(AnnotationMap ann) {
+        return new AnnotatedField(_field, ann);
+    }
+
+    /**
+     * Method used for JDK serialization support
+     */
+    protected AnnotatedField(Serialization ser)
+    {
+        super(null);
+        _field = null;
+        _serialization = ser;
+    }
+    
+    /*
+    /**********************************************************
+    /* Annotated impl
+    /**********************************************************
+     */
+
+    @Override
+    public Field getAnnotated() { return _field; }
+
+    @Override
+    public int getModifiers() { return _field.getModifiers(); }
+
+    @Override
+    public String getName() { return _field.getName(); }
+
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls)
+    {
+        return (_annotations == null) ? null : _annotations.get(acls);
+    }
+
+    @Override
+    public Type getGenericType() {
+        return _field.getGenericType();
+    }
+
+    @Override
+    public Class<?> getRawType() {
+        return _field.getType();
+    }
+    
+    /*
+    /**********************************************************
+    /* AnnotatedMember impl
+    /**********************************************************
+     */
+
+    @Override
+    public Class<?> getDeclaringClass() { return _field.getDeclaringClass(); }
+
+    @Override
+    public Member getMember() { return _field; }
+
+    @Override
+    public void setValue(Object pojo, Object value) throws IllegalArgumentException
+    {
+        try {
+            _field.set(pojo, value);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Failed to setValue() for field "
+                    +getFullName()+": "+e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public Object getValue(Object pojo) throws IllegalArgumentException
+    {
+        try {
+            return _field.get(pojo);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Failed to getValue() for field "
+                    +getFullName()+": "+e.getMessage(), e);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API, generic
+    /**********************************************************
+     */
+
+    public String getFullName() {
+        return getDeclaringClass().getName() + "#" + getName();
+    }
+
+    public int getAnnotationCount() { return _annotations.size(); }
+
+    @Override
+    public String toString()
+    {
+        return "[field "+getFullName()+"]";
+    }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object writeReplace() {
+        return new AnnotatedField(new Serialization(_field));
+    }
+
+    Object readResolve() {
+        Class<?> clazz = _serialization.clazz;
+        try {
+            Field f = clazz.getDeclaredField(_serialization.name);
+            // 06-Oct-2012, tatu: Has "lost" its security override, may need to force back
+            if (!f.isAccessible()) {
+                ClassUtil.checkAndFixAccess(f);
+            }
+            return new AnnotatedField(f, null);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Could not find method '"+_serialization.name
+                        +"' from Class '"+clazz.getName());
+        }
+    }
+    
+    /**
+     * Helper class that is used as the workaround to persist
+     * Field references. It basically just stores declaring class
+     * and field name.
+     */
+    private final static class Serialization
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+        protected Class<?> clazz;
+        protected String name;
+
+        public Serialization(Field f) {
+            clazz = f.getDeclaringClass();
+            name = f.getName();
+            
+        }
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
new file mode 100644
index 0000000..0a4ef3f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMember.java
@@ -0,0 +1,90 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Member;
+
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Intermediate base class for annotated entities that are members of
+ * a class; fields, methods and constructors. This is a superset
+ * of things that can represent logical properties as it contains
+ * constructors in addition to fields and methods.
+ * 
+ * @author tatu
+ */
+public abstract class AnnotatedMember
+    extends Annotated
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 7364428299211355871L;
+
+    // Transient since information not needed after construction, so
+    // no need to persist
+    protected final transient AnnotationMap _annotations;
+
+    protected AnnotatedMember(AnnotationMap annotations) {
+        super();
+        _annotations = annotations;
+    }
+
+    public abstract Class<?> getDeclaringClass();
+
+    public abstract Member getMember();
+
+    @Override
+    protected AnnotationMap getAllAnnotations() {
+        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 void addOrOverride(Annotation a) {
+        _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 void addIfNotPresent(Annotation a) {
+        _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.
+     */
+    public final void fixAccess() {
+        ClassUtil.checkAndFixAccess(getMember());
+    }
+
+    /**
+     * Optional method that can be used to assign value of
+     * this member on given object, if this is a supported
+     * operation for member type.
+     *<p>
+     * This is implemented for fields and single-argument
+     * member methods; but not for constructor parameters or
+     * other types of methods (like static methods)
+     */
+    public abstract void setValue(Object pojo, Object value)
+        throws UnsupportedOperationException, IllegalArgumentException;
+
+    /**
+     * Optional method that can be used to access the value of
+     * this member on given object, if this is a supported
+     * operation for member type.
+     *<p>
+     * This is implemented for fields and no-argument
+     * member methods; but not for constructor parameters or
+     * other types of methods (like static methods)
+     */
+    public abstract Object getValue(Object pojo)
+        throws UnsupportedOperationException, IllegalArgumentException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
new file mode 100644
index 0000000..c37eef5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethod.java
@@ -0,0 +1,276 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public final class AnnotatedMethod
+    extends AnnotatedWithParams
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    final protected transient Method _method;
+
+    // // Simple lazy-caching:
+
+    protected Class<?>[] _paramClasses;
+
+    /**
+     * Field that is used to make JDK serialization work with this
+     * object.
+     * 
+     * @since 2.1
+     */
+    protected Serialization _serialization;
+    
+    /*
+    /*****************************************************
+    /* Life-cycle
+    /*****************************************************
+     */
+
+    public AnnotatedMethod(Method method, AnnotationMap classAnn, AnnotationMap[] paramAnnotations)
+    {
+        super(classAnn, paramAnnotations);
+        if (method == null) {
+            throw new IllegalArgumentException("Can not construct AnnotatedMethod with null Method");
+        }
+        _method = method;
+    }
+
+    /**
+     * Method used for JDK serialization support
+     * @since 2.1
+     */
+    protected AnnotatedMethod(Serialization ser)
+    {
+        super(null, null);
+        _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(m, _annotations, _paramAnnotations);
+    }
+    
+    @Override
+    public AnnotatedMethod withAnnotations(AnnotationMap ann) {
+        return new AnnotatedMethod(_method, ann, _paramAnnotations);
+    }
+
+    /*
+    /*****************************************************
+    /* Annotated impl
+    /*****************************************************
+     */
+
+    @Override
+    public Method getAnnotated() { return _method; }
+
+    @Override
+    public int getModifiers() { return _method.getModifiers(); }
+
+    @Override
+    public String getName() { return _method.getName(); }
+
+    /**
+     * For methods, this returns declared return type, which is only
+     * useful with getters (setters do not return anything; hence "void"
+     * type is returned here)
+     */
+    @Override
+    public Type getGenericType() {
+        return _method.getGenericReturnType();
+    }
+
+    /**
+     * For methods, this returns declared return type, which is only
+     * useful with getters (setters do not usually return anything;
+     * hence "void" type is returned here)
+     */
+    @Override
+    public Class<?> getRawType() {
+        return _method.getReturnType();
+    }
+
+    /**
+     * As per [JACKSON-468], we need to also allow declaration of local
+     * type bindings; mostly it will allow defining bounds.
+     */
+    @Override
+    public JavaType getType(TypeBindings bindings) {
+        return getType(bindings, _method.getTypeParameters());
+    }
+
+    @Override
+    public final Object call() throws Exception {
+        return _method.invoke(null);
+    }
+
+    @Override
+    public final Object call(Object[] args) throws Exception {
+        return _method.invoke(null, args);
+    }
+
+    @Override
+    public final Object call1(Object arg) throws Exception {
+        return _method.invoke(null, arg);
+    }
+    
+    /*
+    /********************************************************
+    /* AnnotatedMember impl
+    /********************************************************
+     */
+
+    @Override
+    public Class<?> getDeclaringClass() { return _method.getDeclaringClass(); }
+
+    @Override
+    public Method getMember() { return _method; }
+
+    @Override
+    public void setValue(Object pojo, Object value)
+        throws IllegalArgumentException
+    {
+        try {
+            _method.invoke(pojo, value);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException("Failed to setValue() with method "
+                    +getFullName()+": "+e.getMessage(), e);
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException("Failed to setValue() with method "
+                    +getFullName()+": "+e.getMessage(), e);
+        }
+    }
+
+    @Override
+    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) {
+            throw new IllegalArgumentException("Failed to getValue() with method "
+                    +getFullName()+": "+e.getMessage(), e);
+        }
+    }
+    
+    /*
+    /*****************************************************
+    /* Extended API, generic
+    /*****************************************************
+     */
+
+    @Override
+    public int getParameterCount() {
+        return getRawParameterTypes().length;
+    }
+
+    public String getFullName() {
+        return getDeclaringClass().getName() + "#" + getName() + "("
+            +getParameterCount()+" params)";
+    }
+    
+    public Class<?>[] getRawParameterTypes()
+    {
+        if (_paramClasses == null) {
+            _paramClasses = _method.getParameterTypes();
+        }
+        return _paramClasses;
+    }
+    
+    public Type[] getGenericParameterTypes() {
+        return _method.getGenericParameterTypes();
+    }
+
+    @Override
+    public Class<?> getRawParameterType(int index)
+    {
+        Class<?>[] types = getRawParameterTypes();
+        return (index >= types.length) ? null : types[index];
+    }
+
+    @Override
+    public Type getGenericParameterType(int index)
+    {
+        Type[] types = _method.getGenericParameterTypes();
+        return (index >= types.length) ? null : types[index];
+    }
+
+    public Class<?> getRawReturnType() {
+        return _method.getReturnType();
+    }
+    
+    public Type getGenericReturnType() {
+        return _method.getGenericReturnType();
+    }
+
+    /*
+    /********************************************************
+    /* Other
+    /********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[method "+getFullName()+"]";
+    }
+
+    /*
+    /**********************************************************
+    /* JDK serialization handling
+    /**********************************************************
+     */
+
+    Object writeReplace() {
+        return new AnnotatedMethod(new Serialization(_method));
+    }
+
+    Object readResolve() {
+        Class<?> clazz = _serialization.clazz;
+        try {
+            Method m = clazz.getDeclaredMethod(_serialization.name,
+                    _serialization.args);
+            // 06-Oct-2012, tatu: Has "lost" its security override, may need to force back
+            if (!m.isAccessible()) {
+                ClassUtil.checkAndFixAccess(m);
+            }
+            return new AnnotatedMethod(m, null, null);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Could not find method '"+_serialization.name
+                        +"' from Class '"+clazz.getName());
+        }
+    }
+    
+    /**
+     * Helper class that is used as the workaround to persist
+     * Field references. It basically just stores declaring class
+     * and field name.
+     */
+    private final static class Serialization
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+        protected Class<?> clazz;
+        protected String name;
+        protected Class<?>[] args;
+
+        public Serialization(Method setter) {
+            clazz = setter.getDeclaringClass();
+            name = setter.getName();
+            args = setter.getParameterTypes();
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java
new file mode 100644
index 0000000..0cfb387
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedMethodMap.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Simple helper class used to keep track of collection of
+ * {@link AnnotatedMethod}s, accessible by lookup. Lookup
+ * is usually needed for augmenting and overriding annotations.
+ */
+public final class AnnotatedMethodMap
+    implements Iterable<AnnotatedMethod>
+{
+    protected LinkedHashMap<MemberKey,AnnotatedMethod> _methods;
+
+    public AnnotatedMethodMap() { }
+
+    /**
+     * Method called to add specified annotated method in the Map.
+     */
+    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 int size() {
+        return (_methods == null) ? 0 : _methods.size();
+    }
+
+    public AnnotatedMethod find(String name, Class<?>[] paramTypes)
+    {
+        if (_methods == null) {
+            return null;
+        }
+        return _methods.get(new MemberKey(name, paramTypes));
+    }
+
+    public AnnotatedMethod find(Method m)
+    {
+        if (_methods == null) {
+            return null;
+        }
+        return _methods.get(new MemberKey(m));
+    }
+
+    /*
+    /**********************************************************
+    /* Iterable implementation (for iterating over values)
+    /**********************************************************
+     */
+
+    @Override
+    public Iterator<AnnotatedMethod> iterator()
+    {
+        if (_methods != null) {
+            return _methods.values().iterator();
+        }
+        List<AnnotatedMethod> empty = Collections.emptyList();
+        return empty.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
new file mode 100644
index 0000000..4e6bdfa
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedParameter.java
@@ -0,0 +1,187 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Member;
+import java.lang.reflect.Type;
+
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * 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
+ * JDK objects associated; so access should mostly be limited to checking
+ * annotation values which are properly aggregated and included.
+ *<p>
+ * Note: as of version 1.7, this type extends {@link AnnotatedMember}, since
+ * it behaves like a member for the most part, but earlier it just extended
+ * {@link Annotated}
+ */
+public final class AnnotatedParameter
+    extends AnnotatedMember
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Member (method, constructor) that this parameter belongs to
+     */
+    protected final AnnotatedWithParams _owner;
+    
+    /**
+     * JDK type of the parameter, possibly contains generic type information
+     */
+    protected final Type _type;
+    
+    /**
+     * Index of the parameter within argument list
+     */
+    protected final int _index;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public AnnotatedParameter(AnnotatedWithParams owner, Type type,  AnnotationMap annotations,
+            int index)
+    {
+        super(annotations);
+        _owner = owner;
+        _type = type;
+        _index = index;
+    }
+
+    @Override
+    public AnnotatedParameter withAnnotations(AnnotationMap ann) {
+        if (ann == _annotations) {
+            return this;
+        }
+        return _owner.replaceParameterAnnotations(_index, ann);
+    }
+
+    /*
+    /**********************************************************
+    /* Annotated impl
+    /**********************************************************
+     */
+
+    /**
+     * Since there is no matching JDK element, this method will
+     * always return null
+     */
+    @Override
+    public AnnotatedElement getAnnotated() { return null; }
+
+    /**
+     * Returns modifiers of the constructor, as parameters do not
+     * have independent modifiers.
+     */
+    @Override
+    public int getModifiers() { return _owner.getModifiers(); }
+
+    /**
+     * Parameters have no names in bytecode (unlike in source code),
+     * will always return empty String ("").
+     */
+    @Override
+    public String getName() { return ""; }
+
+    /**
+     * Accessor for annotations; all annotations associated with parameters
+     * are properly passed and accessible.
+     */
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls)
+    {
+        return (_annotations == null) ? null : _annotations.get(acls);
+    }
+
+    @Override
+    public Type getGenericType() {
+        return _type;
+    }
+
+    @Override
+    public Class<?> getRawType()
+    {
+        if (_type instanceof Class<?>) {
+            return (Class<?>) _type;
+        }
+        // 14-Mar-2011, tatu: Not optimal, but has to do for now...
+        JavaType t = TypeFactory.defaultInstance().constructType(_type);
+        return t.getRawClass();
+    }
+
+    /*
+    /**********************************************************
+    /* AnnotatedMember extras
+    /**********************************************************
+     */
+
+    @Override
+    public Class<?> getDeclaringClass() {
+        return _owner.getDeclaringClass();
+    }
+
+    @Override
+    public Member getMember() {
+        /* This is bit tricky: since there is no JDK equivalent; can either
+         * return null or owner... let's do latter, for now.
+         */
+        return _owner.getMember();
+    }
+
+    @Override
+    public void setValue(Object pojo, Object value) throws UnsupportedOperationException
+    {
+        throw new UnsupportedOperationException("Cannot call setValue() on constructor parameter of "
+                +getDeclaringClass().getName());
+    }
+
+    @Override
+    public Object getValue(Object pojo) throws UnsupportedOperationException
+    {
+        throw new UnsupportedOperationException("Cannot call getValue() on constructor parameter of "
+                +getDeclaringClass().getName());
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    public Type getParameterType() { return _type; }
+
+    /**
+     * Accessor for 'owner' of this parameter; method or constructor that
+     * has this parameter as member of its argument list.
+     * 
+     * @return Owner (member or creator) object of this parameter
+     */
+    public AnnotatedWithParams getOwner() { return _owner; }
+    
+    /**
+     * Accessor for index of this parameter within argument list
+     * 
+     * @return Index of this parameter within argument list
+     */
+    public int getIndex() { return _index; }
+
+    /*
+    /********************************************************
+    /* Other
+    /********************************************************
+     */
+    
+    @Override
+    public String toString()
+    {
+        return "[parameter #"+getIndex()+", annotations: "+_annotations+"]";
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
new file mode 100644
index 0000000..f67ccda
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotatedWithParams.java
@@ -0,0 +1,168 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Intermediate base class that encapsulates features that
+ * constructors and methods share.
+ */
+public abstract class AnnotatedWithParams
+    extends AnnotatedMember
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Annotations associated with parameters of the annotated
+     * entity (method or constructor parameters)
+     */
+    protected final AnnotationMap[] _paramAnnotations;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected AnnotatedWithParams(AnnotationMap annotations, AnnotationMap[] paramAnnotations)
+    {
+        super(annotations);
+        _paramAnnotations = paramAnnotations;
+    }
+
+    /**
+     * Method called to override a method parameter annotation,
+     * usually due to a mix-in
+     * annotation masking or overriding an annotation 'real' method
+     * has.
+     */
+    public final void addOrOverrideParam(int paramIndex, Annotation a)
+    {
+        AnnotationMap old = _paramAnnotations[paramIndex];
+        if (old == null) {
+            old = new AnnotationMap();
+            _paramAnnotations[paramIndex] = old;
+        }
+        old.add(a);
+    }
+
+    /**
+     * Method called by parameter object when an augmented instance is created;
+     * needs to replace parameter with new instance
+     */
+    protected AnnotatedParameter replaceParameterAnnotations(int index, AnnotationMap ann)
+    {
+        _paramAnnotations[index] = ann;
+        return getParameter(index);
+    }    
+
+    /*
+    /**********************************************************
+    /* Helper methods for subclasses
+    /**********************************************************
+     */
+
+    protected JavaType getType(TypeBindings bindings, TypeVariable<?>[] typeParams)
+    {
+        // [JACKSON-468] Need to consider local type binding declarations too...
+        if (typeParams != null && typeParams.length > 0) {
+            bindings = bindings.childInstance();
+            for (TypeVariable<?> var : typeParams) {
+                String name = var.getName();
+                // to prevent infinite loops, need to first add placeholder ("<T extends Enum<T>>" etc)
+                bindings._addPlaceholder(name);
+                // About only useful piece of information is the lower bound (which is at least Object.class)
+                Type lowerBound = var.getBounds()[0];
+                JavaType type = (lowerBound == null) ? TypeFactory.unknownType()
+                        : bindings.resolveType(lowerBound);
+                bindings.addBinding(var.getName(), type);
+            }
+        }
+        return bindings.resolveType(getGenericType());
+    }
+
+    /*
+    /**********************************************************
+    /* Partial Annotated impl
+    /**********************************************************
+     */
+
+    @Override
+    public final <A extends Annotation> A getAnnotation(Class<A> acls)
+    {
+        return _annotations.get(acls);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    public final AnnotationMap getParameterAnnotations(int index)
+    {
+        if (_paramAnnotations != null) {
+            if (index >= 0 && index <= _paramAnnotations.length) {
+                return _paramAnnotations[index];
+            }
+        }
+        return null;
+    }
+
+    public final AnnotatedParameter getParameter(int index) {
+        return new AnnotatedParameter(this, getGenericParameterType(index),
+                getParameterAnnotations(index), index);
+    }
+
+    public abstract int getParameterCount();
+
+    public abstract Class<?> getRawParameterType(int index);
+
+    public abstract Type getGenericParameterType(int index);
+
+    /**
+     * Method called to fully resolve type of one of parameters, given
+     * specified type variable bindings.
+     */
+    public final JavaType resolveParameterType(int index, TypeBindings bindings) {
+        return bindings.resolveType(getGenericParameterType(index));
+    }
+    
+    public final int getAnnotationCount() { return _annotations.size(); }
+
+    /**
+     * Method that can be used to (try to) call this object without arguments.
+     * This may succeed or fail, depending on expected number
+     * of arguments: caller needs to take care to pass correct number.
+     * Exceptions are thrown directly from actual low-level call.
+     *<p>
+     * Note: only works for constructors and static methods.
+     */
+    public abstract Object call() throws Exception;
+
+    /**
+     * Method that can be used to (try to) call this object with specified arguments.
+     * This may succeed or fail, depending on expected number
+     * of arguments: caller needs to take care to pass correct number.
+     * Exceptions are thrown directly from actual low-level call.
+     *<p>
+     * Note: only works for constructors and static methods.
+     */
+    public abstract Object call(Object[] args) throws Exception;
+
+    /**
+     * Method that can be used to (try to) call this object with single arguments.
+     * This may succeed or fail, depending on expected number
+     * of arguments: caller needs to take care to pass correct number.
+     * Exceptions are thrown directly from actual low-level call.
+     *<p>
+     * Note: only works for constructors and static methods.
+     */
+    public abstract Object call1(Object arg) throws Exception;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
new file mode 100644
index 0000000..42c8162
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationIntrospectorPair.java
@@ -0,0 +1,774 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Helper class that allows using 2 introspectors such that one
+ * introspector acts as the primary one to use; and second one
+ * as a fallback used if the primary does not provide conclusive
+ * or useful result for a method.
+ *<p>
+ * An obvious consequence of priority is that it is easy to construct
+ * longer chains of introspectors by linking multiple pairs.
+ * Currently most likely combination is that of using the default
+ * Jackson provider, along with JAXB annotation introspector.
+ *<p>
+ * Note: up until 2.0, this class was an inner class of
+ * {@link AnnotationIntrospector}; moved here for convenience.
+ * 
+ * @since 2.1
+ */
+public class AnnotationIntrospectorPair
+    extends AnnotationIntrospector
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final AnnotationIntrospector _primary, _secondary;
+
+    public AnnotationIntrospectorPair(AnnotationIntrospector p, AnnotationIntrospector s)
+    {
+        _primary = p;
+        _secondary = s;
+    }
+
+    @Override
+    public Version version() {
+        return _primary.version();
+    }
+
+    /**
+     * Helper method for constructing a Pair from two given introspectors (if
+     * neither is null); or returning non-null introspector if one is null
+     * (and return just null if both are null)
+     */
+    public static AnnotationIntrospector create(AnnotationIntrospector primary,
+            AnnotationIntrospector secondary)
+    {
+        if (primary == null) {
+            return secondary;
+        }
+        if (secondary == null) {
+            return primary;
+        }
+        return new AnnotationIntrospectorPair(primary, secondary);
+    }
+
+    @Override
+    public Collection<AnnotationIntrospector> allIntrospectors() {
+        return allIntrospectors(new ArrayList<AnnotationIntrospector>());
+    }
+
+    @Override
+    public Collection<AnnotationIntrospector> allIntrospectors(Collection<AnnotationIntrospector> result)
+    {
+        _primary.allIntrospectors(result);
+        _secondary.allIntrospectors(result);
+        return result;
+    }
+    
+    // // // Generic annotation properties, lookup
+    
+    @Override
+    public boolean isAnnotationBundle(Annotation ann) {
+        return _primary.isAnnotationBundle(ann) || _secondary.isAnnotationBundle(ann);
+    }
+    
+    /*
+    /******************************************************
+    /* General class annotations
+    /******************************************************
+     */
+
+    @Override
+    public PropertyName findRootName(AnnotatedClass ac)
+    {
+        PropertyName name1 = _primary.findRootName(ac);
+        if (name1 == null) {
+            return _secondary.findRootName(ac);
+        }
+        if (name1.hasSimpleName()) {
+            return name1;
+        }
+        // name1 is empty; how about secondary?
+        PropertyName name2 = _secondary.findRootName(ac);
+        return (name2 == null) ? name1 : name2;
+    }
+
+    @Override
+    public String[] findPropertiesToIgnore(Annotated ac)
+    {
+        String[] result = _primary.findPropertiesToIgnore(ac);
+        if (result == null) {
+            result = _secondary.findPropertiesToIgnore(ac);
+        }
+        return result;            
+    }
+
+    @Override
+    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)
+    {
+        Boolean result = _primary.isIgnorableType(ac);
+        if (result == null) {
+            result = _secondary.isIgnorableType(ac);
+        }
+        return result;
+    }
+
+    @Override
+    public Object findFilterId(AnnotatedClass ac)
+    {
+        Object id = _primary.findFilterId(ac);
+        if (id == null) {
+            id = _secondary.findFilterId(ac);
+        }
+        return id;
+    }
+
+    @Override
+    public Object findNamingStrategy(AnnotatedClass ac)
+    {
+        Object str = _primary.findNamingStrategy(ac);
+        if (str == null) {
+            str = _secondary.findNamingStrategy(ac);
+        }
+        return str;
+    }
+
+    /*
+    /******************************************************
+    /* Property auto-detection
+    /******************************************************
+    */
+    
+    @Override
+    public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
+        VisibilityChecker<?> checker)
+    {
+        /* Note: to have proper priorities, we must actually call delegatees
+         * in reverse order:
+         */
+        checker = _secondary.findAutoDetectVisibility(ac, checker);
+        return _primary.findAutoDetectVisibility(ac, checker);
+    }
+
+    /*
+    /******************************************************
+    /* Type handling
+    /******************************************************
+    */
+    
+    @Override
+    public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
+            AnnotatedClass ac, JavaType baseType)
+    {
+        TypeResolverBuilder<?> b = _primary.findTypeResolver(config, ac, baseType);
+        if (b == null) {
+            b = _secondary.findTypeResolver(config, ac, baseType);
+        }
+        return b;
+    }
+
+    @Override
+    public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType baseType)
+    {
+        TypeResolverBuilder<?> b = _primary.findPropertyTypeResolver(config, am, baseType);
+        if (b == null) {
+            b = _secondary.findPropertyTypeResolver(config, am, baseType);
+        }
+        return b;
+    }
+
+    @Override
+    public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType baseType)
+    {
+        TypeResolverBuilder<?> b = _primary.findPropertyContentTypeResolver(config, am, baseType);
+        if (b == null) {
+            b = _secondary.findPropertyContentTypeResolver(config, am, baseType);
+        }
+        return b;
+    }
+    
+    @Override
+    public List<NamedType> findSubtypes(Annotated a)
+    {
+        List<NamedType> types1 = _primary.findSubtypes(a);
+        List<NamedType> types2 = _secondary.findSubtypes(a);
+        if (types1 == null || types1.isEmpty()) return types2;
+        if (types2 == null || types2.isEmpty()) return types1;
+        ArrayList<NamedType> result = new ArrayList<NamedType>(types1.size() + types2.size());
+        result.addAll(types1);
+        result.addAll(types2);
+        return result;
+    }
+
+    @Override
+    public String findTypeName(AnnotatedClass ac)
+    {
+        String name = _primary.findTypeName(ac);
+        if (name == null || name.length() == 0) {
+            name = _secondary.findTypeName(ac);                
+        }
+        return name;
+    }
+    
+    // // // General member (field, method/constructor) annotations
+    
+    @Override        
+    public ReferenceProperty findReferenceType(AnnotatedMember member)
+    {
+        ReferenceProperty ref = _primary.findReferenceType(member);
+        if (ref == null) {
+            ref = _secondary.findReferenceType(member);
+        }
+        return ref; 
+    }
+
+    @Override        
+    public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member)
+    {
+        NameTransformer value = _primary.findUnwrappingNameTransformer(member);
+        if (value == null) {
+            value = _secondary.findUnwrappingNameTransformer(member);
+        }
+        return value;
+    }
+
+    @Override
+    public Object findInjectableValueId(AnnotatedMember m)
+    {
+        Object value = _primary.findInjectableValueId(m);
+        if (value == null) {
+            value = _secondary.findInjectableValueId(m);
+        }
+        return value;
+    }
+
+    @Override
+    public boolean hasIgnoreMarker(AnnotatedMember m) {
+        return _primary.hasIgnoreMarker(m) || _secondary.hasIgnoreMarker(m);
+    }
+    
+    @Override
+    public Boolean hasRequiredMarker(AnnotatedMember m)
+    {
+        Boolean value = _primary.hasRequiredMarker(m);
+        if (value == null) {
+            value = _secondary.hasRequiredMarker(m);
+        }
+        return value;
+    }
+    
+    // // // Serialization: general annotations
+
+    @Override
+    public Object findSerializer(Annotated am)
+    {
+        Object result = _primary.findSerializer(am);
+        if (result == null) {
+            result = _secondary.findSerializer(am);
+        }
+        return result;
+    }
+    
+    @Override
+    public Object findKeySerializer(Annotated a)
+    {
+        Object result = _primary.findKeySerializer(a);
+        if (result == null || result == JsonSerializer.None.class || result == NoClass.class) {
+            result = _secondary.findKeySerializer(a);
+        }
+        return result;
+    }
+
+    @Override
+    public Object findContentSerializer(Annotated a)
+    {
+        Object result = _primary.findContentSerializer(a);
+        if (result == null || result == JsonSerializer.None.class || result == NoClass.class) {
+            result = _secondary.findContentSerializer(a);
+        }
+        return result;
+    }
+    
+    @Override
+    public JsonInclude.Include findSerializationInclusion(Annotated a,
+            JsonInclude.Include defValue)
+    {
+        /* This is bit trickier: need to combine results in a meaningful
+         * way. Seems like it should be a disjoint; that is, most
+         * restrictive value should be returned.
+         * For enumerations, comparison is done by indexes, which
+         * works: largest value is the last one, which is the most
+         * restrictive value as well.
+         */
+        /* 09-Mar-2010, tatu: Actually, as per [JACKSON-256], it is probably better to just
+         *    use strict overriding. Simpler, easier to understand.
+         */
+        // note: call secondary first, to give lower priority
+        defValue = _secondary.findSerializationInclusion(a, defValue);
+        defValue = _primary.findSerializationInclusion(a, defValue);
+        return defValue;
+    }
+    
+    @Override
+    public Class<?> findSerializationType(Annotated a)
+    {
+        Class<?> result = _primary.findSerializationType(a);
+        if (result == null) {
+            result = _secondary.findSerializationType(a);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType)
+    {
+        Class<?> result = _primary.findSerializationKeyType(am, baseType);
+        if (result == null) {
+            result = _secondary.findSerializationKeyType(am, baseType);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<?> findSerializationContentType(Annotated am, JavaType baseType)
+    {
+        Class<?> result = _primary.findSerializationContentType(am, baseType);
+        if (result == null) {
+            result = _secondary.findSerializationContentType(am, baseType);
+        }
+        return result;
+    }
+    
+    @Override
+    public JsonSerialize.Typing findSerializationTyping(Annotated a)
+    {
+        JsonSerialize.Typing result = _primary.findSerializationTyping(a);
+        if (result == null) {
+            result = _secondary.findSerializationTyping(a);
+        }
+        return result;
+    }
+
+    @Override
+    public Object findSerializationConverter(Annotated a)
+    {
+        Object ob = _primary.findSerializationConverter(a);
+        if (ob == null) {
+            ob = _secondary.findSerializationConverter(a);
+        }
+        return ob;
+    }
+
+    @Override
+    public Object findSerializationContentConverter(AnnotatedMember a)
+    {
+        Object ob = _primary.findSerializationContentConverter(a);
+        if (ob == null) {
+            ob = _secondary.findSerializationContentConverter(a);
+        }
+        return ob;
+    }
+
+    @Override
+    public Class<?>[] findViews(Annotated a)
+    {
+        /* Theoretically this could be trickier, if multiple introspectors
+         * return non-null entries. For now, though, we'll just consider
+         * first one to return non-null to win.
+         */
+        Class<?>[] result = _primary.findViews(a);
+        if (result == null) {
+            result = _secondary.findViews(a);
+        }
+        return result;
+    }
+
+    @Override
+    public Boolean isTypeId(AnnotatedMember member) {
+        Boolean b = _primary.isTypeId(member);
+        if (b == null) {
+            b = _secondary.isTypeId(member);
+        }
+        return b;
+    }
+
+    @Override
+    public ObjectIdInfo findObjectIdInfo(Annotated ann) {
+        ObjectIdInfo result = _primary.findObjectIdInfo(ann);
+        if (result == null) {
+            result = _secondary.findObjectIdInfo(ann);
+        }
+        return result;
+    }
+
+    @Override
+    public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
+        // to give precedence for primary, must start with secondary:
+        objectIdInfo = _secondary.findObjectReferenceInfo(ann, objectIdInfo);
+        objectIdInfo = _primary.findObjectReferenceInfo(ann, objectIdInfo);
+        return objectIdInfo;
+    }
+    
+    @Override
+    public JsonFormat.Value findFormat(Annotated ann) {
+        JsonFormat.Value result = _primary.findFormat(ann);
+        if (result == null) {
+            result = _secondary.findFormat(ann);
+        }
+        return result;
+    }
+
+    @Override
+    public PropertyName findWrapperName(Annotated ann) {
+        PropertyName name = _primary.findWrapperName(ann);
+        if (name == null) {
+            name = _secondary.findWrapperName(ann);
+        } else if (name == PropertyName.USE_DEFAULT) {
+            // does the other introspector have a better idea?
+            PropertyName name2 = _secondary.findWrapperName(ann);
+            if (name2 != null) {
+                name = name2;
+            }
+        }
+        return name;
+    }
+    
+    // // // Serialization: class annotations
+
+    @Override
+    public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
+        String[] result = _primary.findSerializationPropertyOrder(ac);
+        if (result == null) {
+            result = _secondary.findSerializationPropertyOrder(ac);
+        }
+        return result;            
+    }
+
+    /**
+     * Method for checking whether an annotation indicates that serialized properties
+     * for which no explicit is defined should be alphabetically (lexicograpically)
+     * ordered
+     */
+    @Override
+    public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
+        Boolean result = _primary.findSerializationSortAlphabetically(ac);
+        if (result == null) {
+            result = _secondary.findSerializationSortAlphabetically(ac);
+        }
+        return result;            
+    }
+
+    // // // Serialization: property annotations
+    
+    @Override
+    public PropertyName findNameForSerialization(Annotated a) {
+        PropertyName n = _primary.findNameForSerialization(a);
+        // note: "use default" should not block explicit answer, so:
+        if (n == null) {
+            n = _secondary.findNameForSerialization(a);
+        } else if (n == PropertyName.USE_DEFAULT) {
+            PropertyName n2 = _secondary.findNameForSerialization(a);
+            if (n2 != null) {
+                n = n2;
+            }
+        }
+        return n;
+    }
+    
+    @Override
+    public boolean hasAsValueAnnotation(AnnotatedMethod am)
+    {
+        return _primary.hasAsValueAnnotation(am) || _secondary.hasAsValueAnnotation(am);
+    }
+    
+    @Override
+    public String findEnumValue(Enum<?> value)
+    {
+        String result = _primary.findEnumValue(value);
+        if (result == null) {
+            result = _secondary.findEnumValue(value);
+        }
+        return result;
+    }        
+
+    // // // Deserialization: general annotations
+
+    @Override
+    public Object findDeserializer(Annotated am)
+    {
+        Object result = _primary.findDeserializer(am);
+        if (result == null) {
+            result = _secondary.findDeserializer(am);
+        }
+        return result;
+    }
+    
+    @Override
+    public Object findKeyDeserializer(Annotated am)
+    {
+        Object result = _primary.findKeyDeserializer(am);
+        if (result == null || result == KeyDeserializer.None.class || result == NoClass.class) {
+            result = _secondary.findKeyDeserializer(am);
+        }
+        return result;
+    }
+
+    @Override
+    public Object findContentDeserializer(Annotated am)
+    {
+        Object result = _primary.findContentDeserializer(am);
+        if (result == null || result == JsonDeserializer.None.class || result == NoClass.class) {
+            result = _secondary.findContentDeserializer(am);
+        }
+        return result;
+    }
+    
+    @Override
+    public Class<?> findDeserializationType(Annotated am, JavaType baseType)
+    {
+        Class<?> result = _primary.findDeserializationType(am, baseType);
+        if (result == null) {
+            result = _secondary.findDeserializationType(am, baseType);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType)
+    {
+        Class<?> result = _primary.findDeserializationKeyType(am, baseKeyType);
+        if (result == null) {
+            result = _secondary.findDeserializationKeyType(am, baseKeyType);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType) {
+        Class<?> result = _primary.findDeserializationContentType(am, baseContentType);
+        if (result == null) {
+            result = _secondary.findDeserializationContentType(am, baseContentType);
+        }
+        return result;
+    }
+
+    @Override
+    public Object findDeserializationConverter(Annotated a) {
+        Object ob = _primary.findDeserializationConverter(a);
+        if (ob == null) {
+            ob = _secondary.findDeserializationConverter(a);
+        }
+        return ob;
+    }
+
+    @Override
+    public Object findDeserializationContentConverter(AnnotatedMember a) {
+        Object ob = _primary.findDeserializationContentConverter(a);
+        if (ob == null) {
+            ob = _secondary.findDeserializationContentConverter(a);
+        }
+        return ob;
+    }
+    
+    // // // Deserialization: class annotations
+
+    @Override
+    public Object findValueInstantiator(AnnotatedClass ac)
+    {
+        Object result = _primary.findValueInstantiator(ac);
+        if (result == null) {
+            result = _secondary.findValueInstantiator(ac);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<?> findPOJOBuilder(AnnotatedClass ac)
+    {
+            Class<?> result = _primary.findPOJOBuilder(ac);
+            if (result == null) {
+                    result = _secondary.findPOJOBuilder(ac);
+            }
+            return result;
+    }
+
+    @Override
+    public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac)
+    {
+        JsonPOJOBuilder.Value result = _primary.findPOJOBuilderConfig(ac);
+        if (result == null) {
+            result = _secondary.findPOJOBuilderConfig(ac);
+        }
+        return result;
+    }
+    
+    // // // Deserialization: method annotations
+
+    @Override
+    public PropertyName findNameForDeserialization(Annotated a)
+    {
+        // note: "use default" should not block explicit answer, so:
+        PropertyName n = _primary.findNameForDeserialization(a);
+        if (n == null) {
+            n = _secondary.findNameForDeserialization(a);
+        } else if (n == PropertyName.USE_DEFAULT) {
+            PropertyName n2 = _secondary.findNameForDeserialization(a);
+            if (n2 != null) {
+                n = n2;
+            }
+        }
+        return n;
+    }
+    
+    @Override
+    public boolean hasAnySetterAnnotation(AnnotatedMethod am)
+    {
+        return _primary.hasAnySetterAnnotation(am) || _secondary.hasAnySetterAnnotation(am);
+    }
+
+    @Override
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am)
+    {
+        return _primary.hasAnyGetterAnnotation(am) || _secondary.hasAnyGetterAnnotation(am);
+    }
+    
+    @Override
+    public boolean hasCreatorAnnotation(Annotated a)
+    {
+        return _primary.hasCreatorAnnotation(a) || _secondary.hasCreatorAnnotation(a);
+    }
+ 
+    /*
+    /******************************************************
+    /* Deprecated methods
+    /******************************************************
+     */
+    
+    @Deprecated
+    @Override
+    public boolean isHandled(Annotation ann) {
+        return _primary.isHandled(ann) || _secondary.isHandled(ann);
+    }
+
+    // // // Deserialization: property annotations
+
+    @Deprecated
+    @Override
+    public String findDeserializationName(AnnotatedMethod am)
+    {
+        String result = _primary.findDeserializationName(am);
+        if (result == null) {
+            result = _secondary.findDeserializationName(am);
+        } else if (result.length() == 0) {
+            /* Empty String is a default; can be overridden by
+             * more explicit answer from secondary entry
+             */
+            String str2 = _secondary.findDeserializationName(am);
+            if (str2 != null) {
+                result = str2;
+            }
+        }
+        return result;
+    }
+    
+    @Deprecated
+    @Override
+    public String findDeserializationName(AnnotatedField af)
+    {
+        String result = _primary.findDeserializationName(af);
+        if (result == null) {
+            result = _secondary.findDeserializationName(af);
+        } else if (result.length() == 0) {
+            /* Empty String is a default; can be overridden by
+             * more explicit answer from secondary entry
+             */
+            String str2 = _secondary.findDeserializationName(af);
+            if (str2 != null) {
+                result = str2;
+            }
+        }
+        return result;
+    }
+
+    @Deprecated
+    @Override
+    public String findDeserializationName(AnnotatedParameter param)
+    {
+        String result = _primary.findDeserializationName(param);
+        if (result == null) {
+            result = _secondary.findDeserializationName(param);
+        }
+        return result;
+    }
+
+    // // // Serialization: property annotations
+    
+    @Deprecated
+    @Override
+    public String findSerializationName(AnnotatedMethod am)
+    {
+        String result = _primary.findSerializationName(am);
+        if (result == null) {
+            result = _secondary.findSerializationName(am);
+        } else if (result.length() == 0) {
+            /* Empty String is a default; can be overridden by
+             * more explicit answer from secondary entry
+             */
+            String str2 = _secondary.findSerializationName(am);
+            if (str2 != null) {
+                result = str2;
+            }
+        }
+        return result;
+    }
+
+    @Deprecated
+    @Override
+    public String findSerializationName(AnnotatedField af)
+    {
+        String result = _primary.findSerializationName(af);
+        if (result == null) {
+            result = _secondary.findSerializationName(af);
+        } else if (result.length() == 0) {
+            /* Empty String is a default; can be overridden by
+             * more explicit answer from secondary entry
+             */
+            String str2 = _secondary.findSerializationName(af);
+            if (str2 != null) {
+                result = str2;
+            }
+        }
+        return result;
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
new file mode 100644
index 0000000..28231eb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/AnnotationMap.java
@@ -0,0 +1,102 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.util.Annotations;
+
+/**
+ * Simple helper class used to keep track of collection of
+ * Jackson Annotations associated with annotatable things
+ * (methods, constructors, classes).
+ * Note that only Jackson-owned annotations are tracked (for now?).
+ */
+public final class AnnotationMap implements Annotations
+{
+    protected HashMap<Class<? extends Annotation>,Annotation> _annotations;
+
+    public AnnotationMap() { }
+    
+    private AnnotationMap(HashMap<Class<? extends Annotation>,Annotation> a) {
+        _annotations = a;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <A extends Annotation> A get(Class<A> cls)
+    {
+        if (_annotations == null) {
+            return null;
+        }
+        return (A) _annotations.get(cls);
+    }
+
+    public static AnnotationMap merge(AnnotationMap primary, AnnotationMap secondary)
+    {
+        if (primary == null || primary._annotations == null || primary._annotations.isEmpty()) {
+            return secondary;
+        }
+        if (secondary == null || secondary._annotations == null || secondary._annotations.isEmpty()) {
+            return primary;
+        }
+        HashMap<Class<? extends Annotation>,Annotation> annotations
+            = new HashMap<Class<? extends Annotation>,Annotation>();
+        // add secondary ones first
+        for (Annotation ann : secondary._annotations.values()) {
+            annotations.put(ann.annotationType(), ann);
+        }
+        // to be overridden by primary ones
+        for (Annotation ann : primary._annotations.values()) {
+            annotations.put(ann.annotationType(), ann);
+        }
+        return new AnnotationMap(annotations);
+    }
+    
+    @Override
+    public int size() {
+        return (_annotations == null) ? 0 : _annotations.size();
+    }
+
+    /**
+     * Method called to add specified annotation in the Map, but
+     * only if it didn't yet exist.
+     */
+    public void addIfNotPresent(Annotation ann)
+    {
+        if (_annotations == null || !_annotations.containsKey(ann.annotationType())) {
+            _add(ann);
+        }
+    }
+
+    /**
+     * Method called to add specified annotation in the Map.
+     */
+    public void add(Annotation ann) {
+        _add(ann);
+    }
+
+    @Override
+    public String toString()
+    {
+        if (_annotations == null) {
+            return "[null]";
+        }
+        return _annotations.toString();
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected final void _add(Annotation ann)
+    {
+        if (_annotations == null) {
+            _annotations = new HashMap<Class<? extends Annotation>,Annotation>();
+        }
+        _annotations.put(ann.annotationType(), ann);
+    }
+}
+
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
new file mode 100644
index 0000000..0a0ce02
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicBeanDescription.java
@@ -0,0 +1,611 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Default {@link BeanDescription} implementation.
+ * Can theoretically be subclassed to customize
+ * some aspects of property introspection.
+ */
+public class BasicBeanDescription extends BeanDescription
+{
+    /*
+    /**********************************************************
+    /* General configuration
+    /**********************************************************
+     */
+
+    final protected MapperConfig<?> _config;
+
+    final protected AnnotationIntrospector _annotationIntrospector;
+    
+    /**
+     * Information collected about the class introspected.
+     */
+    final protected AnnotatedClass _classInfo;
+    
+    /**
+     * We may need type bindings for the bean type. If so, we'll
+     * construct it lazily
+     */
+    protected TypeBindings _bindings;
+
+    /*
+    /**********************************************************
+    /* Member information
+    /**********************************************************
+     */
+
+    /**
+     * Properties collected for the POJO.
+     */
+    protected final List<BeanPropertyDefinition> _properties;
+
+    /**
+     * Details of Object Id to include, if any
+     */
+    protected ObjectIdInfo _objectIdInfo;
+    
+    // // for deserialization
+    
+    protected AnnotatedMethod _anySetterMethod;
+
+    protected Map<Object, AnnotatedMember> _injectables;
+    
+    /**
+     * Set of properties that can be ignored during deserialization, due
+     * to being marked as ignored.
+     */
+    protected Set<String> _ignoredPropertyNames;
+
+    // // for serialization
+    
+    protected AnnotatedMethod _jsonValueMethod;
+
+    protected AnnotatedMember _anyGetter;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected BasicBeanDescription(MapperConfig<?> config,
+            JavaType type, AnnotatedClass classDef,
+            List<BeanPropertyDefinition> props)
+    {
+        super(type);
+        _config = config;
+        _annotationIntrospector = (config == null) ? null : config.getAnnotationIntrospector();
+        _classInfo = classDef;
+        _properties = props;
+    }
+    
+    protected BasicBeanDescription(POJOPropertiesCollector coll)
+    {
+        this(coll.getConfig(), coll.getType(), coll.getClassDef(), coll.getProperties());
+        _objectIdInfo = coll.getObjectIdInfo();
+    }
+
+    /**
+     * Factory method to use for constructing an instance to use for building
+     * deserializers.
+     */
+    public static BasicBeanDescription forDeserialization(POJOPropertiesCollector coll)
+    {
+        BasicBeanDescription desc = new BasicBeanDescription(coll);
+        desc._anySetterMethod = coll.getAnySetterMethod();
+        desc._ignoredPropertyNames = coll.getIgnoredPropertyNames();
+        desc._injectables = coll.getInjectables();
+        desc._jsonValueMethod = coll.getJsonValueMethod();
+        return desc;
+    }
+
+    /**
+     * Factory method to use for constructing an instance to use for building
+     * serializers.
+     */
+    public static BasicBeanDescription forSerialization(POJOPropertiesCollector coll)
+    {
+        BasicBeanDescription desc = new BasicBeanDescription(coll);
+        desc._jsonValueMethod = coll.getJsonValueMethod();
+        desc._anyGetter = coll.getAnyGetter();
+        return desc;
+    }
+
+    /**
+     * Factory method to use for constructing an instance to use for purposes
+     * other than building serializers or deserializers; will only have information
+     * on class, not on properties.
+     */
+    public static BasicBeanDescription forOtherUse(MapperConfig<?> config,
+            JavaType type, AnnotatedClass ac)
+    {
+        return new BasicBeanDescription(config, type,
+                ac, Collections.<BeanPropertyDefinition>emptyList());
+    }
+
+    /*
+    /**********************************************************
+    /* Limited modifications by core databind functionality
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to prune unwanted properties, during
+     * construction of serializers and deserializers.
+     * Use with utmost care, if at all...
+     * 
+     * @since 2.1
+     */
+    public boolean removeProperty(String propName)
+    {
+        Iterator<BeanPropertyDefinition> it = _properties.iterator();
+        while (it.hasNext()) {
+            BeanPropertyDefinition prop = it.next();
+            if (prop.getName().equals(propName)) {
+                it.remove();
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /*
+    /**********************************************************
+    /* Simple accessors from BeanDescription
+    /**********************************************************
+     */
+
+    @Override
+    public AnnotatedClass getClassInfo() { return _classInfo; }
+
+    @Override
+    public ObjectIdInfo getObjectIdInfo() { return  _objectIdInfo; }
+    
+    @Override
+    public List<BeanPropertyDefinition> findProperties() {
+        return _properties;
+    }
+
+    @Override
+    public AnnotatedMethod findJsonValueMethod() {
+        return _jsonValueMethod;
+    }
+
+    @Override
+    public Set<String> getIgnoredPropertyNames() {
+        if (_ignoredPropertyNames == null) {
+            return Collections.emptySet();
+        }
+        return _ignoredPropertyNames;
+    }
+    
+    @Override
+    public boolean hasKnownClassAnnotations() {
+        return _classInfo.hasAnnotations();
+    }
+
+    @Override
+    public Annotations getClassAnnotations() {
+        return _classInfo.getAnnotations();
+    }
+
+    @Override
+    public TypeBindings bindingsForBeanType()
+    {
+        if (_bindings == null) {
+            _bindings = new TypeBindings(_config.getTypeFactory(), _type);
+        }
+        return _bindings;
+    }
+
+    @Override
+    public JavaType resolveType(java.lang.reflect.Type jdkType) {
+        if (jdkType == null) {
+            return null;
+        }
+        return bindingsForBeanType().resolveType(jdkType);
+    }
+
+    @Override
+    public AnnotatedConstructor findDefaultConstructor() {
+        return _classInfo.getDefaultConstructor();
+    }
+
+    @Override
+    public AnnotatedMethod findAnySetter() throws IllegalArgumentException
+    {
+        if (_anySetterMethod != 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 = _anySetterMethod.getRawParameterType(0);
+            if (type != String.class && type != Object.class) {
+                throw new IllegalArgumentException("Invalid 'any-setter' annotation on method "+_anySetterMethod.getName()+"(): first argument not of type String or Object, but "+type.getName());
+            }
+        }
+        return _anySetterMethod;
+    }
+
+    @Override
+    public Map<Object, AnnotatedMember> findInjectables() {
+        return _injectables;
+    }
+
+    @Override
+    public List<AnnotatedConstructor> getConstructors() {
+        return _classInfo.getConstructors();
+    }
+
+    @Override
+    public Object instantiateBean(boolean fixAccess)
+    {
+        AnnotatedConstructor ac = _classInfo.getDefaultConstructor();
+        if (ac == null) {
+            return null;
+        }
+        if (fixAccess) {
+            ac.fixAccess();
+        }
+        try {
+            return ac.getAnnotated().newInstance();
+        } catch (Exception e) {
+            Throwable t = e;
+            while (t.getCause() != null) {
+                t = t.getCause();
+            }
+            if (t instanceof Error) throw (Error) t;
+            if (t instanceof RuntimeException) throw (RuntimeException) t;
+            throw new IllegalArgumentException("Failed to instantiate bean of type "+_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") "+t.getMessage(), t);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Simple accessors, extended
+    /**********************************************************
+     */
+
+    @Override
+    public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes) {
+        return _classInfo.findMethod(name, paramTypes);
+    }
+
+    /*
+    /**********************************************************
+    /* General per-class annotation introspection
+    /**********************************************************
+     */
+
+    @Override
+    public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue)
+    {
+        if (_annotationIntrospector != null) {
+            JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo);
+            if (v != null) {
+                return v;
+            }
+        }
+        return defValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Introspection for serialization
+    /**********************************************************
+     */
+
+    @Override
+    public Converter<Object,Object> findSerializationConverter()
+    {
+        if (_annotationIntrospector == null) {
+            return null;
+        }
+        return _createConverter(_annotationIntrospector.findSerializationConverter(_classInfo));
+    }
+
+    /**
+     * Method for determining whether null properties should be written
+     * out for a Bean of introspected type. This is based on global
+     * feature (lowest priority, passed as argument)
+     * and per-class annotation (highest priority).
+     */
+    @Override
+    public JsonInclude.Include findSerializationInclusion(JsonInclude.Include defValue)
+    {
+        if (_annotationIntrospector == null) {
+            return defValue;
+        }
+        return _annotationIntrospector.findSerializationInclusion(_classInfo, defValue);
+    }
+    
+    /**
+     * Method used to locate the method of introspected class that
+     * implements {@link com.fasterxml.jackson.annotation.JsonAnyGetter}.
+     * If no such method exists null is returned.
+     * If more than one are found, an exception is thrown.
+     */
+    @Override
+    public AnnotatedMember findAnyGetter() throws IllegalArgumentException
+    {
+        if (_anyGetter != null) {
+            /* For now let's require a Map; in future can add support for other
+             * types like perhaps Iterable<Map.Entry>?
+             */
+            Class<?> type = _anyGetter.getRawType();
+            if (!Map.class.isAssignableFrom(type)) {
+                throw new IllegalArgumentException("Invalid 'any-getter' annotation on method "+_anyGetter.getName()+"(): return type is not instance of java.util.Map");
+            }
+        }
+        return _anyGetter;
+    }
+    
+    @Override
+    public Map<String,AnnotatedMember> findBackReferenceProperties()
+    {
+        HashMap<String,AnnotatedMember> result = null;
+        for (BeanPropertyDefinition property : _properties) {
+            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) {
+                    throw new IllegalArgumentException("Multiple back-reference properties with name '"+refName+"'");
+                }
+            }
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Introspection for deserialization, factories
+    /**********************************************************
+     */
+
+    @Override
+    public List<AnnotatedMethod> getFactoryMethods()
+    {
+        // must filter out anything that clearly is not a factory method
+        List<AnnotatedMethod> candidates = _classInfo.getStaticMethods();
+        if (candidates.isEmpty()) {
+            return candidates;
+        }
+        ArrayList<AnnotatedMethod> result = new ArrayList<AnnotatedMethod>();
+        for (AnnotatedMethod am : candidates) {
+            if (isFactoryMethod(am)) {
+                result.add(am);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public Constructor<?> findSingleArgConstructor(Class<?>... argTypes)
+    {
+        for (AnnotatedConstructor ac : _classInfo.getConstructors()) {
+            // This list is already filtered to only include accessible
+            /* (note: for now this is a redundant check; but in future
+             * that may change; thus leaving here for now)
+             */
+            if (ac.getParameterCount() == 1) {
+                Class<?> actArg = ac.getRawParameterType(0);
+                for (Class<?> expArg : argTypes) {
+                    if (expArg == actArg) {
+                        return ac.getAnnotated();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Method findFactoryMethod(Class<?>... expArgTypes)
+    {
+        // So, of all single-arg static methods:
+        for (AnnotatedMethod am : _classInfo.getStaticMethods()) {
+            if (isFactoryMethod(am)) {
+                // And must take one of expected arg types (or supertype)
+                Class<?> actualArgType = am.getRawParameterType(0);
+                for (Class<?> expArgType : expArgTypes) {
+                    // And one that matches what we would pass in
+                    if (actualArgType.isAssignableFrom(expArgType)) {
+                        return am.getAnnotated();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    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)
+         */
+        Class<?> rt = am.getRawReturnType();
+        if (!getBeanClass().isAssignableFrom(rt)) {
+            return false;
+        }
+
+        /* Also: must be a recognized factory method, meaning:
+         * (a) marked with @JsonCreator annotation, or
+         * (a) "valueOf" (at this point, need not be public)
+         */
+        if (_annotationIntrospector.hasCreatorAnnotation(am)) {
+            return true;
+        }
+        if ("valueOf".equals(am.getName())) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Method for getting ordered list of named Creator properties.
+     * Returns an empty list is none found. If multiple Creator
+     * methods are defined, order between properties from different
+     * methods is undefined; however, properties for each such
+     * Creator are ordered properly relative to each other. For the
+     * usual case of just a single Creator, named properties are
+     * thus properly ordered.
+     */
+    public List<String> findCreatorPropertyNames()
+    {
+        List<String> names = null;
+
+        for (int i = 0; i < 2; ++i) {
+            List<? extends AnnotatedWithParams> l = (i == 0)
+                ? getConstructors() : getFactoryMethods();
+            for (AnnotatedWithParams creator : l) {
+                int argCount = creator.getParameterCount();
+                if (argCount < 1) continue;
+                PropertyName name = _annotationIntrospector.findNameForDeserialization(creator.getParameter(0));
+                if (name == null) {
+                    continue;
+                }
+                if (names == null) {
+                    names = new ArrayList<String>();
+                }
+                names.add(name.getSimpleName());
+                for (int p = 1; p < argCount; ++p) {
+                    name = _annotationIntrospector.findNameForDeserialization(creator.getParameter(p));
+                    names.add((name == null) ? null : name.getSimpleName());
+                }
+            }
+        }
+        if (names == null) {
+            return Collections.emptyList();
+        }
+        return names;
+    }
+
+    /*
+    /**********************************************************
+    /* Introspection for deserialization, other
+    /**********************************************************
+     */
+    
+    @Override
+    public Class<?> findPOJOBuilder()
+    {
+    	return (_annotationIntrospector == null) ?
+    			null : _annotationIntrospector.findPOJOBuilder(_classInfo);
+    }
+
+    @Override
+    public JsonPOJOBuilder.Value findPOJOBuilderConfig()
+    {
+        return (_annotationIntrospector == null) ?
+                null : _annotationIntrospector.findPOJOBuilderConfig(_classInfo);
+    }
+
+    @Override
+    public Converter<Object,Object> findDeserializationConverter()
+    {
+        if (_annotationIntrospector == null) {
+            return null;
+        }
+        return _createConverter(_annotationIntrospector.findDeserializationConverter(_classInfo));
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for field introspection
+    /**********************************************************
+     */
+
+    /**
+     * @param ignoredProperties (optional) names of properties to ignore;
+     *   any fields that would be recognized as one of these properties
+     *   is ignored.
+     * @param forSerialization If true, will collect serializable property
+     *    fields; if false, deserializable
+     *
+     * @return Ordered Map with logical property name as key, and
+     *    matching field as value.
+     */
+    public LinkedHashMap<String,AnnotatedField> _findPropertyFields(
+            Collection<String> ignoredProperties, boolean forSerialization)
+    {
+        LinkedHashMap<String,AnnotatedField> results = new LinkedHashMap<String,AnnotatedField>();
+        for (BeanPropertyDefinition property : _properties) {
+            AnnotatedField f = property.getField();
+            if (f != null) {
+                String name = property.getName();
+                if (ignoredProperties != null) {
+                    if (ignoredProperties.contains(name)) {
+                        continue;
+                    }
+                }
+                results.put(name, f);
+            }
+        }
+        return results;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, other
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    public Converter<Object,Object> _createConverter(Object converterDef)
+    {
+        if (converterDef == null) {
+            return null;
+        }
+        if (converterDef instanceof Converter<?,?>) {
+            return (Converter<Object,Object>) converterDef;
+        }
+        if (!(converterDef instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type "
+                    +converterDef.getClass().getName()+"; expected type Converter or Class<Converter> instead");
+        }
+        Class<?> converterClass = (Class<?>)converterDef;
+        // there are some known "no class" markers to consider too:
+        if (converterClass == Converter.None.class || converterClass == NoClass.class) {
+            return null;
+        }
+        if (!Converter.class.isAssignableFrom(converterClass)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Class "
+                    +converterClass.getName()+"; expected Class<Converter>");
+        }
+        HandlerInstantiator hi = _config.getHandlerInstantiator();
+        Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(_config, _classInfo, converterClass);
+        if (conv == null) {
+            conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
+                    _config.canOverrideAccessModifiers());
+        }
+        return (Converter<Object,Object>) conv;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
new file mode 100644
index 0000000..bbb0a12
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BasicClassIntrospector.java
@@ -0,0 +1,188 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.type.SimpleType;
+
+public class BasicClassIntrospector
+    extends ClassIntrospector
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /* We keep a small set of pre-constructed descriptions to use for
+     * common non-structured values, such as Numbers and Strings.
+     * This is strictly performance optimization to reduce what is
+     * usually one-time cost, but seems useful for some cases considering
+     * simplicity.
+     */
+    
+    protected final static BasicBeanDescription STRING_DESC;
+    static {
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(String.class, null, null);
+        STRING_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(String.class), ac);
+    }
+    protected final static BasicBeanDescription BOOLEAN_DESC;
+    static {
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Boolean.TYPE, null, null);
+        BOOLEAN_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Boolean.TYPE), ac);
+    }
+    protected final static BasicBeanDescription INT_DESC;
+    static {
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Integer.TYPE, null, null);
+        INT_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Integer.TYPE), ac);
+    }
+    protected final static BasicBeanDescription LONG_DESC;
+    static {
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(Long.TYPE, null, null);
+        LONG_DESC = BasicBeanDescription.forOtherUse(null, SimpleType.constructUnsafe(Long.TYPE), ac);
+    }
+    
+    /*
+    /**********************************************************
+    /* Life cycle
+    /**********************************************************
+     */
+
+    public final static BasicClassIntrospector instance = new BasicClassIntrospector();
+
+    public BasicClassIntrospector() { }
+    
+    /*
+    /**********************************************************
+    /* Factory method impls
+    /**********************************************************
+     */
+
+    @Override
+    public BasicBeanDescription forSerialization(SerializationConfig cfg,
+            JavaType type, MixInResolver r)
+    {
+        // minor optimization: for JDK types do minimal introspection
+        BasicBeanDescription desc = _findCachedDesc(type);
+        if (desc == null) {
+            desc = BasicBeanDescription.forSerialization(collectProperties(cfg,
+            		type, r, true, "set"));
+        }
+        return desc;
+    }
+
+    @Override
+    public BasicBeanDescription forDeserialization(DeserializationConfig cfg,
+            JavaType type, MixInResolver r)
+    {
+        // minor optimization: for JDK types do minimal introspection
+        BasicBeanDescription desc = _findCachedDesc(type);
+        if (desc == null) {
+            desc = BasicBeanDescription.forDeserialization(collectProperties(cfg,
+            		type, r, false, "set"));
+        }
+        return desc;
+    }
+
+    @Override
+    public BasicBeanDescription forDeserializationWithBuilder(DeserializationConfig cfg,
+            JavaType type, MixInResolver r)
+    {
+    	// no caching for Builders (no standard JDK builder types):
+    	return BasicBeanDescription.forDeserialization(collectPropertiesWithBuilder(cfg,
+            		type, r, false));
+    }
+    
+    @Override
+    public BasicBeanDescription forCreation(DeserializationConfig cfg,
+            JavaType type, MixInResolver r)
+    {
+        BasicBeanDescription desc = _findCachedDesc(type);
+        if (desc == null) {
+            desc = BasicBeanDescription.forDeserialization(
+            		collectProperties(cfg, type, r, false, "set"));
+        }
+        return desc;
+    }
+
+    @Override
+    public BasicBeanDescription forClassAnnotations(MapperConfig<?> cfg,
+            JavaType type, MixInResolver r)
+    {
+        boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
+        AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(),
+                (useAnnotations ? cfg.getAnnotationIntrospector() : null), r);
+        return BasicBeanDescription.forOtherUse(cfg, type, ac);
+    }
+
+    @Override
+    public BasicBeanDescription forDirectClassAnnotations(MapperConfig<?> cfg,
+            JavaType type, MixInResolver r)
+    {
+        boolean useAnnotations = cfg.isAnnotationProcessingEnabled();
+        AnnotationIntrospector ai =  cfg.getAnnotationIntrospector();
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(type.getRawClass(),
+                (useAnnotations ? ai : null), r);
+        return BasicBeanDescription.forOtherUse(cfg, type, ac);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable helper methods
+    /**********************************************************
+     */
+
+    protected POJOPropertiesCollector collectProperties(MapperConfig<?> config,
+            JavaType type, MixInResolver r, boolean forSerialization,
+            String mutatorPrefix)
+    {
+        boolean useAnnotations = config.isAnnotationProcessingEnabled();
+        AnnotatedClass ac = AnnotatedClass.construct(type.getRawClass(),
+                (useAnnotations ? config.getAnnotationIntrospector() : null), r);
+        return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix).collect();
+    }
+    
+    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.getRawClass(), ai, r);
+        JsonPOJOBuilder.Value builderConfig = (ai == null) ? null : ai.findPOJOBuilderConfig(ac);
+        String mutatorPrefix = (builderConfig == null) ? "with" : builderConfig.withPrefix;
+        return constructPropertyCollector(config, ac, type, forSerialization, mutatorPrefix).collect();
+    }
+
+    /**
+     * Overridable method called for creating {@link POJOPropertiesCollector} instance
+     * to use; override is needed if a custom sub-class is to be used.
+     */
+    protected POJOPropertiesCollector constructPropertyCollector(MapperConfig<?> config,
+            AnnotatedClass ac, JavaType type,
+            boolean forSerialization, String mutatorPrefix)
+    {
+        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.
+     */
+    protected BasicBeanDescription _findCachedDesc(JavaType type)
+    {
+        Class<?> cls = type.getRawClass();
+        if (cls == String.class) {
+            return STRING_DESC;
+        }
+        if (cls == Boolean.TYPE) {
+            return BOOLEAN_DESC;
+        }
+        if (cls == Integer.TYPE) {
+            return INT_DESC;
+        }
+        if (cls == Long.TYPE) {
+            return LONG_DESC;
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
new file mode 100644
index 0000000..b6c8d88
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/BeanPropertyDefinition.java
@@ -0,0 +1,175 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.util.Named;
+
+/**
+ * Simple value classes that contain definitions of properties,
+ * used during introspection of properties to use for
+ * serialization and deserialization purposes.
+ * These instances are created before actual {@link BeanProperty}
+ * instances are created, i.e. they are used earlier in the process
+ * flow, and are typically use to construct actual
+ * {@link BeanProperty} instances.
+ */
+public abstract class BeanPropertyDefinition
+    implements Named
+{
+    /*
+    /**********************************************************
+    /* Fluent factory methods for creating modified copies
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to create a definition with
+     * same settings as this one, but with different
+     * (external) name; that is, one for which
+     * {@link #getName()} would return <code>newName</code>.
+     */
+    public abstract BeanPropertyDefinition withName(String newName);
+    
+    /*
+    /**********************************************************
+    /* Basic property information, name, type
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for name used for external representation (in JSON).
+     */
+    @Override // from Named
+    public abstract String getName();
+
+    /**
+     * Accessor that can be used to determine implicit name from underlying
+     * element(s) before possible renaming. This is the "internal"
+     * name derived from accessor ("x" from "getX"), and is not based on
+     * annotations or naming strategy.
+     */
+    public abstract String getInternalName();
+    
+    /**
+     * Accessor for finding wrapper name to use for property (if any).
+     * 
+     * @since 2.2
+     */
+    public abstract PropertyName getWrapperName();
+
+    /**
+     * Accessor that can be called to check whether property was included
+     * due to an explicit marker (usually annotation), or just by naming
+     * convention.
+     * 
+     * @return True if property was explicitly included (usually by having
+     *   one of components being annotated); false if inclusion was purely
+     *   due to naming or visibility definitions (that is, implicit)
+     */
+    public abstract boolean isExplicitlyIncluded();
+    
+    /*
+    /**********************************************************
+    /* Capabilities
+    /**********************************************************
+     */
+
+    public boolean couldDeserialize() {
+        return getMutator() != null;
+    }
+    public boolean couldSerialize() {
+        return getAccessor() != null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Access to accessors (fields, methods etc)
+    /**********************************************************
+     */
+    
+    public abstract boolean hasGetter();
+    public abstract boolean hasSetter();
+    public abstract boolean hasField();
+    public abstract boolean hasConstructorParameter();
+    
+    public abstract AnnotatedMethod getGetter();
+    public abstract AnnotatedMethod getSetter();
+    public abstract AnnotatedField getField();
+    public abstract AnnotatedParameter getConstructorParameter();
+
+    /**
+     * Method used to find accessor (getter, field to access) to use for accessing
+     * value of the property.
+     * Null if no such member exists.
+     */
+    public abstract AnnotatedMember getAccessor();
+
+    /**
+     * 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();
+
+    /**
+     * Method used to find the property member (getter, setter, field) that has
+     * the highest precedence in current context (getter method when serializing,
+     * if available, and so forth), if any.
+     * 
+     * @since 2.1
+     */
+    public AnnotatedMember getPrimaryMember() {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* More refined access to configuration features
+    /* (usually based on annotations)
+    /* 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.
+     */
+    public Class<?>[] findViews() { return null; }
+
+    /**
+     * Method used to find whether property is part of a bi-directional
+     * reference.
+     */
+    public AnnotationIntrospector.ReferenceProperty findReferenceType() {
+        return null;
+    }
+
+    /**
+     * 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.
+     */
+    public boolean isTypeId() {
+        return false;
+    }
+
+    /**
+     * Method used to check whether this logical property indicates that
+     * value POJOs should be written using additional Object Identifier
+     * (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() {
+        return false;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java
new file mode 100644
index 0000000..df664ac
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ClassIntrospector.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+
+
+/**
+ * Helper class used to introspect features of POJO value classes
+ * used with Jackson. The main use is for finding out
+ * POJO construction (creator) and value access (getters, setters)
+ * methods and annotations that define configuration of using
+ * those methods.
+ */
+public abstract class ClassIntrospector
+{
+    /*
+    /**********************************************************
+    /* Helper interfaces
+    /**********************************************************
+     */
+
+    /**
+     * Interface used for decoupling details of how mix-in annotation
+     * definitions are accessed (via this interface), and how
+     * they are stored (defined by classes that implement the interface)
+     */
+    public interface MixInResolver
+    {
+        /**
+         * Method that will check if there are "mix-in" classes (with mix-in
+         * annotations) for given class
+         */
+        public Class<?> findMixInClassFor(Class<?> cls);
+    }
+
+    protected ClassIntrospector() { }
+	
+    /*
+    /**********************************************************
+    /* Public API: factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Factory method that constructs an introspector that has all
+     * information needed for serialization purposes.
+     */
+    public abstract BeanDescription forSerialization(SerializationConfig cfg,
+    		JavaType type, MixInResolver r);
+
+    /**
+     * Factory method that constructs an introspector that has all
+     * information needed for deserialization purposes.
+     */
+    public abstract BeanDescription forDeserialization(DeserializationConfig cfg,
+    		JavaType type, MixInResolver r);
+
+    /**
+     * Factory method that constructs an introspector that has all
+     * information needed for constructing deserializers that use
+     * intermediate Builder objects.
+     */
+    public abstract BeanDescription forDeserializationWithBuilder(DeserializationConfig cfg,
+    		JavaType type, MixInResolver r);
+    
+    /**
+     * Factory method that constructs an introspector that has
+     * information necessary for creating instances of given
+     * class ("creator"), as well as class annotations, but
+     * no information on member methods
+     */
+    public abstract BeanDescription forCreation(DeserializationConfig cfg, JavaType type,
+            MixInResolver r);
+
+    /**
+     * Factory method that constructs an introspector that only has
+     * information regarding annotations class itself (or its supertypes) has,
+     * but nothing on methods or constructors.
+     */
+    public abstract BeanDescription forClassAnnotations(MapperConfig<?> cfg, JavaType type,
+            MixInResolver r);
+
+    /**
+     * Factory method that constructs an introspector that only has
+     * information regarding annotations class itself has (but NOT including
+     * its supertypes), but nothing on methods or constructors.
+     */
+    public abstract BeanDescription forDirectClassAnnotations(MapperConfig<?> cfg, JavaType type,
+            MixInResolver r);
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
new file mode 100644
index 0000000..8e8dbf1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/JacksonAnnotationIntrospector.java
@@ -0,0 +1,900 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.Version;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.*;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
+import com.fasterxml.jackson.databind.ser.std.RawSerializer;
+import com.fasterxml.jackson.databind.util.Converter;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * {@link AnnotationIntrospector} implementation that handles standard
+ * Jackson annotations.
+ */
+public class JacksonAnnotationIntrospector
+    extends AnnotationIntrospector
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    public JacksonAnnotationIntrospector() { }
+
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+    
+    /*
+    /**********************************************************
+    /* General annotation properties
+    /**********************************************************
+     */
+
+    // TODO: remove in 2.2
+    @Override
+    @Deprecated
+    public boolean isHandled(Annotation ann)
+    {
+        Class<? extends Annotation> acls = ann.annotationType();
+        return acls.getAnnotation(JacksonAnnotation.class) != null;
+    }
+
+    /**
+     * Annotations with meta-annotation {@link JacksonAnnotationsInside}
+     * are considered bundles.
+     */
+    @Override
+    public boolean isAnnotationBundle(Annotation ann)
+    {
+        return ann.annotationType().getAnnotation(JacksonAnnotationsInside.class) != null;
+    }
+    
+    /*
+    /**********************************************************
+    /* General annotations
+    /**********************************************************
+     */
+
+    // default impl is fine:
+    //public String findEnumValue(Enum<?> value) { return value.name(); }
+    
+    /*
+    /**********************************************************
+    /* General class annotations
+    /**********************************************************
+     */
+
+    @Override
+    public PropertyName findRootName(AnnotatedClass ac)
+    {
+        JsonRootName ann = ac.getAnnotation(JsonRootName.class);
+        if (ann == null) {
+            return null;
+        }
+        return new PropertyName(ann.value());
+    }
+
+    @Override
+    public String[] findPropertiesToIgnore(Annotated ac) {
+        JsonIgnoreProperties ignore = ac.getAnnotation(JsonIgnoreProperties.class);
+        return (ignore == null) ? null : ignore.value();
+    }
+
+    @Override
+    public Boolean findIgnoreUnknownProperties(AnnotatedClass ac) {
+        JsonIgnoreProperties ignore = ac.getAnnotation(JsonIgnoreProperties.class);
+        return (ignore == null) ? null : ignore.ignoreUnknown();
+    }
+
+    @Override
+    public Boolean isIgnorableType(AnnotatedClass ac) {
+        JsonIgnoreType ignore = ac.getAnnotation(JsonIgnoreType.class);
+        return (ignore == null) ? null : ignore.value();
+    }
+
+    @Override
+    public Object findFilterId(AnnotatedClass ac)
+    {
+        JsonFilter ann = ac.getAnnotation(JsonFilter.class);
+        if (ann != null) {
+            String id = ann.value();
+            // Empty String is same as not having annotation, to allow overrides
+            if (id.length() > 0) {
+                return id;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Object findNamingStrategy(AnnotatedClass ac)
+    {
+        JsonNaming ann = ac.getAnnotation(JsonNaming.class);
+        return (ann == null) ? null : ann.value();
+    } 
+
+    /*
+    /**********************************************************
+    /* Property auto-detection
+    /**********************************************************
+     */
+    
+    @Override
+    public VisibilityChecker<?> findAutoDetectVisibility(AnnotatedClass ac,
+        VisibilityChecker<?> checker)
+    {
+        JsonAutoDetect ann = ac.getAnnotation(JsonAutoDetect.class);
+        return (ann == null) ? checker : checker.with(ann);
+    }
+
+    /*
+    /**********************************************************
+    /* General member (field, method/constructor) annotations
+    /**********************************************************
+     */
+
+    @Override        
+    public ReferenceProperty findReferenceType(AnnotatedMember member)
+    {
+        JsonManagedReference ref1 = member.getAnnotation(JsonManagedReference.class);
+        if (ref1 != null) {
+            return AnnotationIntrospector.ReferenceProperty.managed(ref1.value());
+        }
+        JsonBackReference ref2 = member.getAnnotation(JsonBackReference.class);
+        if (ref2 != null) {
+            return AnnotationIntrospector.ReferenceProperty.back(ref2.value());
+        }
+        return null;
+    }
+
+    @Override
+    public NameTransformer findUnwrappingNameTransformer(AnnotatedMember member)
+    {
+        JsonUnwrapped ann = member.getAnnotation(JsonUnwrapped.class);
+        // if not enabled, just means annotation is not enabled; not necessarily
+        // that unwrapping should not be done (relevant when using chained introspectors)
+        if (ann == null || !ann.enabled()) {
+            return null;
+        }
+        String prefix = ann.prefix();
+        String suffix = ann.suffix();
+        return NameTransformer.simpleTransformer(prefix, suffix);
+    }
+
+    @Override
+    public boolean hasIgnoreMarker(AnnotatedMember m) {
+        return _isIgnorable(m);
+    }
+
+    @Override
+    public Boolean hasRequiredMarker(AnnotatedMember m)
+    {
+        JsonProperty ann = m.getAnnotation(JsonProperty.class);
+        if (ann != null) {
+            return ann.required();
+        }
+        return null;
+    }
+    
+    @Override
+    public Object findInjectableValueId(AnnotatedMember m)
+    {
+        JacksonInject ann = m.getAnnotation(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) {
+            // slight complication; for setters, type 
+            if (!(m instanceof AnnotatedMethod)) {
+                return m.getRawType().getName();
+            }
+            AnnotatedMethod am = (AnnotatedMethod) m;
+            if (am.getParameterCount() == 0) {
+                return m.getRawType().getName();
+            }
+            return am.getRawParameterType(0).getName();
+        }
+        return id;
+    }
+    
+    /*
+    /**********************************************************
+    /* Class annotations for PM type handling (1.5+)
+    /**********************************************************
+     */
+
+    @Override
+    public TypeResolverBuilder<?> findTypeResolver(MapperConfig<?> config,
+            AnnotatedClass ac, JavaType baseType)
+    {
+        return _findTypeResolver(config, ac, baseType);
+    }
+
+    @Override
+    public TypeResolverBuilder<?> findPropertyTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType baseType)
+    {
+        /* As per definition of @JsonTypeInfo, should only apply to contents of container
+         * (collection, map) types, not container types themselves:
+         */
+        if (baseType.isContainerType()) return null;
+        // No per-member type overrides (yet)
+        return _findTypeResolver(config, am, baseType);
+    }
+
+    @Override
+    public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> config,
+            AnnotatedMember am, JavaType containerType)
+    {
+        /* First: let's ensure property is a container type: caller should have
+         * verified but just to be sure
+         */
+        if (!containerType.isContainerType()) {
+            throw new IllegalArgumentException("Must call method with a container type (got "+containerType+")");
+        }
+        return _findTypeResolver(config, am, containerType);
+    }
+    
+    @Override
+    public List<NamedType> findSubtypes(Annotated a)
+    {
+        JsonSubTypes t = a.getAnnotation(JsonSubTypes.class);
+        if (t == null) return null;
+        JsonSubTypes.Type[] types = t.value();
+        ArrayList<NamedType> result = new ArrayList<NamedType>(types.length);
+        for (JsonSubTypes.Type type : types) {
+            result.add(new NamedType(type.value(), type.name()));
+        }
+        return result;
+    }
+
+    @Override        
+    public String findTypeName(AnnotatedClass ac)
+    {
+        JsonTypeName tn = ac.getAnnotation(JsonTypeName.class);
+        return (tn == null) ? null : tn.value();
+    }
+
+    /*
+    /**********************************************************
+    /* Serialization: general annotations
+    /**********************************************************
+    */
+
+    @Override
+    public Object findSerializer(Annotated a)
+    {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<? extends JsonSerializer<?>> serClass = ann.using();
+            if (serClass != JsonSerializer.None.class) {
+                return serClass;
+            }
+        }
+        
+        /* 18-Oct-2010, tatu: [JACKSON-351] @JsonRawValue handled just here, for now;
+         *  if we need to get raw indicator from other sources need to add
+         *  separate accessor within {@link AnnotationIntrospector} interface.
+         */
+        JsonRawValue annRaw =  a.getAnnotation(JsonRawValue.class);
+        if ((annRaw != null) && annRaw.value()) {
+            // let's construct instance with nominal type:
+            Class<?> cls = a.getRawType();
+            return new RawSerializer<Object>(cls);
+        }       
+        return null;
+    }
+
+    @Override
+    public Class<? extends JsonSerializer<?>> findKeySerializer(Annotated a)
+    {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<? extends JsonSerializer<?>> serClass = ann.keyUsing();
+            if (serClass != JsonSerializer.None.class) {
+                return serClass;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<? extends JsonSerializer<?>> findContentSerializer(Annotated a)
+    {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<? extends JsonSerializer<?>> serClass = ann.contentUsing();
+            if (serClass != JsonSerializer.None.class) {
+                return serClass;
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public JsonInclude.Include findSerializationInclusion(Annotated a, JsonInclude.Include defValue)
+    {
+        JsonInclude inc = a.getAnnotation(JsonInclude.class);
+        if (inc != null) {
+            return inc.value();
+        }
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            @SuppressWarnings("deprecation")
+            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;
+            }
+        }
+        return defValue;
+    }
+
+    @Override
+    public Class<?> findSerializationType(Annotated am)
+    {
+        JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.as();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<?> findSerializationKeyType(Annotated am, JavaType baseType)
+    {
+        JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.keyAs();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<?> findSerializationContentType(Annotated am, JavaType baseType)
+    {
+        JsonSerialize ann = am.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.contentAs();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public JsonSerialize.Typing findSerializationTyping(Annotated a)
+    {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        return (ann == null) ? null : ann.typing();
+    }
+
+    @Override
+    public Object findSerializationConverter(Annotated a) {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<?> def = ann.converter();
+            if (def != Converter.None.class) {
+                return def;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Object findSerializationContentConverter(AnnotatedMember a) {
+        JsonSerialize ann = a.getAnnotation(JsonSerialize.class);
+        if (ann != null) {
+            Class<?> def = ann.contentConverter();
+            if (def != Converter.None.class) {
+                return def;
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public Class<?>[] findViews(Annotated a)
+    {
+        JsonView ann = a.getAnnotation(JsonView.class);
+        return (ann == null) ? null : ann.value();
+    }
+
+    @Override
+    public Boolean isTypeId(AnnotatedMember member) {
+        return member.hasAnnotation(JsonTypeId.class);
+    }
+
+    @Override
+    public ObjectIdInfo findObjectIdInfo(Annotated ann) {
+        JsonIdentityInfo info = ann.getAnnotation(JsonIdentityInfo.class);
+        if (info == null || info.generator() == ObjectIdGenerators.None.class) {
+            return null;
+        }
+        return new ObjectIdInfo(info.property(), info.scope(), info.generator());
+    }
+
+    @Override
+    public ObjectIdInfo findObjectReferenceInfo(Annotated ann, ObjectIdInfo objectIdInfo) {
+        JsonIdentityReference ref = ann.getAnnotation(JsonIdentityReference.class);
+        if (ref != null) {
+            objectIdInfo = objectIdInfo.withAlwaysAsId(ref.alwaysAsId());
+        }
+        return objectIdInfo;
+    }
+    
+    @Override
+    public JsonFormat.Value findFormat(AnnotatedMember member) {
+        return findFormat(member);
+    }
+    
+    @Override
+    public JsonFormat.Value findFormat(Annotated annotated) {
+        JsonFormat ann = annotated.getAnnotation(JsonFormat.class);
+        return (ann == null)  ? null : new JsonFormat.Value(ann);
+    }
+
+    /*
+    /**********************************************************
+    /* Serialization: class annotations
+    /**********************************************************
+     */
+
+    @Override
+    public String[] findSerializationPropertyOrder(AnnotatedClass ac) {
+        JsonPropertyOrder order = ac.getAnnotation(JsonPropertyOrder.class);
+        return (order == null) ? null : order.value();
+    }
+
+    @Override
+    public Boolean findSerializationSortAlphabetically(AnnotatedClass ac) {
+        JsonPropertyOrder order = ac.getAnnotation(JsonPropertyOrder.class);
+        return (order == null) ? null : order.alphabetic();
+    }
+
+    /*
+    /**********************************************************
+    /* Serialization: property annotations
+    /**********************************************************
+     */
+
+    @Override
+    public PropertyName findNameForSerialization(Annotated a)
+    {
+        // [Issue#69], need bit of delegation 
+        // !!! TODO: in 2.2, remove old methods?
+        String name;
+        if (a instanceof AnnotatedField) {
+            name = findSerializationName((AnnotatedField) a);
+        } else if (a instanceof AnnotatedMethod) {
+            name = findSerializationName((AnnotatedMethod) a);
+        } else {
+            name = null;
+        }
+        if (name != null) {
+            if (name.length() == 0) { // empty String means 'default'
+                return PropertyName.USE_DEFAULT;
+            }
+            return new PropertyName(name);
+        }
+        return null;
+    }
+
+    @Override
+    public String findSerializationName(AnnotatedField af)
+    {
+        JsonProperty pann = af.getAnnotation(JsonProperty.class);
+        if (pann != null) {
+            return pann.value();
+        }
+        // Also: having JsonSerialize implies it is such a property
+        // 09-Apr-2010, tatu: Ditto for JsonView
+        if (af.hasAnnotation(JsonSerialize.class) || af.hasAnnotation(JsonView.class)) {
+            return "";
+        }
+        return null;
+    }
+    
+    @Override
+    public String findSerializationName(AnnotatedMethod am)
+    {
+        // @JsonGetter is most specific, has precedence
+        JsonGetter ann = am.getAnnotation(JsonGetter.class);
+        if (ann != null) {
+            return ann.value();
+        }
+        JsonProperty pann = am.getAnnotation(JsonProperty.class);
+        if (pann != null) {
+            return pann.value();
+        }
+        /* 22-May-2009, tatu: And finally, JsonSerialize implies
+         *   that there is a property, although doesn't define name
+         */
+        // 09-Apr-2010, tatu: Ditto for JsonView
+        if (am.hasAnnotation(JsonSerialize.class) || am.hasAnnotation(JsonView.class)) {
+            return "";
+        }
+        return null;
+    }
+
+    @Override
+    public boolean hasAsValueAnnotation(AnnotatedMethod am)
+    {
+        JsonValue ann = am.getAnnotation(JsonValue.class);
+        // value of 'false' means disabled...
+        return (ann != null && ann.value());
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization: general annotations
+    /**********************************************************
+     */
+
+    @Override
+    public Class<? extends JsonDeserializer<?>> findDeserializer(Annotated a)
+    {
+        JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<? extends JsonDeserializer<?>> deserClass = ann.using();
+            if (deserClass != JsonDeserializer.None.class) {
+                return deserClass;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<? extends KeyDeserializer> findKeyDeserializer(Annotated a)
+    {
+        JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<? extends KeyDeserializer> deserClass = ann.keyUsing();
+            if (deserClass != KeyDeserializer.None.class) {
+                return deserClass;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<? extends JsonDeserializer<?>> findContentDeserializer(Annotated a)
+    {
+        JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<? extends JsonDeserializer<?>> deserClass = ann.contentUsing();
+            if (deserClass != JsonDeserializer.None.class) {
+                return deserClass;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<?> findDeserializationType(Annotated am, JavaType baseType)
+    {
+        // Primary annotation, JsonDeserialize
+        JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.as();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<?> findDeserializationKeyType(Annotated am, JavaType baseKeyType)
+    {
+        // Primary annotation, JsonDeserialize
+        JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.keyAs();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Class<?> findDeserializationContentType(Annotated am, JavaType baseContentType)
+    {
+        // Primary annotation, JsonDeserialize
+        JsonDeserialize ann = am.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<?> cls = ann.contentAs();
+            if (cls != NoClass.class) {
+                return cls;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Object findDeserializationConverter(Annotated a)
+    {
+        JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<?> def = ann.converter();
+            if (def != Converter.None.class) {
+                return def;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Object findDeserializationContentConverter(AnnotatedMember a)
+    {
+        JsonDeserialize ann = a.getAnnotation(JsonDeserialize.class);
+        if (ann != null) {
+            Class<?> def = ann.contentConverter();
+            if (def != Converter.None.class) {
+                return def;
+            }
+        }
+        return null;    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization: Class annotations
+    /**********************************************************
+     */
+    
+    @Override
+    public Object findValueInstantiator(AnnotatedClass ac)
+    {
+        JsonValueInstantiator ann = ac.getAnnotation(JsonValueInstantiator.class);
+        // no 'null' marker yet, so:
+        return (ann == null) ? null : ann.value();
+    }
+
+    @Override
+    public Class<?> findPOJOBuilder(AnnotatedClass ac)
+    {
+        JsonDeserialize ann = ac.getAnnotation(JsonDeserialize.class);
+        return ((ann == null) || (ann.builder() == NoClass.class)) ?
+                null : ann.builder();
+    }
+
+    @Override
+    public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac)
+    {
+        JsonPOJOBuilder ann = ac.getAnnotation(JsonPOJOBuilder.class);
+        return (ann == null) ? null : new JsonPOJOBuilder.Value(ann);
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserialization: property annotations
+    /**********************************************************
+     */
+
+    @Override
+    public PropertyName findNameForDeserialization(Annotated a)
+    {
+        // [Issue#69], need bit of delegation 
+        // !!! TODO: in 2.2, remove old methods?
+        String name;
+        if (a instanceof AnnotatedField) {
+            name = findDeserializationName((AnnotatedField) a);
+        } else if (a instanceof AnnotatedMethod) {
+            name = findDeserializationName((AnnotatedMethod) a);
+        } else if (a instanceof AnnotatedParameter) {
+            name = findDeserializationName((AnnotatedParameter) a);
+        } else {
+            name = null;
+        }
+        if (name != null) {
+            if (name.length() == 0) { // empty String means 'default'
+                return PropertyName.USE_DEFAULT;
+            }
+            return new PropertyName(name);
+        }
+        return null;
+    }
+    
+    @Override
+    public String findDeserializationName(AnnotatedMethod am)
+    {
+        // @JsonSetter has precedence over @JsonProperty, being more specific
+        JsonSetter ann = am.getAnnotation(JsonSetter.class);
+        if (ann != null) {
+            return ann.value();
+        }
+        JsonProperty pann = am.getAnnotation(JsonProperty.class);
+        if (pann != null) {
+            return pann.value();
+        }
+        // @JsonSerialize implies that there is a property, but no name
+        // 09-Apr-2010, tatu: Ditto for JsonView
+        // 19-Oct-2011, tatu: And JsonBackReference/JsonManagedReference
+    	if (am.hasAnnotation(JsonDeserialize.class)
+    	        || am.hasAnnotation(JsonView.class)
+                || am.hasAnnotation(JsonBackReference.class)
+                || am.hasAnnotation(JsonManagedReference.class)
+    	        ) {
+            return "";
+        }
+        return null;
+    }
+
+    @Override
+    public String findDeserializationName(AnnotatedField af)
+    {
+        JsonProperty pann = af.getAnnotation(JsonProperty.class);
+        if (pann != null) {
+            return pann.value();
+        }
+        // Also: having JsonDeserialize implies it is such a property
+        // 09-Apr-2010, tatu: Ditto for JsonView
+        if (af.hasAnnotation(JsonDeserialize.class)
+                || af.hasAnnotation(JsonView.class)
+                || af.hasAnnotation(JsonBackReference.class)
+                || af.hasAnnotation(JsonManagedReference.class)
+                ) {
+            return "";
+        }
+        return null;
+    }
+    @Override
+    public String findDeserializationName(AnnotatedParameter param)
+    {
+        if (param != null) {
+            JsonProperty pann = param.getAnnotation(JsonProperty.class);
+            if (pann != null) {
+                return pann.value();
+            }
+            /* And can not use JsonDeserialize as we can not use
+             * name auto-detection (names of local variables including
+             * parameters are not necessarily preserved in bytecode)
+             */
+        }
+        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)
+         */
+        return am.hasAnnotation(JsonAnySetter.class);
+    }
+
+    @Override
+    public boolean hasAnyGetterAnnotation(AnnotatedMethod am)
+    {
+        /* No dedicated disabling; regular @JsonIgnore used
+         * if needs to be ignored (handled separately
+         */
+        return am.hasAnnotation(JsonAnyGetter.class);
+    }
+    
+    @Override
+    public boolean hasCreatorAnnotation(Annotated a)
+    {
+        /* No dedicated disabling; regular @JsonIgnore used
+         * if needs to be ignored (and if so, is handled prior
+         * to this method getting called)
+         */
+        return a.hasAnnotation(JsonCreator.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected boolean _isIgnorable(Annotated a)
+    {
+        JsonIgnore ann = a.getAnnotation(JsonIgnore.class);
+        return (ann != null && ann.value());
+    }
+
+    /**
+     * Helper method called to construct and initialize instance of {@link TypeResolverBuilder}
+     * if given annotated element indicates one is needed.
+     */
+    protected TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config,
+            Annotated ann, JavaType baseType)
+    {
+        // First: maybe we have explicit type resolver?
+        TypeResolverBuilder<?> b;
+        JsonTypeInfo info = ann.getAnnotation(JsonTypeInfo.class);
+        JsonTypeResolver resAnn = ann.getAnnotation(JsonTypeResolver.class);
+        
+        if (resAnn != null) {
+            if (info == null) {
+                return null;
+            }
+            /* let's not try to force access override (would need to pass
+             * settings through if we did, since that's not doable on some
+             * platforms)
+             */
+            b = config.typeResolverBuilderInstance(ann, resAnn.value());
+        } else { // if not, use standard one, if indicated by annotations
+            if (info == null) {
+                return null;
+            }
+            // bit special; must return 'marker' to block use of default typing:
+            if (info.use() == JsonTypeInfo.Id.NONE) {
+                return _constructNoTypeResolverBuilder();
+            }
+            b = _constructStdTypeResolverBuilder();
+        }
+        // Does it define a custom type id resolver?
+        JsonTypeIdResolver idResInfo = ann.getAnnotation(JsonTypeIdResolver.class);
+        TypeIdResolver idRes = (idResInfo == null) ? null
+                : config.typeIdResolverInstance(ann, idResInfo.value());
+        if (idRes != null) { // [JACKSON-359]
+            idRes.init(baseType);
+        }
+        b = b.init(info.use(), idRes);
+        /* 13-Aug-2011, tatu: One complication wrt [JACKSON-453]; external id
+         *   only works for properties; so if declared for a Class, we will need
+         *   to map it to "PROPERTY" instead of "EXTERNAL_PROPERTY"
+         */
+        JsonTypeInfo.As inclusion = info.include();
+        if (inclusion == JsonTypeInfo.As.EXTERNAL_PROPERTY && (ann instanceof AnnotatedClass)) {
+            inclusion = JsonTypeInfo.As.PROPERTY;
+        }
+        b = b.inclusion(inclusion);
+        b = b.typeProperty(info.property());
+        Class<?> defaultImpl = info.defaultImpl();
+        if (defaultImpl != JsonTypeInfo.None.class) {
+            b = b.defaultImpl(defaultImpl);
+        }
+        b = b.typeIdVisibility(info.visible());
+        return b;
+    }
+
+    /**
+     * Helper method for constructing standard {@link TypeResolverBuilder}
+     * implementation.
+     */
+    protected StdTypeResolverBuilder _constructStdTypeResolverBuilder() {
+        return new StdTypeResolverBuilder();
+    }
+
+    /**
+     * Helper method for dealing with "no type info" marker; can't be null
+     * (as it'd be replaced by default typing)
+     */
+    protected StdTypeResolverBuilder _constructNoTypeResolverBuilder() {
+        return StdTypeResolverBuilder.noTypeInfoBuilder();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java
new file mode 100644
index 0000000..b8ee086
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/MemberKey.java
@@ -0,0 +1,83 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Helper class needed to be able to efficiently access class
+ * member functions ({@link Method}s and {@link Constructor}s)
+ * in {@link java.util.Map}s.
+ */
+public final class MemberKey
+{
+    final static Class<?>[] NO_CLASSES = new Class<?>[0];
+
+    final String _name;
+    final Class<?>[] _argTypes;
+
+    public MemberKey(Method m)
+    {
+        this(m.getName(), m.getParameterTypes());
+    }
+
+    public MemberKey(Constructor<?> ctor)
+    {
+        this("", ctor.getParameterTypes());
+    }
+
+    public MemberKey(String name, Class<?>[] argTypes)
+    {
+        _name = name;
+        _argTypes = (argTypes == null) ? NO_CLASSES : argTypes;
+    }
+
+    @Override
+    public String toString() {
+        return _name + "(" + _argTypes.length+"-args)";
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return _name.hashCode() + _argTypes.length;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) {
+            return false;
+        }
+        MemberKey other = (MemberKey) o;
+        if (!_name.equals(other._name)) {
+            return false;
+        }
+        Class<?>[] otherArgs = other._argTypes;
+        int len = _argTypes.length;
+        if (otherArgs.length != len) {
+            return false;
+        }
+        for (int i = 0; i < len; ++i) {
+            Class<?> type1 = otherArgs[i];
+            Class<?> type2 = _argTypes[i];
+            if (type1 == type2) {
+                continue;
+            }
+            /* 23-Feb-2009, tatu: Are there any cases where we would have to
+             *   consider some narrowing conversions or such? For now let's
+             *   assume exact type match is enough
+             */
+            /* 07-Apr-2009, tatu: Indeed there are (see [JACKSON-97]).
+             *    This happens with generics when a bound is specified.
+             *    I hope this works; check here must be transitive
+             */
+            if (type1.isAssignableFrom(type2) || type2.isAssignableFrom(type1)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java b/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java
new file mode 100644
index 0000000..bd7881e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/NopAnnotationIntrospector.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.core.Version;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Dummy, "no-operation" implementation of {@link AnnotationIntrospector}.
+ * Can be used as is to suppress handling of annotations; or as a basis
+ * for simple configuration overrides (whether based on annotations or not).
+ */
+public abstract class NopAnnotationIntrospector
+    extends AnnotationIntrospector
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Static immutable and shareable instance that can be used as
+     * "null" introspector: one that never finds any annotation
+     * information.
+     */
+    public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public Version version() {
+            return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+       }
+    };
+
+    @Override
+    public Version version() {
+        return Version.unknownVersion();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java
new file mode 100644
index 0000000..8eb6de2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/ObjectIdInfo.java
@@ -0,0 +1,50 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+/**
+ * Container object that encapsulates information usually
+ * derived from {@link JsonIdentityInfo} annotation or its
+ * custom alternatives
+ */
+public class ObjectIdInfo
+{
+    protected final String _propertyName;
+    protected final Class<? extends ObjectIdGenerator<?>> _generator;
+    protected final Class<?> _scope;
+    protected final boolean _alwaysAsId;
+
+    public ObjectIdInfo(String prop, Class<?> scope, Class<? extends ObjectIdGenerator<?>> gen) {
+        this(prop, scope, gen, false);
+    }
+
+    protected ObjectIdInfo(String prop, Class<?> scope, Class<? extends ObjectIdGenerator<?>> gen,
+            boolean alwaysAsId)
+    {
+        _propertyName = prop;
+        _scope = scope;
+        _generator = gen;
+        _alwaysAsId = alwaysAsId;
+    }
+
+    public ObjectIdInfo withAlwaysAsId(boolean state) {
+        if (_alwaysAsId == state) {
+            return this;
+        }
+        return new ObjectIdInfo(_propertyName, _scope, _generator, state);
+    }
+    
+    public String getPropertyName() { return _propertyName; }
+    public Class<?> getScope() { return _scope; }
+    public Class<? extends ObjectIdGenerator<?>> getGeneratorType() { return _generator; }
+    public boolean getAlwaysAsId() { return _alwaysAsId; }
+
+    @Override
+    public String toString() {
+        return "ObjectIdInfo: propName="+_propertyName
+                +", scope="+(_scope == null ? "null" : _scope.getName())
+                +", generatorType="+(_generator == null ? "null" : _generator.getName())
+                +", 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
new file mode 100644
index 0000000..9d7eeb3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollector.java
@@ -0,0 +1,837 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+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;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Helper class used for aggregating information about all possible
+ * properties of a POJO.
+ */
+public class POJOPropertiesCollector
+{
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+    
+    /**
+     * Configuration settings
+     */
+    protected final MapperConfig<?> _config;
+
+    /**
+     * True if introspection is done for serialization (giving
+     *   precedence for serialization annotations), or not (false, deserialization)
+     */
+    protected final boolean _forSerialization;
+    
+    /**
+     * Type of POJO for which properties are being collected.
+     */
+    protected final JavaType _type;
+
+    /**
+     * Low-level introspected class information (methods, fields etc)
+     */
+    protected final AnnotatedClass _classDef;
+
+    protected final VisibilityChecker<?> _visibilityChecker;
+
+    protected final AnnotationIntrospector _annotationIntrospector;
+
+    /**
+     * Prefix used by auto-detected mutators ("setters"): usually "set",
+     * but differs for builder objects ("with" by default).
+     */
+    protected final String _mutatorPrefix;
+    
+    /*
+    /**********************************************************
+    /* Collected property information
+    /**********************************************************
+     */
+
+    /**
+     * Set of logical property information collected so far
+     */
+    protected final LinkedHashMap<String, POJOPropertyBuilder> _properties
+        = new LinkedHashMap<String, POJOPropertyBuilder>();
+
+    protected LinkedList<POJOPropertyBuilder> _creatorProperties = null;
+    
+    protected LinkedList<AnnotatedMember> _anyGetters = null;
+
+    protected LinkedList<AnnotatedMethod> _anySetters = null;
+
+    /**
+     * Method(s) marked with 'JsonValue' annotation
+     */
+    protected LinkedList<AnnotatedMethod> _jsonValueGetters = null;
+
+    /**
+     * Lazily collected list of properties that can be implicitly
+     * ignored during serialization; only updated when collecting
+     * information for deserialization purposes
+     */
+    protected HashSet<String> _ignoredPropertyNames;
+
+    /**
+     * Lazily collected list of members that were annotated to
+     * indicate that they represent mutators for deserializer
+     * value injection.
+     */
+    protected LinkedHashMap<Object, AnnotatedMember> _injectables;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected POJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization,
+            JavaType type, AnnotatedClass classDef, String mutatorPrefix)
+    {
+        _config = config;
+        _forSerialization = forSerialization;
+        _type = type;
+        _classDef = classDef;
+        _mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix;
+        _annotationIntrospector = config.isAnnotationProcessingEnabled() ?
+                _config.getAnnotationIntrospector() : null;
+        if (_annotationIntrospector == null) {
+            _visibilityChecker = _config.getDefaultVisibilityChecker();
+        } else {
+            _visibilityChecker = _annotationIntrospector.findAutoDetectVisibility(classDef,
+                    _config.getDefaultVisibilityChecker());
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    public MapperConfig<?> getConfig() {
+        return _config;
+    }
+
+    public JavaType getType() {
+        return _type;
+    }
+    
+    public AnnotatedClass getClassDef() {
+        return _classDef;
+    }
+
+    public AnnotationIntrospector getAnnotationIntrospector() {
+        return _annotationIntrospector;
+    }
+    
+    public List<BeanPropertyDefinition> getProperties() {
+        // make sure we return a copy, so caller can remove entries if need be:
+        return new ArrayList<BeanPropertyDefinition>(_properties.values());
+    }
+
+    public Map<Object, AnnotatedMember> getInjectables() {
+        return _injectables;
+    }
+    
+    public AnnotatedMethod getJsonValueMethod()
+    {
+        // 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)+")");
+            }
+            // otherwise we won't greatly care
+            return _jsonValueGetters.get(0);
+        }
+        return null;
+    }
+
+    public AnnotatedMember getAnyGetter()
+    {
+        if (_anyGetters != null) {
+            if (_anyGetters.size() > 1) {
+                reportProblem("Multiple 'any-getters' defined ("+_anyGetters.get(0)+" vs "
+                        +_anyGetters.get(1)+")");
+            }
+            return _anyGetters.getFirst();
+        }        
+        return null;
+    }
+
+    public AnnotatedMethod getAnySetterMethod()
+    {
+        if (_anySetters != null) {
+            if (_anySetters.size() > 1) {
+                reportProblem("Multiple 'any-setters' defined ("+_anySetters.get(0)+" vs "
+                        +_anySetters.get(1)+")");
+            }
+            return _anySetters.getFirst();
+        }
+        return null;
+    }
+
+    public Set<String> getIgnoredPropertyNames() {
+        return _ignoredPropertyNames;
+    }
+
+    /**
+     * Accessor to find out whether type specified requires inclusion
+     * of Object Identifier.
+     */
+    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);
+        }
+        return info;
+    }
+
+    /**
+     * Method for finding Class to use as POJO builder, if any.
+     */
+    public Class<?> findPOJOBuilderClass()
+    {
+    	return _annotationIntrospector.findPOJOBuilder(_classDef);
+    }
+    
+    // for unit tests:
+    protected Map<String, POJOPropertyBuilder> getPropertyMap() {
+        return _properties;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API: main-level collection
+    /**********************************************************
+     */
+
+    /**
+     * Method that orchestrates collection activities, and needs to be called
+     * after creating the instance.
+     */
+    public POJOPropertiesCollector collect()
+    {
+        _properties.clear();
+        
+        // First: gather basic data
+        _addFields();
+        _addMethods();
+        _addCreators();
+        _addInjectables();
+
+        // Remove ignored properties, individual entries
+        _removeUnwantedProperties();
+
+        // Rename remaining properties
+        _renameProperties();
+        // And use custom naming strategy, if applicable...
+        PropertyNamingStrategy naming = _findNamingStrategy();
+        if (naming != null) {
+            _renameUsing(naming);
+        }
+
+        /* Sort by visibility (explicit over implicit); drop all but first
+         * of member type (getter, setter etc) if there is visibility
+         * difference
+         */
+        for (POJOPropertyBuilder property : _properties.values()) {
+            property.trimByVisibility();
+        }
+
+        // and then "merge" annotations
+        for (POJOPropertyBuilder property : _properties.values()) {
+            property.mergeAnnotations(_forSerialization);
+        }
+
+        /* and, if required, apply wrapper name: note, MUST be done after
+         * annotations are merged.
+         */
+        if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) {
+            _renameWithWrappers();
+        }
+        
+        // well, almost last: there's still ordering...
+        _sortProperties();
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable internal methods, sorting, other stuff
+    /**********************************************************
+     */
+    
+    /* First, order by [JACKSON-90] (explicit ordering and/or alphabetic)
+     * and then for [JACKSON-170] (implicitly order creator properties before others)
+     */
+    protected void _sortProperties()
+    {
+        // Then how about explicit ordering?
+        AnnotationIntrospector intr = _annotationIntrospector;
+        boolean sort;
+        Boolean alpha = (intr == null) ? null : intr.findSerializationSortAlphabetically(_classDef);
+        
+        if (alpha == null) {
+            sort = _config.shouldSortPropertiesAlphabetically();
+        } else {
+            sort = alpha.booleanValue();
+        }
+        String[] propertyOrder = (intr == null) ? null : intr.findSerializationPropertyOrder(_classDef);
+        
+        // no sorting? no need to shuffle, then
+        if (!sort && (_creatorProperties == null) && (propertyOrder == null)) {
+            return;
+        }
+        int size = _properties.size();
+        Map<String, POJOPropertyBuilder> all;
+        // Need to (re)sort alphabetically?
+        if (sort) {
+            all = new TreeMap<String,POJOPropertyBuilder>();
+        } else {
+            all = new LinkedHashMap<String,POJOPropertyBuilder>(size+size);
+        }
+
+        for (POJOPropertyBuilder prop : _properties.values()) {
+            all.put(prop.getName(), prop);
+        }
+        Map<String,POJOPropertyBuilder> ordered = new LinkedHashMap<String,POJOPropertyBuilder>(size+size);
+        // Ok: primarily by explicit order
+        if (propertyOrder != null) {
+            for (String name : propertyOrder) {
+                POJOPropertyBuilder w = all.get(name);
+                if (w == null) { // also, as per [JACKSON-268], we will allow use of "implicit" names
+                    for (POJOPropertyBuilder prop : _properties.values()) {
+                        if (name.equals(prop.getInternalName())) {
+                            w = prop;
+                            // plus re-map to external name, to avoid dups:
+                            name = prop.getName();
+                            break;
+                        }
+                    }
+                }
+                if (w != null) {
+                    ordered.put(name, w);
+                }
+            }
+        }
+        // And secondly by sorting Creator properties before other unordered properties
+        if (_creatorProperties != null) {
+            for (POJOPropertyBuilder prop : _creatorProperties) {
+                ordered.put(prop.getName(), prop);
+            }
+        }
+        // And finally whatever is left (trying to put again will not change ordering)
+        ordered.putAll(all);
+        
+        _properties.clear();
+        _properties.putAll(ordered);
+    }        
+    
+    /*
+    /**********************************************************
+    /* Overridable internal methods, adding members
+    /**********************************************************
+     */
+    
+    /**
+     * Method for collecting basic information on all fields found
+     */
+    protected void _addFields()
+    {
+        final AnnotationIntrospector ai = _annotationIntrospector;
+        /* 28-Mar-2013, tatu: For deserialization we may also want to remove
+         *   final fields, as often they won't make very good mutators...
+         *   (although, maybe surprisingly, JVM _can_ force setting of such fields!)
+         */
+        final boolean pruneFinalFields = !_forSerialization && !_config.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS);
+        
+        for (AnnotatedField f : _classDef.fields()) {
+            String implName = f.getName();
+
+            String explName;
+            if (ai == null) {
+                explName = null;
+            } else if (_forSerialization) {
+                /* 18-Aug-2011, tatu: As per existing unit tests, we should only
+                 *   use serialization annotation (@JsonSerializer) when serializing
+                 *   fields, and similarly for deserialize-only annotations... so
+                 *   no fallbacks in this particular case.
+                 */
+                PropertyName pn = ai.findNameForSerialization(f);
+                explName = (pn == null) ? null : pn.getSimpleName();
+            } else {
+                PropertyName pn = ai.findNameForDeserialization(f);
+                explName = (pn == null) ? null : pn.getSimpleName();
+            }
+            if ("".equals(explName)) { // empty String meaning "use default name", here just means "same as field name"
+                explName = implName;
+            }
+            // having explicit name means that field is visible; otherwise need to check the rules
+            boolean visible = (explName != null);
+            if (!visible) {
+                visible = _visibilityChecker.isFieldVisible(f);
+            }
+            // and finally, may also have explicit ignoral
+            boolean ignored = (ai != null) && ai.hasIgnoreMarker(f);
+            /* [Issue#190]: this is the place to prune final fields, if they are not
+             *  to be used as mutators. Must verify they are not explicitly included.
+             *  Also: if 'ignored' is set, need to included until a later point, to
+             *  avoid losing ignoral information.
+             */
+            if (pruneFinalFields && (explName == null) && !ignored && Modifier.isFinal(f.getModifiers())) {
+                continue;
+            }
+            
+            _property(implName).addField(f, explName, visible, ignored);
+        }
+    }
+
+    /**
+     * Method for collecting basic information on constructor(s) found
+     */
+    protected void _addCreators()
+    {
+        final AnnotationIntrospector ai = _annotationIntrospector;
+        // can be null if annotation processing is disabled...
+        if (ai == null) {
+            return;
+        }
+        for (AnnotatedConstructor ctor : _classDef.getConstructors()) {
+            if (_creatorProperties == null) {
+                _creatorProperties = new LinkedList<POJOPropertyBuilder>();
+            }
+            for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) {
+                AnnotatedParameter param = ctor.getParameter(i);
+                PropertyName pn = ai.findNameForDeserialization(param);
+                String name = (pn == null) ? null : pn.getSimpleName();
+                // is it legal not to have name?
+                if (name != null) {
+                    // shouldn't need to worry about @JsonIgnore (no real point, so)
+                    POJOPropertyBuilder prop = _property(name);
+                    prop.addCtor(param, name, true, false);
+                    _creatorProperties.add(prop);
+                }
+            }
+        }
+        for (AnnotatedMethod factory : _classDef.getStaticMethods()) {
+            if (_creatorProperties == null) {
+                _creatorProperties = new LinkedList<POJOPropertyBuilder>();
+            }
+            for (int i = 0, len = factory.getParameterCount(); i < len; ++i) {
+                AnnotatedParameter param = factory.getParameter(i);
+                PropertyName pn = ai.findNameForDeserialization(param);
+                String name = (pn == null) ? null : pn.getSimpleName();
+                // is it legal not to have name?
+                if (name != null) {
+                    // shouldn't need to worry about @JsonIgnore (no real point, so)
+                    POJOPropertyBuilder prop = _property(name);
+                    prop.addCtor(param, name, true, false);
+                    _creatorProperties.add(prop);
+                }
+            }
+        }
+    }
+
+    /**
+     * Method for collecting basic information on all fields found
+     */
+    protected void _addMethods()
+    {
+        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
+             * naming convention or are explicitly marked: just being visible
+             * is not enough (unlike with fields)
+             */
+            int argCount = m.getParameterCount();
+            if (argCount == 0) { // getters (including 'any getter')
+            	_addGetterMethod(m, ai);
+            } else if (argCount == 1) { // setters
+            	_addSetterMethod(m, ai);
+            } else if (argCount == 2) { // any getter?
+                if (ai != null  && ai.hasAnySetterAnnotation(m)) {
+                    if (_anySetters == null) {
+                        _anySetters = new LinkedList<AnnotatedMethod>();
+                    }
+                    _anySetters.add(m);
+                }
+            }
+        }
+    }
+
+    protected void _addGetterMethod(AnnotatedMethod m, AnnotationIntrospector ai)
+    {
+        // any getter?
+        if (ai != null) {
+            if (ai.hasAnyGetterAnnotation(m)) {
+                if (_anyGetters == null) {
+                    _anyGetters = new LinkedList<AnnotatedMember>();
+                }
+                _anyGetters.add(m);
+                return;
+            }
+            // @JsonValue?
+            if (ai.hasAsValueAnnotation(m)) {
+                if (_jsonValueGetters == null) {
+                    _jsonValueGetters = new LinkedList<AnnotatedMethod>();
+                }
+                _jsonValueGetters.add(m);
+                return;
+            }
+        }
+        String implName; // from naming convention
+        boolean visible;
+        
+        PropertyName pn = (ai == null) ? null : ai.findNameForSerialization(m);
+        String explName = (pn == null) ? null : pn.getSimpleName();
+        if (explName == null) { // no explicit name; must follow naming convention
+            implName = BeanUtil.okNameForRegularGetter(m, m.getName());
+            if (implName == null) { // if not, must skip
+                implName = BeanUtil.okNameForIsGetter(m, m.getName());
+                if (implName == null) {
+                    return;
+                }
+                visible = _visibilityChecker.isIsGetterVisible(m);
+            } else {
+                visible = _visibilityChecker.isGetterVisible(m);
+            }
+        } else { // explicit indication of inclusion, but may be empty
+            // we still need implicit name to link with other pieces
+            implName = BeanUtil.okNameForGetter(m);
+            // if not regular getter name, use method name as is
+            if (implName == null) {
+                implName = m.getName();
+            }
+            if (explName.length() == 0) {
+                explName = implName;
+            }
+            visible = true;
+        }
+        boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
+        _property(implName).addGetter(m, explName, visible, ignore);
+    }
+
+    protected void _addSetterMethod(AnnotatedMethod m, AnnotationIntrospector ai)
+    {
+        String implName; // from naming convention
+        boolean visible;
+        PropertyName pn = (ai == null) ? null : ai.findNameForDeserialization(m);
+        String explName = (pn == null) ? null : pn.getSimpleName();
+        if (explName == null) { // no explicit name; must follow naming convention
+            implName = BeanUtil.okNameForMutator(m, _mutatorPrefix);
+            if (implName == null) { // if not, must skip
+            	return;
+            }
+            visible = _visibilityChecker.isSetterVisible(m);
+        } else { // explicit indication of inclusion, but may be empty
+            // we still need implicit name to link with other pieces
+            implName = BeanUtil.okNameForMutator(m, _mutatorPrefix);
+            // if not regular getter name, use method name as is
+            if (implName == null) {
+                implName = m.getName();
+            }
+            if (explName.length() == 0) { 
+                explName = implName;
+            }
+            visible = true;
+        }
+        boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m);
+        _property(implName).addSetter(m, explName, visible, ignore);
+    }
+    
+    protected void _addInjectables()
+    {
+        final AnnotationIntrospector ai = _annotationIntrospector;
+        if (ai == null) {
+            return;
+        }
+        
+        // first fields, then methods
+        for (AnnotatedField f : _classDef.fields()) {
+            _doAddInjectable(ai.findInjectableValueId(f), f);
+        }
+        
+        for (AnnotatedMethod m : _classDef.memberMethods()) {
+            /* for now, only allow injection of a single arg
+             * (to be changed in future)
+             */
+            if (m.getParameterCount() != 1) {
+                continue;
+            }
+            _doAddInjectable(ai.findInjectableValueId(m), m);
+        }
+    }
+
+    protected void _doAddInjectable(Object id, AnnotatedMember m)
+    {
+        if (id == null) {
+            return;
+        }
+        if (_injectables == null) {
+            _injectables = new LinkedHashMap<Object, AnnotatedMember>();
+        }
+        AnnotatedMember prev = _injectables.put(id, m);
+        if (prev != null) {
+            String type = (id == null) ? "[null]" : id.getClass().getName();
+            throw new IllegalArgumentException("Duplicate injectable value with id '"
+                    +String.valueOf(id)+"' (of type "+type+")");
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods; removing ignored properties
+    /**********************************************************
+     */
+
+    /**
+     * Method called to get rid of candidate properties that are marked
+     * as ignored, or that are not visible.
+     */
+    protected void _removeUnwantedProperties()
+    {
+        Iterator<Map.Entry<String,POJOPropertyBuilder>> it = _properties.entrySet().iterator();
+        final boolean forceNonVisibleRemoval = !_config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS);
+
+        while (it.hasNext()) {
+            Map.Entry<String, POJOPropertyBuilder> entry = it.next();
+            POJOPropertyBuilder prop = entry.getValue();
+
+            // First: if nothing visible, just remove altogether
+            if (!prop.anyVisible()) {
+                it.remove();
+                continue;
+            }
+            // Otherwise, check ignorals
+            if (prop.anyIgnorals()) {
+                // first: if one or more ignorals, and no explicit markers, remove the whole thing
+                if (!prop.isExplicitlyIncluded()) {
+                    it.remove();
+                    _addIgnored(prop.getName());
+                    continue;
+                }
+                // otherwise just remove ones marked to be ignored
+                prop.removeIgnored();
+                if (!_forSerialization && !prop.couldDeserialize()) {
+                    _addIgnored(prop.getName());
+                }
+            }
+            // and finally, handle removal of individual non-visible elements
+            prop.removeNonVisible(forceNonVisibleRemoval);
+        }
+    }
+    
+    private void _addIgnored(String name)
+    {
+        if (!_forSerialization) {
+            if (_ignoredPropertyNames == null) {
+                _ignoredPropertyNames = new HashSet<String>();
+            }
+            _ignoredPropertyNames.add(name);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods; renaming properties
+    /**********************************************************
+     */
+
+    protected void _renameProperties()
+    {
+        // With renaming need to do in phases: first, find properties to rename
+        Iterator<Map.Entry<String,POJOPropertyBuilder>> it = _properties.entrySet().iterator();
+        LinkedList<POJOPropertyBuilder> renamed = null;
+        while (it.hasNext()) {
+            Map.Entry<String, POJOPropertyBuilder> entry = it.next();
+            POJOPropertyBuilder prop = entry.getValue();
+            String newName = prop.findNewName();
+            if (newName != null) {
+                if (renamed == null) {
+                    renamed = new LinkedList<POJOPropertyBuilder>();
+                }
+                prop = prop.withName(newName);
+                renamed.add(prop);
+                it.remove();
+            }
+        }
+        
+        // and if any were renamed, merge back in...
+        if (renamed != null) {
+            for (POJOPropertyBuilder prop : renamed) {
+                String name = prop.getName();
+                POJOPropertyBuilder old = _properties.get(name);
+                if (old == null) {
+                    _properties.put(name, prop);
+                } else {
+                    old.addAll(prop);
+                }
+
+                // replace the creatorProperty too, if there is one
+                if (_creatorProperties != null) {
+                    for (int i = 0; i < _creatorProperties.size(); ++i) {
+                        if (_creatorProperties.get(i).getInternalName() == prop.getInternalName()) {
+                            _creatorProperties.set(i, prop);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected void _renameUsing(PropertyNamingStrategy naming)
+    {
+        POJOPropertyBuilder[] props = _properties.values().toArray(new POJOPropertyBuilder[_properties.size()]);
+        _properties.clear();
+        for (POJOPropertyBuilder prop : props) {
+            String name = prop.getName();
+            if (_forSerialization) {
+                if (prop.hasGetter()) {
+                    name = naming.nameForGetterMethod(_config, prop.getGetter(), name);
+                } else if (prop.hasField()) {
+                    name = naming.nameForField(_config, prop.getField(), name);
+                }
+            } else {
+                if (prop.hasSetter()) {
+                    name = naming.nameForSetterMethod(_config, prop.getSetter(), name);
+                } else if (prop.hasConstructorParameter()) {
+                    name = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), name);
+                } else if (prop.hasField()) {
+                    name = naming.nameForField(_config, prop.getField(), name);
+                } else if (prop.hasGetter()) {
+                    /* Plus, when getter-as-setter is used, need to convert that too..
+                     * (should we verify that's enabled? For now, assume it's ok always)
+                     */
+                    name = naming.nameForGetterMethod(_config, prop.getGetter(), name);
+                }
+            }
+            if (!name.equals(prop.getName())) {
+                prop = prop.withName(name);
+            }
+            /* As per [JACKSON-687], need to consider case where there may already be
+             * something in there...
+             */
+            POJOPropertyBuilder old = _properties.get(name);
+            if (old == null) {
+                _properties.put(name, prop);
+            } else {
+                old.addAll(prop);
+            }
+        }
+    }
+
+    protected void _renameWithWrappers()
+    {
+        /* 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME',
+         *   need another round of renaming...
+         */
+        Iterator<Map.Entry<String,POJOPropertyBuilder>> it = _properties.entrySet().iterator();
+        LinkedList<POJOPropertyBuilder> renamed = null;
+        while (it.hasNext()) {
+            Map.Entry<String, POJOPropertyBuilder> entry = it.next();
+            POJOPropertyBuilder prop = entry.getValue();
+            AnnotatedMember member = prop.getPrimaryMember();
+            if (member == null) {
+                continue;
+            }
+            PropertyName wrapperName = _annotationIntrospector.findWrapperName(member);
+            if (wrapperName == null || !wrapperName.hasSimpleName()) {
+                continue;
+            }
+            String name = wrapperName.getSimpleName();
+            if (!name.equals(prop.getName())) {
+                if (renamed == null) {
+                    renamed = new LinkedList<POJOPropertyBuilder>();
+                }
+                prop = prop.withName(name);
+                renamed.add(prop);
+                it.remove();
+            }
+        }
+        // and if any were renamed, merge back in...
+        if (renamed != null) {
+            for (POJOPropertyBuilder prop : renamed) {
+                String name = prop.getName();
+                POJOPropertyBuilder old = _properties.get(name);
+                if (old == null) {
+                    _properties.put(name, prop);
+                } else {
+                    old.addAll(prop);
+                }
+            }
+        }
+    }
+    
+    
+    /*
+    /**********************************************************
+    /* Internal methods; helpers
+    /**********************************************************
+     */
+
+    protected void reportProblem(String msg) {
+        throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg);
+    }
+    
+    protected POJOPropertyBuilder _property(String implName)
+    {
+        POJOPropertyBuilder prop = _properties.get(implName);
+        if (prop == null) {
+            prop = new POJOPropertyBuilder(implName, _annotationIntrospector,
+                    _forSerialization);
+            _properties.put(implName, prop);
+        }
+        return prop;
+    }
+
+    private PropertyNamingStrategy _findNamingStrategy()
+    {
+        Object namingDef = (_annotationIntrospector == null)? null
+                : _annotationIntrospector.findNamingStrategy(_classDef);
+        if (namingDef == null) {
+            return _config.getPropertyNamingStrategy();
+        }
+        if (namingDef instanceof PropertyNamingStrategy) {
+            return (PropertyNamingStrategy) namingDef;
+        }
+        /* Alas, there's no way to force return type of "either class
+         * X or Y" -- need to throw an exception after the fact
+         */
+        if (!(namingDef instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type "
+                    +namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class<PropertyNamingStrategy> instead");
+        }
+        Class<?> namingClass = (Class<?>)namingDef;
+        if (!PropertyNamingStrategy.class.isAssignableFrom(namingClass)) {
+            throw new IllegalStateException("AnnotationIntrospector returned Class "
+                    +namingClass.getName()+"; expected Class<PropertyNamingStrategy>");
+        }
+        HandlerInstantiator hi = _config.getHandlerInstantiator();
+        if (hi != null) {
+            PropertyNamingStrategy pns = hi.namingStrategyInstance(_config, _classDef, namingClass);
+            if (pns != null) {
+                return pns;
+            }
+        }
+        return (PropertyNamingStrategy) ClassUtil.createInstance(namingClass,
+                    _config.canOverrideAccessModifiers());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
new file mode 100644
index 0000000..a9206f6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java
@@ -0,0 +1,799 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.PropertyName;
+
+/**
+ * Helper class used for aggregating information about a single
+ * potential POJO property.
+ */
+public class POJOPropertyBuilder
+    extends BeanPropertyDefinition
+    implements Comparable<POJOPropertyBuilder>
+{
+    /**
+     * Whether property is being composed for serialization
+     * (true) or deserialization (false)
+     */
+    protected final boolean _forSerialization;
+
+    protected final AnnotationIntrospector _annotationIntrospector;
+    
+    /**
+     * External name of logical property; may change with
+     * renaming (by new instance being constructed using
+     * a new name)
+     */
+    protected final String _name;
+
+    /**
+     * Original internal name, derived from accessor, of this
+     * property. Will not be changed by renaming.
+     */
+    protected final String _internalName;
+
+    protected Linked<AnnotatedField> _fields;
+    
+    protected Linked<AnnotatedParameter> _ctorParameters;
+    
+    protected Linked<AnnotatedMethod> _getters;
+
+    protected Linked<AnnotatedMethod> _setters;
+    
+    public POJOPropertyBuilder(String internalName,
+            AnnotationIntrospector annotationIntrospector, boolean forSerialization)
+    {
+        _internalName = internalName;
+        _name = internalName;
+        _annotationIntrospector = annotationIntrospector;
+        _forSerialization = forSerialization;
+    }
+
+    public POJOPropertyBuilder(POJOPropertyBuilder src, String newName)
+    {
+        _internalName = src._internalName;
+        _name = newName;
+        _annotationIntrospector = src._annotationIntrospector;
+        _fields = src._fields;
+        _ctorParameters = src._ctorParameters;
+        _getters = src._getters;
+        _setters = src._setters;
+        _forSerialization = src._forSerialization;
+    }
+    
+    /*
+    /**********************************************************
+    /* Fluent factory methods
+    /**********************************************************
+     */
+
+    @Override
+    public POJOPropertyBuilder withName(String newName) {
+        return new POJOPropertyBuilder(this, newName);
+    }
+    
+    /*
+    /**********************************************************
+    /* Comparable implementation: sort alphabetically, except
+    /* that properties with constructor parameters sorted
+    /* before other properties
+    /**********************************************************
+     */
+
+    @Override
+    public int compareTo(POJOPropertyBuilder other)
+    {
+        // first, if one has ctor params, that should come first:
+        if (_ctorParameters != null) {
+            if (other._ctorParameters == null) {
+                return -1;
+            }
+        } else if (other._ctorParameters != null) {
+            return 1;
+        }
+        /* otherwise sort by external name (including sorting of
+         * ctor parameters)
+         */
+        return getName().compareTo(other.getName());
+    }
+
+    /*
+    /**********************************************************
+    /* BeanPropertyDefinition implementation, name/type
+    /**********************************************************
+     */
+
+    @Override
+    public String getName() { return _name; }
+
+    @Override
+    public String getInternalName() { return _internalName; }
+
+    @Override
+    public PropertyName getWrapperName() {
+        /* 13-Mar-2013, tatu: Accessing via primary member SHOULD work,
+         *   due to annotation merging. However, I have seen some problems
+         *   with this access (for other annotations) so will leave full
+         *   traversal code in place just in case.
+         */
+        AnnotatedMember member = getPrimaryMember();
+        return (member == null || _annotationIntrospector == null) ? null
+                : _annotationIntrospector.findWrapperName(member);
+    	/*
+        return fromMemberAnnotations(new WithMember<PropertyName>() {
+            @Override
+            public PropertyName withMember(AnnotatedMember member) {
+                return _annotationIntrospector.findWrapperName(member);
+            }
+        });
+        */
+    }
+
+    @Override
+    public boolean isExplicitlyIncluded() {
+        return _anyExplicitNames(_fields)
+                || _anyExplicitNames(_getters)
+                || _anyExplicitNames(_setters)
+                || _anyExplicitNames(_ctorParameters)
+                ;
+    }
+
+    /*
+    /**********************************************************
+    /* BeanPropertyDefinition implementation, accessor access
+    /**********************************************************
+     */
+    
+    @Override
+    public boolean hasGetter() { return _getters != null; }
+
+    @Override
+    public boolean hasSetter() { return _setters != null; }
+
+    @Override
+    public boolean hasField() { return _fields != null; }
+
+    @Override
+    public boolean hasConstructorParameter() { return _ctorParameters != null; }
+
+    @Override
+    public boolean couldSerialize() {
+        return (_getters != null) || (_fields != null);
+    }
+
+    @Override
+    public AnnotatedMethod getGetter()
+    {
+        if (_getters == null) {
+            return null;
+        }
+        // If multiple, verify that they do not conflict...
+        AnnotatedMethod getter = _getters.value;
+        Linked<AnnotatedMethod> next = _getters.next;
+        for (; next != null; next = next.next) {
+            /* [JACKSON-255] Allow masking, i.e. report exception only if
+             *   declarations in same class, or there's no inheritance relationship
+             *   (sibling interfaces etc)
+             */
+            AnnotatedMethod nextGetter = next.value;
+            Class<?> getterClass = getter.getDeclaringClass();
+            Class<?> nextClass = nextGetter.getDeclaringClass();
+            if (getterClass != nextClass) {
+                if (getterClass.isAssignableFrom(nextClass)) { // next is more specific
+                    getter = nextGetter;
+                    continue;
+                }
+                if (nextClass.isAssignableFrom(getterClass)) { // getter more specific
+                    continue;
+                }
+            }
+            throw new IllegalArgumentException("Conflicting getter definitions for property \""+getName()+"\": "
+                    +getter.getFullName()+" vs "+nextGetter.getFullName());
+        }
+        return getter;
+    }
+
+    @Override
+    public AnnotatedMethod getSetter()
+    {
+        if (_setters == null) {
+            return null;
+        }
+        // If multiple, verify that they do not conflict...
+        AnnotatedMethod setter = _setters.value;
+        Linked<AnnotatedMethod> next = _setters.next;
+        for (; next != null; next = next.next) {
+            /* [JACKSON-255] Allow masking, i.e. report exception only if
+             *   declarations in same class, or there's no inheritance relationship
+             *   (sibling interfaces etc)
+             */
+            AnnotatedMethod nextSetter = next.value;
+            Class<?> setterClass = setter.getDeclaringClass();
+            Class<?> nextClass = nextSetter.getDeclaringClass();
+            if (setterClass != nextClass) {
+                if (setterClass.isAssignableFrom(nextClass)) { // next is more specific
+                    setter = nextSetter;
+                    continue;
+                }
+                if (nextClass.isAssignableFrom(setterClass)) { // getter more specific
+                    continue;
+                }
+            }
+            throw new IllegalArgumentException("Conflicting setter definitions for property \""+getName()+"\": "
+                    +setter.getFullName()+" vs "+nextSetter.getFullName());
+        }
+        return setter;
+    }
+
+    @Override
+    public AnnotatedField getField()
+    {
+        if (_fields == null) {
+            return null;
+        }
+        // If multiple, verify that they do not conflict...
+        AnnotatedField field = _fields.value;
+        Linked<AnnotatedField> next = _fields.next;
+        for (; next != null; next = next.next) {
+            AnnotatedField nextField = next.value;
+            Class<?> fieldClass = field.getDeclaringClass();
+            Class<?> nextClass = nextField.getDeclaringClass();
+            if (fieldClass != nextClass) {
+                if (fieldClass.isAssignableFrom(nextClass)) { // next is more specific
+                    field = nextField;
+                    continue;
+                }
+                if (nextClass.isAssignableFrom(fieldClass)) { // getter more specific
+                    continue;
+                }
+            }
+            throw new IllegalArgumentException("Multiple fields representing property \""+getName()+"\": "
+                    +field.getFullName()+" vs "+nextField.getFullName());
+        }
+        return field;
+    }
+
+    @Override
+    public AnnotatedParameter getConstructorParameter()
+    {
+        if (_ctorParameters == null) {
+            return null;
+        }
+        /* Hmmh. Checking for constructor parameters is trickier; for one,
+         * we must allow creator and factory method annotations.
+         * If this is the case, constructor parameter has the precedence.
+         * 
+         * So, for now, just try finding the first constructor parameter;
+         * if none, first factory method. And don't check for dups, if we must,
+         * can start checking for them later on.
+         */
+        Linked<AnnotatedParameter> curr = _ctorParameters;
+        do {
+            if (curr.value.getOwner() instanceof AnnotatedConstructor) {
+                return curr.value;
+            }
+            curr = curr.next;
+        } while (curr != null);
+        return _ctorParameters.value;
+    }
+    
+    @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 getPrimaryMember() {
+        if (_forSerialization) {
+            return getAccessor();
+        }
+        return getMutator();
+    }
+
+    /*
+    /**********************************************************
+    /* Implementations of refinement accessors
+    /**********************************************************
+     */
+
+    @Override
+    public Class<?>[] findViews() {
+        return fromMemberAnnotations(new WithMember<Class<?>[]>() {
+            @Override
+            public Class<?>[] withMember(AnnotatedMember member) {
+                return _annotationIntrospector.findViews(member);
+            }
+        });
+    }
+
+    @Override
+    public AnnotationIntrospector.ReferenceProperty findReferenceType() {
+        return fromMemberAnnotations(new WithMember<AnnotationIntrospector.ReferenceProperty>() {
+            @Override
+            public AnnotationIntrospector.ReferenceProperty withMember(AnnotatedMember member) {
+                return _annotationIntrospector.findReferenceType(member);
+            }
+        });
+    }
+
+    @Override
+    public boolean isTypeId() {
+        Boolean b = fromMemberAnnotations(new WithMember<Boolean>() {
+            @Override
+            public Boolean withMember(AnnotatedMember member) {
+                return _annotationIntrospector.isTypeId(member);
+            }
+        });
+        return (b != null) && b.booleanValue();
+    }
+
+    @Override
+    public boolean isRequired() {
+        Boolean b = fromMemberAnnotations(new WithMember<Boolean>() {
+            @Override
+            public Boolean withMember(AnnotatedMember member) {
+                return _annotationIntrospector.hasRequiredMarker(member);
+            }
+        });
+        return (b != null) && b.booleanValue();
+    }
+
+    @Override
+    public ObjectIdInfo findObjectIdInfo() {
+        return fromMemberAnnotations(new WithMember<ObjectIdInfo>() {
+            @Override
+            public ObjectIdInfo withMember(AnnotatedMember member) {
+                ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(member);
+                if (info != null) {
+                    info = _annotationIntrospector.findObjectReferenceInfo(member, info);
+                }
+                return info;
+            }
+        });
+    }
+    
+    /*
+    /**********************************************************
+    /* Data aggregation
+    /**********************************************************
+     */
+    
+    public void addField(AnnotatedField a, String ename, boolean visible, boolean ignored) {
+        _fields = new Linked<AnnotatedField>(a, _fields, ename, visible, ignored);
+    }
+
+    public void addCtor(AnnotatedParameter a, String ename, boolean visible, boolean ignored) {
+        _ctorParameters = new Linked<AnnotatedParameter>(a, _ctorParameters, ename, visible, ignored);
+    }
+
+    public void addGetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
+        _getters = new Linked<AnnotatedMethod>(a, _getters, ename, visible, ignored);
+    }
+
+    public void addSetter(AnnotatedMethod a, String ename, boolean visible, boolean ignored) {
+        _setters = new Linked<AnnotatedMethod>(a, _setters, ename, visible, ignored);
+    }
+
+    /**
+     * Method for adding all property members from specified collector into
+     * this collector.
+     */
+    public void addAll(POJOPropertyBuilder src)
+    {
+        _fields = merge(_fields, src._fields);
+        _ctorParameters = merge(_ctorParameters, src._ctorParameters);
+        _getters= merge(_getters, src._getters);
+        _setters = merge(_setters, src._setters);
+    }
+
+    private static <T> Linked<T> merge(Linked<T> chain1, Linked<T> chain2)
+    {
+        if (chain1 == null) {
+            return chain2;
+        }
+        if (chain2 == null) {
+            return chain1;
+        }
+        return chain1.append(chain2);
+    }
+    
+    /*
+    /**********************************************************
+    /* Modifications
+    /**********************************************************
+     */
+
+    /**
+     * Method called to remove all entries that are marked as
+     * ignored.
+     */
+    public void removeIgnored()
+    {
+        _fields = _removeIgnored(_fields);
+        _getters = _removeIgnored(_getters);
+        _setters = _removeIgnored(_setters);
+        _ctorParameters = _removeIgnored(_ctorParameters);
+    }
+
+    /**
+     * @deprecated Since 2.2, use variant that takes boolean argument
+     */
+    @Deprecated
+    public void removeNonVisible() {
+        removeNonVisible(false);
+    }
+    
+    
+    public void removeNonVisible(boolean force)
+    {
+        /* 21-Aug-2011, tatu: This is tricky part -- if and when allow
+         *   non-visible property elements to be "pulled in" by visible
+         *   counterparts?
+         *   For now, we will only do this to pull in setter or field used
+         *   as setter, if an explicit getter is found.
+         */
+        /*
+         * 28-Mar-2013, tatu: Also, as per [Issue#195], may force removal
+         *   if inferred properties are NOT supported.
+         */
+        _getters = _removeNonVisible(_getters);
+        _ctorParameters = _removeNonVisible(_ctorParameters);
+
+        if (force || (_getters == null)) {
+            _fields = _removeNonVisible(_fields);
+            _setters = _removeNonVisible(_setters);
+        }
+    }
+
+    /**
+     * Method called to trim unnecessary entries, such as implicit
+     * getter if there is an explict one available. This is important
+     * for later stages, to avoid unnecessary conflicts.
+     */
+    public void trimByVisibility()
+    {
+        _fields = _trimByVisibility(_fields);
+        _getters = _trimByVisibility(_getters);
+        _setters = _trimByVisibility(_setters);
+        _ctorParameters = _trimByVisibility(_ctorParameters);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void mergeAnnotations(boolean forSerialization)
+    {
+        if (forSerialization) {
+            if (_getters != null) {
+                AnnotationMap ann = _mergeAnnotations(0, _getters, _fields, _ctorParameters, _setters);
+                _getters = _getters.withValue(_getters.value.withAnnotations(ann));
+            } else if (_fields != null) {
+                AnnotationMap ann = _mergeAnnotations(0, _fields, _ctorParameters, _setters);
+                _fields = _fields.withValue(_fields.value.withAnnotations(ann));
+            }
+        } else {
+            if (_ctorParameters != null) {
+                AnnotationMap ann = _mergeAnnotations(0, _ctorParameters, _setters, _fields, _getters);
+                _ctorParameters = _ctorParameters.withValue(_ctorParameters.value.withAnnotations(ann));
+            } else if (_setters != null) {
+                AnnotationMap ann = _mergeAnnotations(0, _setters, _fields, _getters);
+                _setters = _setters.withValue(_setters.value.withAnnotations(ann));
+            } else if (_fields != null) {
+                AnnotationMap ann = _mergeAnnotations(0, _fields, _getters);
+                _fields = _fields.withValue(_fields.value.withAnnotations(ann));
+            }
+        }
+    }
+
+    private AnnotationMap _mergeAnnotations(int index, Linked<? extends AnnotatedMember>... nodes)
+    {
+        AnnotationMap ann = nodes[index].value.getAllAnnotations();
+        ++index;
+        for (; index < nodes.length; ++index) {
+            if (nodes[index] != null) {
+              return AnnotationMap.merge(ann, _mergeAnnotations(index, nodes));
+            }
+        }
+        return ann;
+    }
+    
+    private <T> Linked<T> _removeIgnored(Linked<T> node)
+    {
+        if (node == null) {
+            return node;
+        }
+        return node.withoutIgnored();
+    }
+
+    private <T> Linked<T> _removeNonVisible(Linked<T> node)
+    {
+        if (node == null) {
+            return node;
+        }
+        return node.withoutNonVisible();
+    }
+
+    private <T> Linked<T> _trimByVisibility(Linked<T> node)
+    {
+        if (node == null) {
+            return node;
+        }
+        return node.trimByVisibility();
+    }
+        
+    /*
+    /**********************************************************
+    /* Accessors for aggregate information
+    /**********************************************************
+     */
+
+    private <T> boolean _anyExplicitNames(Linked<T> n)
+    {
+        for (; n != null; n = n.next) {
+            if (n.explicitName != null && n.explicitName.length() > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean anyVisible() {
+        return _anyVisible(_fields)
+            || _anyVisible(_getters)
+            || _anyVisible(_setters)
+            || _anyVisible(_ctorParameters)
+        ;
+    }
+
+    private <T> boolean _anyVisible(Linked<T> n)
+    {
+        for (; n != null; n = n.next) {
+            if (n.isVisible) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public boolean anyIgnorals() {
+        return _anyIgnorals(_fields)
+            || _anyIgnorals(_getters)
+            || _anyIgnorals(_setters)
+            || _anyIgnorals(_ctorParameters)
+        ;
+    }
+
+    private <T> boolean _anyIgnorals(Linked<T> n)
+    {
+        for (; n != null; n = n.next) {
+            if (n.isMarkedIgnored) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Method called to check whether property represented by this collector
+     * should be renamed from the implicit name; and also verify that there
+     * are no conflicting rename definitions.
+     */
+    public String findNewName()
+    {
+        Linked<? extends AnnotatedMember> renamed = null;
+        renamed = findRenamed(_fields, renamed);
+        renamed = findRenamed(_getters, renamed);
+        renamed = findRenamed(_setters, renamed);
+        renamed = findRenamed(_ctorParameters, renamed);
+        return (renamed == null) ? null : renamed.explicitName;
+    }
+
+    private Linked<? extends AnnotatedMember> findRenamed(Linked<? extends AnnotatedMember> node,
+            Linked<? extends AnnotatedMember> renamed)
+    {
+        for (; node != null; node = node.next) {
+            String explName = node.explicitName;
+            if (explName == null) {
+                continue;
+            }
+            // different from default name?
+            if (explName.equals(_name)) { // nope, skip
+                continue;
+            }
+            if (renamed == null) {
+                renamed = node;
+            } else {
+                // different from an earlier renaming? problem
+                if (!explName.equals(renamed.explicitName)) {
+                    throw new IllegalStateException("Conflicting property name definitions: '"
+                            +renamed.explicitName+"' (for "+renamed.value+") vs '"
+                            +node.explicitName+"' (for "+node.value+")");
+                }
+            }
+        }
+        return renamed;
+    }
+    
+    // For trouble-shooting
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[Property '").append(_name)
+          .append("'; ctors: ").append(_ctorParameters)
+          .append(", field(s): ").append(_fields)
+          .append(", getter(s): ").append(_getters)
+          .append(", setter(s): ").append(_setters)
+          ;
+        sb.append("]");
+        return sb.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used for finding annotation values, from accessors
+     * relevant to current usage (deserialization, serialization)
+     */
+    protected <T> T fromMemberAnnotations(WithMember<T> func)
+    {
+        T result = null;
+        if (_annotationIntrospector != null) {
+            if (_forSerialization) {
+                if (_getters != null) {
+                    result = func.withMember(_getters.value);
+                }
+            } else {
+                if (_ctorParameters != null) {
+                    result = func.withMember(_ctorParameters.value);
+                }
+                if (result == null && _setters != null) {
+                    result = func.withMember(_setters.value);
+                }
+            }
+            if (result == null && _fields != null) {
+                result = func.withMember(_fields.value);
+            }
+        }
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    private interface WithMember<T>
+    {
+        public T withMember(AnnotatedMember member);
+    }
+    
+    /**
+     * Node used for creating simple linked lists to efficiently store small sets
+     * of things.
+     */
+    private final static class Linked<T>
+    {
+        public final T value;
+        public final Linked<T> next;
+
+        public final String explicitName;
+        public final boolean isVisible;
+        public final boolean isMarkedIgnored;
+        
+        public Linked(T v, Linked<T> n,
+                String explName, boolean visible, boolean ignored)
+        {
+            value = v;
+            next = n;
+            // ensure that we'll never have missing names
+            if (explName == null) {
+                explicitName = null;
+            } else {
+                explicitName = (explName.length() == 0) ? null : explName;
+            }
+            isVisible = visible;
+            isMarkedIgnored = ignored;
+        }
+
+        public Linked<T> withValue(T newValue)
+        {
+            if (newValue == value) {
+                return this;
+            }
+            return new Linked<T>(newValue, next, explicitName, isVisible, isMarkedIgnored);
+        }
+        
+        public Linked<T> withNext(Linked<T> newNext) {
+            if (newNext == next) {
+                return this;
+            }
+            return new Linked<T>(value, newNext, explicitName, isVisible, isMarkedIgnored);
+        }
+        
+        public Linked<T> withoutIgnored()
+        {
+            if (isMarkedIgnored) {
+                return (next == null) ? null : next.withoutIgnored();
+            }
+            if (next != null) {
+                Linked<T> newNext = next.withoutIgnored();
+                if (newNext != next) {
+                    return withNext(newNext);
+                }
+            }
+            return this;
+        }
+        
+        public Linked<T> withoutNonVisible()
+        {
+            Linked<T> newNext = (next == null) ? null : next.withoutNonVisible();
+            return isVisible ? withNext(newNext) : newNext;
+        }
+
+        /**
+         * Method called to append given node(s) at the end of this
+         * node chain.
+         */
+        private Linked<T> append(Linked<T> appendable) 
+        {
+            if (next == null) {
+                return withNext(appendable);
+            }
+            return withNext(next.append(appendable));
+        }
+        
+        public Linked<T> trimByVisibility()
+        {
+            if (next == null) {
+                return this;
+            }
+            Linked<T> newNext = next.trimByVisibility();
+            if (explicitName != null) { // this already has highest; how about next one?
+                if (newNext.explicitName == null) { // next one not, drop it
+                    return withNext(null);
+                }
+                //  both have it, keep
+                return withNext(newNext);
+            }
+            if (newNext.explicitName != null) { // next one has higher, return it...
+                return newNext;
+            }
+            // neither has explicit name; how about visibility?
+            if (isVisible == newNext.isVisible) { // same; keep both in current order
+                return withNext(newNext);
+            }
+            return isVisible ? withNext(null) : newNext;
+        }
+        
+        @Override
+        public String toString() {
+            String msg = value.toString()+"[visible="+isVisible+"]";
+            if (next != null) {
+                msg = msg + ", "+next.toString();
+            }
+            return msg;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
new file mode 100644
index 0000000..e67ae6b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/VisibilityChecker.java
@@ -0,0 +1,395 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+/**
+ * Interface for object used for determine which property elements
+ * (methods, fields, constructors) can be auto-detected, with respect
+ * to their visibility modifiers.
+ *<p>
+ * Note on type declaration: funky recursive type is necessary to
+ * support builder/fluent pattern.
+ * 
+ * @author tatu
+ */
+public interface VisibilityChecker<T extends VisibilityChecker<T>>
+{
+    // // Builder methods
+
+    /**
+     * Builder method that will return an instance that has same
+     * settings as this instance has, except for values that
+     * given annotation overrides.
+     */
+    public T with(JsonAutoDetect ann);
+
+    /**
+     * 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:
+     *<pre>
+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().with(Visibility.NONE));
+     *</pre>
+     * (which would basically disable all auto-detection)
+     */
+    public T with(Visibility v);
+
+    /**
+     * Builder method that will create and return an instance that has specified
+     * {@link Visibility} value to use for specified property.
+     * Typical usage would be:
+     *<pre>
+     *  mapper.setVisibilityChecker(
+     *     mapper.getVisibilityChecker().withVisibility(JsonMethod.FIELD, Visibility.ANY));
+     *</pre>
+     * (which would basically enable auto-detection for all member fields)
+     */
+    public T withVisibility(PropertyAccessor method, Visibility v);
+    
+    /**
+     * Builder method that will return a checker instance that has
+     * specified minimum visibility level for regular ("getXxx") getters.
+     */
+    public T withGetterVisibility(Visibility v);
+
+    /**
+     * Builder method that will return a checker instance that has
+     * specified minimum visibility level for "is-getters" ("isXxx").
+     */
+    public T withIsGetterVisibility(Visibility v);
+    
+    /**
+     * Builder method that will return a checker instance that has
+     * specified minimum visibility level for setters.
+     */
+    public T withSetterVisibility(Visibility v);
+
+    /**
+     * Builder method that will return a checker instance that has
+     * specified minimum visibility level for creator methods
+     * (constructors, factory methods)
+     */
+    public T withCreatorVisibility(Visibility v);
+
+    /**
+     * Builder method that will return a checker instance that has
+     * specified minimum visibility level for fields.
+     */
+    public T withFieldVisibility(Visibility v);
+	
+    // // Accessors
+	
+    /**
+     * Method for checking whether given method is auto-detectable
+     * as regular getter, with respect to its visibility (not considering
+     * method signature or name, just visibility)
+     */
+    public boolean isGetterVisible(Method m);
+    public boolean isGetterVisible(AnnotatedMethod m);
+
+    /**
+     * Method for checking whether given method is auto-detectable
+     * as is-getter, with respect to its visibility (not considering
+     * method signature or name, just visibility)
+     */
+    public boolean isIsGetterVisible(Method m);
+    public boolean isIsGetterVisible(AnnotatedMethod m);
+    
+    /**
+     * Method for checking whether given method is auto-detectable
+     * as setter, with respect to its visibility (not considering
+     * method signature or name, just visibility)
+     */
+    public boolean isSetterVisible(Method m);
+    public boolean isSetterVisible(AnnotatedMethod m);
+
+    /**
+     * Method for checking whether given method is auto-detectable
+     * as Creator, with respect to its visibility (not considering
+     * method signature or name, just visibility)
+     */
+    public boolean isCreatorVisible(Member m);
+    public boolean isCreatorVisible(AnnotatedMember m);
+
+    /**
+     * Method for checking whether given field is auto-detectable
+     * as property, with respect to its visibility (not considering
+     * method signature or name, just visibility)
+     */
+    public boolean isFieldVisible(Field f);
+    public boolean isFieldVisible(AnnotatedField f);
+
+    /*
+    /********************************************************
+    /* Standard implementation suitable for basic use
+    /********************************************************
+    */
+
+   /**
+    * Default standard implementation is purely based on visibility
+    * modifier of given class members, and its configured minimum
+    * levels.
+    * Implemented using "builder" (aka "Fluid") pattern, whereas instances
+    * are immutable, and configuration is achieved by chainable factory
+    * 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
+    {
+        private static final long serialVersionUID = -7073939237187922755L;
+
+        /**
+         * 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 Visibility _getterMinLevel;
+        protected final Visibility _isGetterMinLevel;
+        protected final Visibility _setterMinLevel;
+        protected final Visibility _creatorMinLevel;
+        protected final Visibility _fieldMinLevel;
+		
+        public static Std defaultInstance() { return DEFAULT; }
+        
+        /**
+         * Constructor used for building instance that has minumum visibility
+         * levels as indicated by given annotation instance
+         * 
+         * @param ann Annotations to use for determining minimum visibility levels
+         */
+        public Std(JsonAutoDetect ann)
+        {
+            // let's combine checks for enabled/disabled, with minimimum level checks:
+            _getterMinLevel = ann.getterVisibility();
+            _isGetterMinLevel = ann.isGetterVisibility();
+            _setterMinLevel = ann.setterVisibility();
+            _creatorMinLevel = ann.creatorVisibility();
+            _fieldMinLevel = ann.fieldVisibility();
+        }
+
+        /**
+         * Constructor that allows directly specifying minimum visibility levels to use
+         */
+        public Std(Visibility getter, Visibility isGetter, Visibility setter, Visibility creator, Visibility field)
+        {
+            _getterMinLevel = getter;
+            _isGetterMinLevel = isGetter;
+            _setterMinLevel = setter;
+            _creatorMinLevel = creator;
+            _fieldMinLevel = field;
+        }
+
+        /**
+         * Costructor that will assign given visibility value for all
+         * properties.
+         * 
+         * @param v level to use for all property types
+         */
+        public Std(Visibility v)
+        {
+            // typically we shouldn't get this value; but let's handle it if we do:
+            if (v == Visibility.DEFAULT) {
+                _getterMinLevel = DEFAULT._getterMinLevel;
+                _isGetterMinLevel = DEFAULT._isGetterMinLevel;
+                _setterMinLevel = DEFAULT._setterMinLevel;
+                _creatorMinLevel = DEFAULT._creatorMinLevel;
+                _fieldMinLevel = DEFAULT._fieldMinLevel;
+            } else {
+                _getterMinLevel = v;
+                _isGetterMinLevel = v;
+                _setterMinLevel = v;
+                _creatorMinLevel = v;
+                _fieldMinLevel = v;
+            }
+        }
+
+        /*
+        /********************************************************
+        /* Builder/fluent methods for instantiating configured
+        /* instances
+        /********************************************************
+         */
+
+    @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 curr;
+	}
+
+    @Override
+    public Std with(Visibility v)
+    {
+        if (v == Visibility.DEFAULT) {
+            return DEFAULT;
+        }
+        return new Std(v);
+    }
+
+    @Override
+    public Std withVisibility(PropertyAccessor method, Visibility v)
+    {
+	    switch (method) {
+	    case GETTER:
+	        return withGetterVisibility(v);
+	    case SETTER:
+	        return withSetterVisibility(v);
+	    case CREATOR:
+	        return withCreatorVisibility(v);
+	    case FIELD:
+	        return withFieldVisibility(v);
+	    case IS_GETTER:
+	        return withIsGetterVisibility(v);
+            case ALL:
+                return with(v);
+        //case NONE:
+        default:
+            // break;
+            return this;
+	    }
+	}
+	
+    @Override
+	public Std withGetterVisibility(Visibility v) {
+	    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 (_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 (_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 (_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
+    /********************************************************
+     */
+
+    @Override
+    public boolean isCreatorVisible(Member m) {
+        return _creatorMinLevel.isVisible(m);
+    }
+	
+    @Override
+    public boolean isCreatorVisible(AnnotatedMember m) {
+        return isCreatorVisible(m.getMember());
+    }
+    
+    @Override
+    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());
+    }
+
+    @Override
+    public boolean isIsGetterVisible(Method m) {
+        return _isGetterMinLevel.isVisible(m);
+    }    
+
+    @Override
+    public boolean isIsGetterVisible(AnnotatedMethod m) {
+        return isIsGetterVisible(m.getAnnotated());
+    }
+
+    @Override
+    public boolean isSetterVisible(Method m) {
+        return _setterMinLevel.isVisible(m);
+    }
+    
+    @Override
+    public boolean isSetterVisible(AnnotatedMethod m) {
+        return isSetterVisible(m.getAnnotated());
+    }
+
+    /*
+    /********************************************************
+    /* Standard methods
+    /********************************************************
+     */
+
+    @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();
+    }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/WithMember.java b/src/main/java/com/fasterxml/jackson/databind/introspect/WithMember.java
new file mode 100644
index 0000000..2118a57
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/WithMember.java
@@ -0,0 +1,6 @@
+package com.fasterxml.jackson.databind.introspect;
+
+public interface WithMember<T>
+{
+    public T withMember(AnnotatedMember member);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/introspect/package-info.java b/src/main/java/com/fasterxml/jackson/databind/introspect/package-info.java
new file mode 100644
index 0000000..17f8567
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/introspect/package-info.java
@@ -0,0 +1,12 @@
+/**
+ * Functionality needed for Bean introspection, required for detecting
+ * accessors and mutators for Beans, as well as locating and handling
+ * method annotations.
+ *<p>
+ * Beyond collecting annotations, additional "method annotation inheritance"
+ * is also supported: whereas regular JDK classes do not add annotations
+ * from overridden methods in any situation. But code in this package does.
+ * Similarly class-annotations are inherited properly from interfaces, in
+ * addition to abstract and concrete classes.
+ */
+package com.fasterxml.jackson.databind.introspect;
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java
new file mode 100644
index 0000000..5073675
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonAnyFormatVisitor.java
@@ -0,0 +1,11 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+public interface JsonAnyFormatVisitor
+{
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base implements JsonAnyFormatVisitor { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java
new file mode 100644
index 0000000..bd16cd5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonArrayFormatVisitor.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public interface JsonArrayFormatVisitor extends JsonFormatVisitorWithSerializerProvider
+{
+    /**
+     * Visit method called for structured types, as well as possibly
+     * for leaf types (especially if handled by custom serializers).
+     * 
+     * @param handler Serializer used, to allow for further callbacks
+     * @param elementType Type of elements in JSON array value
+     */
+    void itemsFormat(JsonFormatVisitable handler, JavaType elementType)
+        throws JsonMappingException;
+    
+    /**
+     * Visit method that is called if the content type is a simple
+     * scalar type like {@link JsonFormatTypes#STRING} (but not
+     * for structured types like {@link JsonFormatTypes#OBJECT} since
+     * they would be missing type information).
+     */
+    void itemsFormat(JsonFormatTypes format)
+        throws JsonMappingException;
+
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base implements JsonArrayFormatVisitor {
+        protected SerializerProvider _provider;
+
+        public Base() { }
+        public Base(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public SerializerProvider getProvider() { return _provider; }
+
+        @Override
+        public void setProvider(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public void itemsFormat(JsonFormatVisitable handler, JavaType elementType)
+            throws JsonMappingException { }
+
+        @Override
+        public void itemsFormat(JsonFormatTypes format)
+            throws JsonMappingException { }
+    }
+
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java
new file mode 100644
index 0000000..0ca65b9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonBooleanFormatVisitor.java
@@ -0,0 +1,12 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+public interface JsonBooleanFormatVisitor extends JsonValueFormatVisitor
+{
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base extends JsonValueFormatVisitor.Base
+        implements JsonBooleanFormatVisitor { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java
new file mode 100644
index 0000000..5290e9f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatTypes.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum JsonFormatTypes {
+	
+	STRING,
+	NUMBER,
+	INTEGER,
+	BOOLEAN,
+	OBJECT,
+	ARRAY,
+	NULL,
+	ANY;
+	
+	
+	@JsonValue
+	public String value() {
+		return this.name().toLowerCase();
+	}
+	
+	@JsonCreator
+	public static JsonFormatTypes forValue(String s) {
+		return valueOf(s.toUpperCase());
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java
new file mode 100644
index 0000000..95618a1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitable.java
@@ -0,0 +1,20 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Interface {@link com.fasterxml.jackson.databind.JsonSerializer} implements
+ * to allow for visiting type hierarchy.
+ */
+public interface JsonFormatVisitable
+{
+    /**
+     * Get the representation of the schema to which this serializer will conform.
+     * @param typeHint Type of element (entity like property) being visited
+     *
+     * @returns <a href="http://json-schema.org/">Json-schema</a> for this serializer.
+     */
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java
new file mode 100644
index 0000000..b8f437f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWithSerializerProvider.java
@@ -0,0 +1,14 @@
+/**
+ * 
+ */
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * @author jphelan
+ */
+public interface JsonFormatVisitorWithSerializerProvider {
+    public SerializerProvider getProvider();
+    public abstract void setProvider(SerializerProvider provider);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java
new file mode 100644
index 0000000..a0bcb61
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonFormatVisitorWrapper.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+/**
+ * Interface for visitor callbacks, when type in question can be any of
+ * legal JSON types.
+ */
+public interface JsonFormatVisitorWrapper extends JsonFormatVisitorWithSerializerProvider
+{
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonObjectFormatVisitor expectObjectFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonArrayFormatVisitor expectArrayFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonStringFormatVisitor expectStringFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonNumberFormatVisitor expectNumberFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonIntegerFormatVisitor expectIntegerFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonBooleanFormatVisitor expectBooleanFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonNullFormatVisitor expectNullFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * @param type Declared type of visited property (or List element) in Java
+     */
+    public JsonAnyFormatVisitor expectAnyFormat(JavaType type) throws JsonMappingException;
+
+    /**
+     * Method called when type is of Java {@link java.util.Map} type, and will
+     * be serialized as a JSON Object.
+     * 
+     * @since 2.2
+     */
+    public JsonMapFormatVisitor expectMapFormat(JavaType type) throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java
new file mode 100644
index 0000000..238cb73
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonIntegerFormatVisitor.java
@@ -0,0 +1,25 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public interface JsonIntegerFormatVisitor extends JsonValueFormatVisitor
+{
+    /**
+     * Method called to provide more exact type of number being serialized
+     * (regardless of logical type, which may be {@link java.util.Date} or
+     * {@link java.lang.Enum}, in addition to actual numeric types like
+     * {@link java.lang.Integer}).
+     */
+    public void numberType(JsonParser.NumberType type);
+
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base extends JsonValueFormatVisitor.Base
+        implements JsonIntegerFormatVisitor {
+        @Override
+        public void numberType(JsonParser.NumberType type) { }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java
new file mode 100644
index 0000000..04a3fe2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonMapFormatVisitor.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public interface JsonMapFormatVisitor extends JsonFormatVisitorWithSerializerProvider
+{
+    /**
+     * Visit method called to indicate type of keys of the Map type
+     * being visited
+     */
+    public void keyFormat(JsonFormatVisitable handler, JavaType keyType) throws JsonMappingException;
+
+    /**
+     * Visit method called after {@link #keyFormat} to allow visiting of
+     * the value type
+     */
+    public void valueFormat(JsonFormatVisitable handler, JavaType valueType) throws JsonMappingException;
+    
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base
+        implements JsonMapFormatVisitor
+    {
+        protected SerializerProvider _provider;
+
+        public Base() { }
+        public Base(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public SerializerProvider getProvider() { return _provider; }
+
+        @Override
+        public void setProvider(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public void keyFormat(JsonFormatVisitable handler, JavaType keyType) throws JsonMappingException { }
+        @Override
+        public void valueFormat(JsonFormatVisitable handler, JavaType valueType) throws JsonMappingException { }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java
new file mode 100644
index 0000000..c0cab71
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNullFormatVisitor.java
@@ -0,0 +1,10 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+public interface JsonNullFormatVisitor {
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base implements JsonNullFormatVisitor { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java
new file mode 100644
index 0000000..a131552
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonNumberFormatVisitor.java
@@ -0,0 +1,25 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+public interface JsonNumberFormatVisitor extends JsonValueFormatVisitor
+{
+    /**
+     * Method called to provide more exact type of number being serialized
+     * (regardless of logical type, which may be {@link java.util.Date} or
+     * {@link java.lang.Enum}, in addition to actual numeric types like
+     * {@link java.lang.Integer}).
+     */
+    public void numberType(JsonParser.NumberType type);
+
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base extends JsonValueFormatVisitor.Base
+        implements JsonNumberFormatVisitor {
+        @Override
+        public void numberType(JsonParser.NumberType type) { }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java
new file mode 100644
index 0000000..9ec1811
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonObjectFormatVisitor.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public interface JsonObjectFormatVisitor extends JsonFormatVisitorWithSerializerProvider
+{
+    public void property(BeanProperty writer) throws JsonMappingException;
+    public void property(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException;
+
+    @Deprecated
+    public void property(String name) throws JsonMappingException;
+
+    public void optionalProperty(BeanProperty writer) throws JsonMappingException;
+    public void optionalProperty(String name, JsonFormatVisitable handler,
+            JavaType propertyTypeHint)
+        throws JsonMappingException;
+
+    @Deprecated
+    public void optionalProperty(String name) throws JsonMappingException;
+
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base
+        implements JsonObjectFormatVisitor
+    {
+        protected SerializerProvider _provider;
+
+        public Base() { }
+        public Base(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public SerializerProvider getProvider() { return _provider; }
+
+        @Override
+        public void setProvider(SerializerProvider p) { _provider = p; }
+
+        @Override
+        public void property(BeanProperty writer) throws JsonMappingException { }
+
+        @Override
+        public void property(String name, JsonFormatVisitable handler,
+                JavaType propertyTypeHint) throws JsonMappingException { }
+
+        @Deprecated
+        @Override
+        public void property(String name) throws JsonMappingException { }
+
+        @Override
+        public void optionalProperty(BeanProperty writer)
+                throws JsonMappingException { }
+
+        @Override
+        public void optionalProperty(String name, JsonFormatVisitable handler,
+                JavaType propertyTypeHint) throws JsonMappingException { }
+
+        @Deprecated
+        @Override
+        public void optionalProperty(String name) throws JsonMappingException { }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java
new file mode 100644
index 0000000..981b8a9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonStringFormatVisitor.java
@@ -0,0 +1,13 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+public interface JsonStringFormatVisitor extends JsonValueFormatVisitor
+{
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base extends JsonValueFormatVisitor.Base
+        implements JsonStringFormatVisitor { }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java
new file mode 100644
index 0000000..b7bcfde
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormat.java
@@ -0,0 +1,124 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+/**
+ * This enum represents the encoded format for a jsonSchema value type
+ * @author jphelan
+ *
+ */
+public enum JsonValueFormat {
+	/**
+	 * This SHOULD be a date in ISO 8601 format of YYYY-MM-
+      DDThh:mm:ssZ in UTC time.  This is the recommended form of date/
+      timestamp.
+	 */
+	DATE_TIME {
+		@Override
+		public String toString() { return "date-time"; }
+	},
+	
+	/**
+	 * This SHOULD be a date in the format of YYYY-MM-DD.  It is
+      recommended that you use the "date-time" format instead of "date"
+      unless you need to transfer only the date part.
+	 */
+	DATE {
+		@Override
+		public String toString() { return "date"; }
+	},
+	
+	/**
+	 * This SHOULD be a time in the format of hh:mm:ss.  It is
+      recommended that you use the "date-time" format instead of "time"
+      unless you need to transfer only the time part.
+	 */
+	TIME {
+		@Override
+		public String toString() { return "time"; }
+	},
+	
+	/**
+	 * This SHOULD be the difference, measured in
+      milliseconds, between the specified time and midnight, 00:00 of
+      January 1, 1970 UTC.  The value SHOULD be a number (integer or
+      float).
+	 */
+	UTC_MILLISEC {
+		@Override
+		public String toString() { return "utc-millisec"; }
+	},
+	
+	/**
+	 * A regular expression, following the regular expression
+  	  specification from ECMA 262/Perl 5.
+	 */
+	REGEX {
+		@Override
+		public String toString() { return "regex"; }
+	},
+	
+	/**
+	 * This is a CSS color (like "#FF0000" or "red"), based on CSS
+  		2.1 [W3C.CR-CSS21-20070719].
+	 */
+	COLOR {
+		@Override
+		public String toString() { return "color"; }
+	},
+	
+	/**
+	 * This is a CSS style definition (like "color: red; background-
+  		color:#FFF"), based on CSS 2.1 [W3C.CR-CSS21-20070719].
+	 */
+	STYLE {
+		@Override
+		public String toString() { return "style"; }
+	},
+	
+	/**
+	 * This SHOULD be a phone number (format MAY follow E.123).
+	 */
+	PHONE {
+		@Override
+		public String toString() { return "phone"; }
+	},
+	
+	/**
+	 * This value SHOULD be a URI..
+	 */
+	URI {
+		@Override
+		public String toString() { return "uri"; }
+	},
+	
+	/**
+	 * This SHOULD be an email address.
+	 */
+	EMAIL {
+		@Override
+		public String toString() { return "email"; }
+	},
+	/**
+	 * This SHOULD be an ip version 4 address.
+	 */
+	IP_ADDRESS {
+		@Override
+		public String toString() { return "ip-address"; }
+	},
+	
+	/**
+	 * This SHOULD be an ip version 6 address.
+	 */
+	IPV6 {
+		@Override
+		public String toString() { return "ipv6"; }
+	},
+	
+	/**
+	 * This SHOULD be a host-name.
+	 */
+	HOST_NAME {
+		@Override
+		public String toString() { return "host-name"; }
+	}
+	
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java
new file mode 100644
index 0000000..107898b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonFormatVisitors/JsonValueFormatVisitor.java
@@ -0,0 +1,28 @@
+package com.fasterxml.jackson.databind.jsonFormatVisitors;
+
+import java.util.Set;
+
+public interface JsonValueFormatVisitor {
+    /**
+     * Method called to indicate configured format for value type being visited.
+     */
+    void format(JsonValueFormat format);
+
+    /**
+     * Method called to indicate enumerated (String) values type being visited
+     * can take as values.
+     */
+    void enumTypes(Set<String> enums);
+
+    /**
+     * Default "empty" implementation, useful as the base to start on;
+     * especially as it is guaranteed to implement all the method
+     * of the interface, even if new methods are getting added.
+     */
+    public static class Base implements JsonValueFormatVisitor {
+        @Override
+        public void format(JsonValueFormat format) { }
+        @Override
+        public void enumTypes(Set<String> enums) { }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java
new file mode 100644
index 0000000..5edd39c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSchema.java
@@ -0,0 +1,96 @@
+package com.fasterxml.jackson.databind.jsonschema;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Container for a logical JSON Schema instance.
+ * Internally schema data is stored as a JSON Tree
+ * (instance of {@link JsonNode} is the root
+ * of schema document)
+ *
+ * @author Ryan Heaton
+ * @see <a href="http://json-schema.org/">JSON Schema</a>
+ * 
+ * @deprecated Since 2.2, we recommend use of external
+ *   <a href="https://github.com/FasterXML/jackson-module-jsonSchema">JSON Schema generator module</module>
+ */
+ at Deprecated
+public class JsonSchema
+{
+    private final ObjectNode schema;
+
+    /**
+     * Main constructor for schema instances.
+     *<p>
+     * This is the creator constructor used by Jackson itself when
+     * deserializing instances. It is so-called delegating creator, 
+     * meaning that its argument will be bound by Jackson before
+     * constructor gets called.
+     */
+    @JsonCreator
+    public JsonSchema(ObjectNode schema)
+    {
+        this.schema = schema;
+    }
+
+    /**
+     * Method for accessing root JSON object of the contained schema.
+     *<p>
+     * Note: this method is specified with {@link JsonValue} annotation
+     * to represent serialization to use; same as if explicitly
+     * serializing returned object.
+     *
+     * @return Root node of the schema tree
+     */
+    @JsonValue
+    public ObjectNode getSchemaNode()
+    {
+        return schema;
+    }
+
+    @Override
+    public String toString()
+    {
+        return this.schema.toString();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return schema.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (!(o instanceof JsonSchema)) return false;
+
+        JsonSchema other = (JsonSchema) o;
+        if (schema == null) {
+            return other.schema == null;
+        }
+        return schema.equals(other.schema);
+    }
+
+    /**
+     * Get the default schema node.
+     *
+     * @return The default schema node.
+     */
+    public static JsonNode getDefaultSchemaNode()
+    {
+        ObjectNode objectNode = JsonNodeFactory.instance.objectNode();
+        objectNode.put("type", "any");
+        // "required" is false by default, no need to include
+        //objectNode.put("required", false);
+        return objectNode;
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java
new file mode 100644
index 0000000..553206e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/JsonSerializableSchema.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.databind.jsonschema;
+
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Retention;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotation;
+
+/**
+ * Annotation that can be used to define JSON Schema definition for
+ * the annotated class.
+ *<p>
+ * Note that annotation is often not needed: for example, regular
+ * Jackson beans that Jackson can introspect can be used without
+ * annotations, to produce JSON schema definition.
+ * 
+ * @author Ryan Heaton
+ * @author Tatu Saloranta
+ */
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+ at JacksonAnnotation
+public @interface JsonSerializableSchema
+{
+    /**
+     * Marker value used to indicate that property has "no value";
+     * needed because annotations can not have null as default
+     * value.
+     */
+    public final static String NO_VALUE = "##irrelevant";
+
+    /**
+     * Property that can be used to indicate id of the type when
+     * generating JSON Schema; empty String indicates that no id
+     * is defined.
+     */
+    public String id() default "";
+    
+    /**
+     * The schema type for this JsonSerializable instance.
+     * Possible values: "string", "number", "boolean", "object", "array", "null", "any"
+     *
+     * @return The schema type for this JsonSerializable instance.
+     */
+    public String schemaType() default "any";
+
+    /**
+     * If the schema type is "object", JSON definition of properties of the object as
+     * a String.
+     *
+     * @return The node representing the schema properties, or "##irrelevant" if irrelevant.
+     * 
+     * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is
+     *   fundamentally bad way for customizing anything. No direct replacements offered.
+     */
+    @Deprecated
+    public String schemaObjectPropertiesDefinition() default NO_VALUE;
+
+    /**
+     * If the schema type if "array", JSON definition of the schema for item types contained.
+     *
+     * @return The schema for the items in the array, or "##irrelevant" if irrelevant.
+     * 
+     * @deprecated (since 2.1) -- support will be dropped in future, since JSON-as-String is
+     *   fundamentally bad way for customizing anything. No direct replacements offered.
+     */
+    @Deprecated
+    public String schemaItemDefinition() default NO_VALUE;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java
new file mode 100644
index 0000000..f3505cd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/SchemaAware.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.jsonschema;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.lang.reflect.Type;
+
+/**
+ * Marker interface for schema-aware serializers.
+ */
+public interface SchemaAware
+{
+    /**
+     * Get the representation of the schema to which this serializer will conform.
+     *
+     * @param provider The serializer provider.
+     * @param typeHint A hint about the type.
+     * @return <a href="http://json-schema.org/">Json-schema</a> for this serializer.
+     */
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException;
+    
+    /**
+     * Get the representation of the schema to which this serializer will conform.
+     *
+     * @param provider The serializer provider.
+     * @param isOptional Is the type optional
+     * @param typeHint A hint about the type.
+     * @return <a href="http://json-schema.org/">Json-schema</a> for this serializer.
+     */
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java b/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java
new file mode 100644
index 0000000..443dcf4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsonschema/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Classes needed for JSON schema support (currently just ability
+ * to generate schemas using serialization part of data mapping)
+ */
+package com.fasterxml.jackson.databind.jsonschema;
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java
new file mode 100644
index 0000000..eeb7ad0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/NamedType.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+/**
+ * Simple container class for types with optional logical name, used
+ * as external identifier
+ * 
+ * @author tatu
+ */
+public final class NamedType
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final Class<?> _class;
+    protected final int _hashCode;
+
+    protected String _name;
+    
+    public NamedType(Class<?> c) { this(c, null); }
+    
+    public NamedType(Class<?> c, String name)
+    {
+        _class = c;
+        _hashCode = c.getName().hashCode();
+        setName(name);
+    }
+
+    public Class<?> getType() { return _class; }
+    public String getName() { return _name; }
+    public void setName(String name) {
+        _name = (name == null || name.length() == 0) ? null : name;
+    }
+
+    public boolean hasName() { return _name != null; }
+    
+    /**
+     * Equality is defined based on class only, not on name
+     */
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+        return _class == ((NamedType) o)._class;
+    }
+
+    @Override
+    public int hashCode() { return _hashCode; }
+
+    @Override
+    public String toString() {
+    	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
new file mode 100644
index 0000000..e23b6af
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/SubtypeResolver.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.Collection;
+
+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.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+
+/**
+ * Helper object used for handling registration on resolving of super-types
+ * to sub-types.
+ */
+public abstract class SubtypeResolver
+{
+    /**
+     * Method for registering specified subtypes (possibly including type
+     * names); for type entries without name, non-qualified class name
+     * as used as name (unless overridden by annotation).
+     */
+    public abstract void registerSubtypes(NamedType... types);
+
+    public abstract void registerSubtypes(Class<?>... classes);
+    
+    /**
+     * @deprecated Since 2.1: use variant that takes in property type.
+     */
+    @Deprecated
+    public abstract Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
+            MapperConfig<?> config, AnnotationIntrospector ai);
+
+    /**
+     * Method for finding out all reachable subtypes for a property specified
+     * by given element (method or field)
+     * 
+     * @param baseType Effective property base type to use; may differ from
+     *    actual type of property; for structured types it is content (value) type and NOT
+     *    structured type.
+     * 
+     * @since 2.1
+     */
+    public abstract Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
+            MapperConfig<?> config, AnnotationIntrospector ai, JavaType baseType);
+    
+    /**
+     * Method for finding out all reachable subtypes for given type.
+     */
+    public abstract Collection<NamedType> collectAndResolveSubtypes(AnnotatedClass basetype,
+            MapperConfig<?> config, AnnotationIntrospector ai);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java
new file mode 100644
index 0000000..4faa82c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeDeserializer.java
@@ -0,0 +1,193 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+
+
+/**
+ * Interface for deserializing type information from JSON content, to
+ * type-safely deserialize data into correct polymorphic instance
+ * (when type inclusion has been enabled for type handled).
+ *<p>
+ * Separate deserialization methods are needed because serialized
+ * form for inclusion mechanism {@link As#PROPERTY}
+ * is slighty different if value is not expressed as JSON Object:
+ * and as such both type deserializer and serializer need to
+ * JSON Object form (array, object or other (== scalar)) being used.
+ */
+public abstract class TypeDeserializer
+{
+    /*
+    /**********************************************************
+    /* Initialization
+    /**********************************************************
+     */
+
+    /**
+     * Method called to create contextual version, to be used for
+     * values of given property. This may be the type itself
+     * (as is the case for bean properties), or values contained
+     * (for {@link java.util.Collection} or {@link java.util.Map}
+     * valued properties).
+     * 
+     * @since 2.0
+     */
+    public abstract TypeDeserializer forProperty(BeanProperty prop);
+    
+    /*
+    /**********************************************************
+    /* Introspection
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for type information inclusion method
+     * that deserializer uses; indicates how type information
+     * is (expected to be) embedded in JSON input.
+     */
+    public abstract As getTypeInclusion();
+
+    /**
+     * Name of property that contains type information, if
+     * 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();
+
+    /**
+     * 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
+     * resolved)
+     */
+    public abstract Class<?> getDefaultImpl();
+    
+    /*
+    /**********************************************************
+    /* Type deserialization methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to let this type deserializer handle 
+     * deserialization of "typed" object, when value itself
+     * is serialized as JSON Object (regardless of Java type).
+     * Method needs to figure out intended
+     * polymorphic type, locate {@link JsonDeserializer} to use, and
+     * call it with JSON data to deserializer (which does not contain
+     * type information).
+     */
+    public abstract Object deserializeTypedFromObject(JsonParser jp,
+            DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method called to let this type deserializer handle 
+     * deserialization of "typed" object, when value itself
+     * is serialized as JSON Array (regardless of Java type).
+     * Method needs to figure out intended
+     * polymorphic type, locate {@link JsonDeserializer} to use, and
+     * call it with JSON data to deserializer (which does not contain
+     * type information).
+     */
+    public abstract Object deserializeTypedFromArray(JsonParser jp,
+            DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method called to let this type deserializer handle 
+     * deserialization of "typed" object, when value itself
+     * is serialized as a scalar JSON value (something other
+     * than Array or Object), regardless of Java type.
+     * Method needs to figure out intended
+     * polymorphic type, locate {@link JsonDeserializer} to use, and
+     * call it with JSON data to deserializer (which does not contain
+     * type information).
+     */
+    public abstract Object deserializeTypedFromScalar(JsonParser jp,
+            DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method called to let this type deserializer handle 
+     * deserialization of "typed" object, when value itself
+     * may have been serialized using any kind of JSON value
+     * (Array, Object, scalar). Should only be called if JSON
+     * serialization is polymorphic (not Java type); for example when
+     * using JSON node representation, or "untyped" Java object
+     * (which may be Map, Collection, wrapper/primitive etc).
+     */
+    public abstract Object deserializeTypedFromAny(JsonParser jp,
+            DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+
+    /*
+    /**********************************************************
+    /* Shared helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used to check if given parser might be pointing to
+     * a "natural" value, and one that would be acceptable as the
+     * result value (compatible with declared base type)
+     */
+    public static Object deserializeIfNatural(JsonParser jp, DeserializationContext ctxt,
+            JavaType baseType)
+        throws IOException, JsonProcessingException
+    {
+        return deserializeIfNatural(jp, ctxt, baseType.getRawClass());
+    }
+    
+    @SuppressWarnings("incomplete-switch")
+    public static Object deserializeIfNatural(JsonParser jp, DeserializationContext ctxt,
+            Class<?> base)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == null) {
+            return null;
+        }
+        switch (t) {
+        case VALUE_STRING:
+            if (base.isAssignableFrom(String.class)) {
+                return jp.getText();
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            if (base.isAssignableFrom(Integer.class)) {
+                return jp.getIntValue();
+            }
+            break;
+
+        case VALUE_NUMBER_FLOAT:
+            if (base.isAssignableFrom(Double.class)) {
+                return Double.valueOf(jp.getDoubleValue());
+            }
+            break;
+        case VALUE_TRUE:
+            if (base.isAssignableFrom(Boolean.class)) {
+                return Boolean.TRUE;
+            }
+            break;
+        case VALUE_FALSE:
+            if (base.isAssignableFrom(Boolean.class)) {
+                return Boolean.FALSE;
+            }
+            break;
+        }
+        return null;
+    }
+}
+    
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
new file mode 100644
index 0000000..ad352ea
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeIdResolver.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Interface that defines standard API for converting types
+ * to type identifiers and vice versa. Used by type resolvers
+ * ({@link com.fasterxml.jackson.databind.jsontype.TypeSerializer},
+ * {@link com.fasterxml.jackson.databind.jsontype.TypeDeserializer}) for converting
+ * between type and matching id; id is stored in JSON and needed for
+ * creating instances of proper subtypes when deserializing values.
+ */
+public interface TypeIdResolver
+{
+    /*
+    /**********************************************************
+    /* Initialization/configuration methods
+    /**********************************************************
+     */
+
+    /**
+     * Method that will be called once before any type resolution calls;
+     * used to initialize instance with configuration. This is necessary
+     * since instances may be created via reflection, without ability to
+     * call specific constructor to pass in configuration settings.
+     * 
+     * @param baseType Base type for which this id resolver instance is
+     *   used
+     */
+    public void init(JavaType baseType);
+
+    /*
+    /**********************************************************
+    /* Conversions between types and type ids
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to serialize type of the type of given value
+     * as a String to include in serialized JSON content.
+     */
+    public String idFromValue(Object value);
+
+    /**
+     * Alternative method used for determining type from combination of
+     * value and type, using suggested type (that serializer provides)
+     * and possibly value of that type. Most common implementation will
+     * use suggested type as is.
+     */
+    public String idFromValueAndType(Object value, Class<?> suggestedType);
+
+    /**
+     * Method that can be called to figure out type id to use for instances
+     * of base type (declared type of property). This is usually only used
+     * for fallback handling, for cases where real type information is not
+     * available for some reason.
+     */
+    public String idFromBaseType();
+    
+    /**
+     * Method called to resolve type from given type identifier.
+     */
+    public JavaType typeFromId(String id);
+
+    /*
+    /**********************************************************
+    /* Accessors for metadata
+    /**********************************************************
+     */
+
+     /**
+      * Accessor for mechanism that this resolver uses for determining
+      * type id from type. Mostly informational; not required to be called
+      * or used.
+      */
+     public JsonTypeInfo.Id getMechanism();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java
new file mode 100644
index 0000000..58a801b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeResolverBuilder.java
@@ -0,0 +1,151 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.SerializationConfig;
+
+/**
+ * Interface that defines builders that are configured based on
+ * annotations (like {@link com.fasterxml.jackson.annotation.JsonTypeInfo} or JAXB annotations),
+ * and produce type serializers and deserializers used for
+ * handling type information embedded in JSON to allow for safe
+ * polymorphic type handling.
+ *<p>
+ * Builder is first initialized by calling {@link #init} method, and then
+ * configured using <code>setXxx</code> (and <code>registerXxx</code>)
+ * methods. Finally, after calling all configuration methods,
+ * {@link #buildTypeSerializer} or {@link #buildTypeDeserializer}
+ * will be called to get actual type resolver constructed
+ * and used for resolving types for configured base type and its
+ * subtypes.
+ *<p>
+ * Note that instances are used for two related but distinct use cases:
+ *<ul>
+ * <li>To create builders to use with explicit type information
+ *    inclusion (usually via <code>@JsonTypeInfo</code> annotation)
+ *   </li>
+ * <li>To create builders when "default typing" is used; if so, type information
+ *   is automatically included for certain kind of types, regardless of annotations
+ *   </li>
+ *</ul>
+ * Important distinction between the cases is that in first case, calls to
+ * create builders are only made when builders are certainly needed; whereas
+ * in second case builder has to first verify whether type information is
+ * applicable for given type, and if not, just return null to indicate this.
+ * 
+ * @author tatu
+ */
+public interface TypeResolverBuilder<T extends TypeResolverBuilder<T>>
+{
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for currently configured default type; implementation
+     * class that may be used in case no valid type information is
+     * available during type resolution
+     */
+    public Class<?> getDefaultImpl();
+    
+    /*
+    /**********************************************************
+    /* Actual builder methods
+    /**********************************************************
+     */
+
+    /**
+     * Method for building type serializer based on current configuration
+     * of this builder.
+     * 
+     * @param baseType Base type that constructed resolver will
+     *    handle; super type of all types it will be used for.
+     */
+    public TypeSerializer buildTypeSerializer(SerializationConfig config,
+            JavaType baseType, Collection<NamedType> subtypes);
+
+    /**
+     * Method for building type deserializer based on current configuration
+     * of this builder.
+     * 
+     * @param baseType Base type that constructed resolver will
+     *    handle; super type of all types it will be used for.
+     * @param subtypes Known subtypes of the base type.
+     */
+    public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
+            JavaType baseType, Collection<NamedType> subtypes);
+    
+    /*
+    /**********************************************************
+    /* Initialization method(s) that must be called before other
+    /* configuration
+    /**********************************************************
+     */
+
+    /**
+     * Initialization method that is called right after constructing
+     * the builder instance.
+     *
+     * @param idType Which type metadata is used
+     * @param res (optional) Custom type id resolver used, if any
+     * 
+     * @return Resulting builder instance (usually this builder,
+     *   but not necessarily)
+     */
+    public T init(JsonTypeInfo.Id idType, TypeIdResolver res);
+    
+    /*
+    /**********************************************************
+    /* Methods for configuring resolver to build 
+    /**********************************************************
+     */
+    
+    /**
+     * Method for specifying mechanism to use for including type metadata
+     * in JSON.
+     * If not explicitly called, setting defaults to
+     * {@link As#PROPERTY}.
+     * 
+     * @param includeAs Mechanism used for including type metadata in JSON
+     * 
+     * @return Resulting builder instance (usually this builder,
+     *   but not necessarily)
+     */
+    public T inclusion(As includeAs);
+
+    /**
+     * Method for specifying name of property used for including type
+     * information. Not used for all inclusion mechanisms;
+     * usually only used with {@link As#PROPERTY}.
+     *<p>
+     * If not explicitly called, name of property to use is based on
+     * defaults for {@link com.fasterxml.jackson.annotation.JsonTypeInfo.Id} configured.
+     * 
+     * @param propName Name of JSON property to use for including
+     *    type information
+     * 
+     * @return Resulting builder instance (usually this builder,
+     *   but not necessarily)
+     */
+    public T typeProperty(String propName);
+
+    /**
+     * Method for specifying default implementation to use if type id 
+     * is either not available, or can not be resolved.
+     */
+    public T defaultImpl(Class<?> defaultImpl);
+
+    /**
+     * Method for specifying whether type id should be visible to
+     * {@link com.fasterxml.jackson.databind.JsonDeserializer}s or not.
+     * 
+     * @since 2.0
+     */
+    public T typeIdVisibility(boolean isVisible);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
new file mode 100644
index 0000000..c93cabd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/TypeSerializer.java
@@ -0,0 +1,231 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+
+/**
+ * Interface for serializing type information regarding instances of specified
+ * base type (super class), so that exact subtype can be properly deserialized
+ * later on. These instances are to be called by regular
+ * {@link com.fasterxml.jackson.databind.JsonSerializer}s using proper contextual
+ * calls, to add type information using mechanism type serializer was
+ * configured with.
+ * 
+ * @author tatu
+ */
+public abstract class TypeSerializer
+{
+    /*
+    /**********************************************************
+    /* Initialization
+    /**********************************************************
+     */
+
+    /**
+     * Method called to create contextual version, to be used for
+     * values of given property. This may be the type itself
+     * (as is the case for bean properties), or values contained
+     * (for {@link java.util.Collection} or {@link java.util.Map}
+     * valued properties).
+     * 
+     * @since 2.0
+     */
+    public abstract TypeSerializer forProperty(BeanProperty prop);
+    
+    /*
+    /**********************************************************
+    /* Introspection
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for type information inclusion method
+     * that serializer uses; indicates how type information
+     * is embedded in resulting JSON.
+     */
+    public abstract JsonTypeInfo.As getTypeInclusion();
+
+    /**
+     * Name of property that contains type information, if
+     * 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
+    /**********************************************************
+     */
+    
+    /**
+     * 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 jgen Generator to use for writing type information
+     */
+    public abstract void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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.
+     * 
+     * @param value Value that will be serialized, for which type information is
+     *   to be written
+     * @param jgen Generator to use for writing type information
+     */
+    public abstract void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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 jgen Generator to use for writing type information
+     */
+    public abstract void writeTypePrefixForArray(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+    
+    /**
+     * 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).
+     */
+    public abstract void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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.
+     */
+    public abstract void writeTypeSuffixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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.
+     */
+    public abstract void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * 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
+     */
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        writeTypePrefixForScalar(value, jgen);
+    }
+
+    /**
+     * 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
+     */
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        writeTypePrefixForObject(value, jgen);
+    }
+
+    /**
+     * 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
+     */
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        writeTypePrefixForArray(value, jgen);
+    }
+
+    /*
+    /**********************************************************
+    /* Type serialization methods with type id override
+    /**********************************************************
+     */
+
+    /**
+     * 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 jgen Generator to use for writing type information
+     * @param typeId Exact type id to use
+     */
+    public abstract void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+    
+    /**
+     * 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 jgen Generator to use for writing type information
+     * @param typeId Exact type id to use
+     */
+    public abstract void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+    
+    public abstract void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+
+    public abstract void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+
+    public abstract void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+
+    public abstract void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen,
+            String typeId)
+        throws IOException, JsonProcessingException;
+
+}
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
new file mode 100644
index 0000000..bb6dc72
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeDeserializer.java
@@ -0,0 +1,146 @@
+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.core.util.JsonParserSequence;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Type deserializer used with {@link As#WRAPPER_ARRAY}
+ * inclusion mechanism. Simple since JSON structure used is always
+ * the same, regardless of structure used for actual value: wrapping
+ * is done using a 2-element JSON Array where type id is the first
+ * element, and actual object data as second element.
+ * 
+ * @author tatu
+ */
+public class AsArrayTypeDeserializer
+    extends TypeDeserializerBase
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 5345570420394408290L;
+
+    public AsArrayTypeDeserializer(JavaType bt, TypeIdResolver idRes,
+            String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+    {
+        super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
+    }
+
+    public AsArrayTypeDeserializer(AsArrayTypeDeserializer src, BeanProperty property) {
+        super(src, property);
+    }
+    
+    @Override
+    public TypeDeserializer forProperty(BeanProperty prop) {
+        if (prop == _property) { // usually if it's null
+            return this;
+        }
+        return new AsArrayTypeDeserializer(this, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() {
+        return As.WRAPPER_ARRAY;
+    }
+
+    /**
+     * Method called when actual object is serialized as JSON Array.
+     */
+    @Override
+    public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }
+
+    /**
+     * Method called when actual object is serialized as JSON Object
+     */
+    @Override
+    public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }
+    
+    @Override
+    public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }    
+
+    @Override
+    public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }    
+    
+    /*
+    /***************************************************************
+    /* Internal methods
+    /***************************************************************
+     */
+
+    /**
+     * Method that handles type information wrapper, locates actual
+     * subtype deserializer to use, and calls it to do actual
+     * deserialization.
+     */
+    private final Object _deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        boolean hadStartArray = jp.isExpectedStartArrayToken();
+        String typeId = _locateTypeId(jp, ctxt);
+        JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
+        // Minor complication: we may need to merge type id in?
+        if (_typeIdVisible && jp.getCurrentToken() == JsonToken.START_OBJECT) {
+            // but what if there's nowhere to add it in? Error? Or skip? For now, skip.
+            @SuppressWarnings("resource")
+            TokenBuffer tb = new TokenBuffer(null);
+            tb.writeStartObject(); // recreate START_OBJECT
+            tb.writeFieldName(_typePropertyName);
+            tb.writeString(typeId);
+            jp = JsonParserSequence.createFlattened(tb.asParser(jp), jp);
+            jp.nextToken();
+        }
+        Object value = deser.deserialize(jp, ctxt);
+        // And then need the closing END_ARRAY
+        if (hadStartArray && jp.nextToken() != JsonToken.END_ARRAY) {
+            throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY,
+                    "expected closing END_ARRAY after type information and deserialized value");
+        }
+        return value;
+    }    
+    
+    protected final String _locateTypeId(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        if (!jp.isExpectedStartArrayToken()) {
+            // [JACKSON-712] Need to allow even more customized handling, if something unexpected seen...
+            // but should there be a way to limit this to likely success cases?
+            if (_defaultImpl != null) {
+                return _idResolver.idFromBaseType();
+            }
+            throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "need JSON Array to contain As.WRAPPER_ARRAY type information for class "+baseTypeName());
+        }
+        // And then type id as a String
+        JsonToken t = jp.nextToken();
+        if (t == JsonToken.VALUE_STRING) {
+            String result = jp.getText();
+            jp.nextToken();
+            return result;
+        }
+        if (_defaultImpl != null) {
+            return _idResolver.idFromBaseType();
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+    }
+}
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
new file mode 100644
index 0000000..d750454
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsArrayTypeSerializer.java
@@ -0,0 +1,176 @@
+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;
+
+/**
+ * Type serializer that will embed type information in an array,
+ * as the first element, and actual value as the second element.
+ * 
+ * @author tatu
+ */
+public class AsArrayTypeSerializer
+    extends TypeSerializerBase
+{
+    public AsArrayTypeSerializer(TypeIdResolver idRes, BeanProperty property)
+    {
+        super(idRes, property);
+    }
+
+    @Override
+    public AsArrayTypeSerializer forProperty(BeanProperty prop) {
+        if (_property == prop) return this;
+        return new AsArrayTypeSerializer(this._idResolver, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() { return As.WRAPPER_ARRAY; }
+    
+    /*
+    /**********************************************************
+    /* Writing prefixes
+    /**********************************************************
+     */
+    
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(idFromValue(value));
+        jgen.writeStartObject();
+    }
+
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(idFromValueAndType(value, type));
+        jgen.writeStartObject();
+    }
+    
+    @Override
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(idFromValue(value));
+        jgen.writeStartArray();
+    }
+
+    @Override
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(idFromValueAndType(value, type));
+        jgen.writeStartArray();
+    }
+    
+    @Override
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException
+    {
+        // only need the wrapper array
+        jgen.writeStartArray();
+        jgen.writeString(idFromValue(value));
+    }
+
+    @Override
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        // only need the wrapper array
+        jgen.writeStartArray();
+        jgen.writeString(idFromValueAndType(value, type));
+    }
+
+    /*
+    /**********************************************************
+    /* Writing suffixes
+    /**********************************************************
+     */
+    
+    @Override
+    public void writeTypeSuffixForObject(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException
+    {
+        jgen.writeEndObject();
+        jgen.writeEndArray();
+    }
+
+    @Override
+    public void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException
+    {
+        // wrapper array first, and then array caller needs to close
+        jgen.writeEndArray();
+        jgen.writeEndArray();
+    }
+
+    @Override
+    public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
+            throws IOException, JsonProcessingException
+    {
+        // just the wrapper array to close
+        jgen.writeEndArray();
+    }
+    
+    /*
+    /**********************************************************
+    /* Writing with custom type id
+    /**********************************************************
+     */
+
+    @Override
+    public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException {
+        jgen.writeStartArray();
+        jgen.writeString(typeId);
+        jgen.writeStartObject();
+    }
+    
+    @Override
+    public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(typeId);
+        jgen.writeStartArray();
+    }
+
+    @Override
+    public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartArray();
+        jgen.writeString(typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException {
+        writeTypeSuffixForObject(value, jgen); // standard impl works fine
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId)
+            throws IOException, JsonProcessingException {
+        writeTypeSuffixForArray(value, jgen); // standard impl works fine
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException {
+        writeTypeSuffixForScalar(value, jgen); // standard impl works fine
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java
new file mode 100644
index 0000000..c12112a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeDeserializer.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+/**
+ * Type deserializer used with {@link As#EXTERNAL_PROPERTY} inclusion mechanism.
+ * Actual implementation may look bit strange since it depends on comprehensive
+ * pre-processing done by {@link com.fasterxml.jackson.databind.deser.BeanDeserializer}
+ * to basically transform external type id into structure that looks more like
+ * "wrapper-array" style inclusion. This intermediate form is chosen to allow
+ * supporting all possible JSON structures.
+ */
+public class AsExternalTypeDeserializer extends AsArrayTypeDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    public AsExternalTypeDeserializer(JavaType bt, TypeIdResolver idRes,
+            String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+    {
+        super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
+    }
+
+    public AsExternalTypeDeserializer(AsExternalTypeDeserializer src, BeanProperty property) {
+        super(src, property);
+    }
+    
+    @Override
+    public TypeDeserializer forProperty(BeanProperty prop)
+    {
+        if (prop == _property) { // usually if it's null
+            return this;
+        }
+        return new AsExternalTypeDeserializer(this, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() {
+        return As.EXTERNAL_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
new file mode 100644
index 0000000..8ce5da2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsExternalTypeSerializer.java
@@ -0,0 +1,205 @@
+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;
+
+/**
+ * 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
+ * to revert operation similar to {@link AsPropertyTypeSerializer}.
+ *<p>
+ * Note that implementation of serialization is bit cumbersome as we must
+ * serialized external type id AFTER object; this because callback only
+ * occurs after field name has been written.
+ */
+public class AsExternalTypeSerializer
+   extends TypeSerializerBase
+{
+   protected final String _typePropertyName;
+
+   public AsExternalTypeSerializer(TypeIdResolver idRes, BeanProperty property,
+          String propName)
+   {
+       super(idRes, property);
+       _typePropertyName = propName;
+   }
+
+   @Override
+   public AsExternalTypeSerializer forProperty(BeanProperty prop) {
+       if (_property == prop) return this;
+       return new AsExternalTypeSerializer(_idResolver, prop, _typePropertyName);
+   }
+   
+   @Override
+   public String getPropertyName() { return _typePropertyName; }
+
+   @Override
+   public As getTypeInclusion() { return As.EXTERNAL_PROPERTY; }
+
+   /*
+   /**********************************************************
+   /* Writing prefixes
+   /**********************************************************
+    */
+   
+   @Override
+   public void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException
+   {
+       _writeObjectPrefix(value, jgen);
+   }
+
+   @Override
+   public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type)
+       throws IOException, JsonProcessingException
+   {
+       _writeObjectPrefix(value, jgen);
+   }
+   
+   @Override
+   public void writeTypePrefixForArray(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException
+   {
+       _writeArrayPrefix(value, jgen);
+   }
+
+   @Override
+   public void writeTypePrefixForArray(Object value, JsonGenerator jgen, Class<?> type)
+           throws IOException, JsonProcessingException
+   {
+       _writeArrayPrefix(value, jgen);
+   }
+
+   @Override
+   public void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
+           throws IOException, JsonProcessingException
+   {
+       _writeScalarPrefix(value, jgen);
+   }
+
+   @Override
+   public void writeTypePrefixForScalar(Object value, JsonGenerator jgen, Class<?> type)
+           throws IOException, JsonProcessingException
+   {
+       _writeScalarPrefix(value, jgen);
+   }
+
+   /*
+   /**********************************************************
+   /* Writing suffixes
+   /**********************************************************
+    */
+   
+   @Override
+   public void writeTypeSuffixForObject(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException
+   {
+       _writeObjectSuffix(value, jgen, idFromValue(value));
+   }
+
+   @Override
+   public void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException
+   {
+       _writeArraySuffix(value, jgen, idFromValue(value));
+   }
+   
+   @Override
+   public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException
+   {
+       _writeScalarSuffix(value, jgen, idFromValue(value));
+   }
+
+   /*
+   /**********************************************************
+   /* Writing with custom type id
+   /**********************************************************
+    */
+
+   @Override
+   public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException
+   {
+       _writeScalarPrefix(value, jgen);
+   }
+   
+   @Override
+   public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException {
+       _writeObjectPrefix(value, jgen);
+   }
+   
+   @Override
+   public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException
+   {
+       _writeArrayPrefix(value, jgen);
+   }
+
+   @Override
+   public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException {
+       _writeScalarSuffix(value, jgen, typeId);
+   }
+
+   @Override
+   public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException {
+       _writeObjectSuffix(value, jgen, typeId);
+   }
+
+   @Override
+   public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId)
+           throws IOException, JsonProcessingException {
+       _writeArraySuffix(value, jgen, typeId);
+   }
+   
+   /*
+   /**********************************************************
+   /* Helper methods
+   /**********************************************************
+    */
+
+   protected final void _writeScalarPrefix(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException {
+       // nothing to wrap it with
+   }
+
+   protected final void _writeObjectPrefix(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException {
+       jgen.writeStartObject();
+   }
+
+   protected final void _writeArrayPrefix(Object value, JsonGenerator jgen)
+       throws IOException, JsonProcessingException {
+       jgen.writeStartArray();
+   }
+   
+   protected final void _writeScalarSuffix(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException
+   {
+       jgen.writeStringField(_typePropertyName, typeId);
+   }
+   
+   protected final void _writeObjectSuffix(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException
+   {
+       jgen.writeEndObject();
+       jgen.writeStringField(_typePropertyName, typeId);
+   }
+
+   protected final void _writeArraySuffix(Object value, JsonGenerator jgen, String typeId)
+       throws IOException, JsonProcessingException
+   {
+       jgen.writeEndArray();
+       jgen.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
new file mode 100644
index 0000000..84a25a0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeDeserializer.java
@@ -0,0 +1,165 @@
+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.core.util.JsonParserSequence;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Type deserializer used with {@link As#PROPERTY}
+ * inclusion mechanism.
+ * Uses regular form (additional key/value entry before actual data)
+ * when typed object is expressed as JSON Object; otherwise behaves similar to how
+ * {@link As#WRAPPER_ARRAY} works.
+ * Latter is used if JSON representation is polymorphic
+ * 
+ * @author tatu
+ */
+public class AsPropertyTypeDeserializer extends AsArrayTypeDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    public AsPropertyTypeDeserializer(JavaType bt, TypeIdResolver idRes,
+            String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+    {
+        super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl);
+    }
+
+    public AsPropertyTypeDeserializer(AsPropertyTypeDeserializer src, BeanProperty property) {
+        super(src, property);
+    }
+    
+    @Override
+    public TypeDeserializer forProperty(BeanProperty prop) {
+        if (prop == _property) { // usually if it's null
+            return this;
+        }
+        return new AsPropertyTypeDeserializer(this, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() {
+        return As.PROPERTY;
+    }
+
+    /**
+     * This is the trickiest thing to handle, since property we are looking
+     * for may be anywhere...
+     */
+    @Override
+    public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // but first, sanity check to ensure we have START_OBJECT or FIELD_NAME
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = jp.nextToken();
+        } else if (t == JsonToken.START_ARRAY) {
+            /* This is most likely due to the fact that not all Java types are
+             * serialized as JSON Objects; so if "as-property" inclusion is requested,
+             * serialization of things like Lists must be instead handled as if
+             * "as-wrapper-array" was requested.
+             * But this can also be due to some custom handling: so, if "defaultImpl"
+             * is defined, it will be asked to handle this case.
+             */
+            return _deserializeTypedUsingDefaultImpl(jp, ctxt, null);
+        } else if (t != JsonToken.FIELD_NAME) {
+            return _deserializeTypedUsingDefaultImpl(jp, ctxt, null);
+        }
+        // Ok, let's try to find the property. But first, need token buffer...
+        TokenBuffer tb = null;
+
+        for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) {
+            String name = jp.getCurrentName();
+            jp.nextToken(); // to point to the value
+            if (_typePropertyName.equals(name)) { // gotcha!
+                return _deserializeTypedForId(jp, ctxt, tb);
+            }
+            if (tb == null) {
+                tb = new TokenBuffer(null);
+            }
+            tb.writeFieldName(name);
+            tb.copyCurrentStructure(jp);
+        }
+        return _deserializeTypedUsingDefaultImpl(jp, ctxt, tb);
+    }
+
+    @SuppressWarnings("resource")
+    protected final Object _deserializeTypedForId(JsonParser jp, DeserializationContext ctxt,
+            TokenBuffer tb)
+        throws IOException, JsonProcessingException
+    {
+        String typeId = jp.getText();
+        JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
+        if (_typeIdVisible) { // need to merge id back in JSON input?
+            if (tb == null) {
+                tb = new TokenBuffer(null);
+            }
+            tb.writeFieldName(jp.getCurrentName());
+            tb.writeString(typeId);
+        }
+        if (tb != null) { // need to put back skipped properties?
+            jp = JsonParserSequence.createFlattened(tb.asParser(jp), jp);
+        }
+        // Must point to the next value; tb had no current, jp pointed to VALUE_STRING:
+        jp.nextToken(); // to skip past String value
+        // deserializer should take care of closing END_OBJECT as well
+        return deser.deserialize(jp, ctxt);
+    }
+    
+    // off-lined to keep main method lean and mean...
+    protected Object _deserializeTypedUsingDefaultImpl(JsonParser jp,
+            DeserializationContext ctxt, TokenBuffer tb)
+        throws IOException, JsonProcessingException
+    {
+        // As per [JACKSON-614], may have default implementation to use
+        JsonDeserializer<Object> deser = _findDefaultImplDeserializer(ctxt);
+        if (deser != null) {
+            if (tb != null) {
+                tb.writeEndObject();
+                jp = tb.asParser(jp);
+                // must move to point to the first token:
+                jp.nextToken();
+            }
+            return deser.deserialize(jp, ctxt);
+        }
+        // or, perhaps we just bumped into a "natural" value (boolean/int/double/String)?
+        Object result = TypeDeserializer.deserializeIfNatural(jp, ctxt, _baseType);
+        if (result != null) {
+            return result;
+        }
+        // or, something for which "as-property" won't work, changed into "wrapper-array" type:
+        if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
+            return super.deserializeTypedFromAny(jp, ctxt);
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.FIELD_NAME,
+                "missing property '"+_typePropertyName+"' that is to contain type id  (for class "+baseTypeName()+")");
+    }
+
+    /* As per [JACKSON-352], also need to re-route "unknown" version. Need to think
+     * this through bit more in future, but for now this does address issue and has
+     * no negative side effects (at least within existing unit test suite).
+     */
+    @Override
+    public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        /* [JACKSON-387]: Sometimes, however, we get an array wrapper; specifically
+         *   when an array or list has been serialized with type information.
+         */
+        if (jp.getCurrentToken() == JsonToken.START_ARRAY) {
+            return super.deserializeTypedFromArray(jp, ctxt);
+        }
+        return deserializeTypedFromObject(jp, ctxt);
+    }    
+    
+    // These are fine from base class:
+    //public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt)
+    //public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt)    
+}
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
new file mode 100644
index 0000000..07d5122
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsPropertyTypeSerializer.java
@@ -0,0 +1,96 @@
+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;
+
+/**
+ * Type serializer that preferably embeds type information as an additional
+ * JSON Object property, if possible (when resulting serialization would
+ * use JSON Object). If this is not possible (for JSON Arrays, scalars),
+ * uses a JSON Array wrapper (similar to how
+ * {@link As#WRAPPER_ARRAY} always works) as a fallback.
+ * 
+ * @author tatus
+ */
+public class AsPropertyTypeSerializer
+    extends AsArrayTypeSerializer
+{
+    protected final String _typePropertyName;
+
+    public AsPropertyTypeSerializer(TypeIdResolver idRes, BeanProperty property,
+            String propName)
+    {
+        super(idRes, property);
+        _typePropertyName = propName;
+    }
+
+    @Override
+    public AsPropertyTypeSerializer forProperty(BeanProperty prop) {
+        if (_property == prop) return this;
+        return new AsPropertyTypeSerializer(this._idResolver, prop, this._typePropertyName);
+    }
+    
+    @Override
+    public String getPropertyName() { return _typePropertyName; }
+
+    @Override
+    public As getTypeInclusion() { return As.PROPERTY; }
+    
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        jgen.writeStringField(_typePropertyName, idFromValue(value));
+    }
+
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen, Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        jgen.writeStringField(_typePropertyName, idFromValueAndType(value, type));
+    }
+    
+    //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, JsonProcessingException
+    {
+        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, JsonProcessingException {
+        jgen.writeStartObject();
+        jgen.writeStringField(_typePropertyName, typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException {
+        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
new file mode 100644
index 0000000..9fef7b5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeDeserializer.java
@@ -0,0 +1,132 @@
+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.core.util.JsonParserSequence;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Type deserializer used with {@link As#WRAPPER_OBJECT}
+ * inclusion mechanism. Simple since JSON structure used is always
+ * the same, regardless of structure used for actual value: wrapping
+ * is done using a single-element JSON Object where type id is the key,
+ * and actual object data as the value.
+ * 
+ * @author tatu
+ */
+public class AsWrapperTypeDeserializer
+    extends TypeDeserializerBase
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 5345570420394408290L;
+
+    public AsWrapperTypeDeserializer(JavaType bt, TypeIdResolver idRes,
+            String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+    {
+        super(bt, idRes, typePropertyName, typeIdVisible, null);
+    }
+
+    protected AsWrapperTypeDeserializer(AsWrapperTypeDeserializer src, BeanProperty property) {
+        super(src, property);
+    }
+    
+    @Override
+    public TypeDeserializer forProperty(BeanProperty prop)
+    {
+        if (prop == _property) { // usually if it's null
+            return this;
+        }
+        return new AsWrapperTypeDeserializer(this, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() {
+        return As.WRAPPER_OBJECT;
+    }
+
+    /**
+     * Deserializing type id enclosed using WRAPPER_OBJECT style is straightforward
+     */
+    @Override
+    public Object deserializeTypedFromObject(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }    
+
+    @Override
+    public Object deserializeTypedFromArray(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }
+
+    @Override
+    public Object deserializeTypedFromScalar(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }
+
+    @Override
+    public Object deserializeTypedFromAny(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        return _deserialize(jp, ctxt);
+    }
+    
+    /*
+    /***************************************************************
+    /* Internal methods
+    /***************************************************************
+     */
+
+    /**
+     * Method that handles type information wrapper, locates actual
+     * subtype deserializer to use, and calls it to do actual
+     * deserialization.
+     */
+    private final Object _deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // first, sanity checks
+        if (jp.getCurrentToken() != JsonToken.START_OBJECT) {
+            throw ctxt.wrongTokenException(jp, JsonToken.START_OBJECT,
+                    "need JSON Object to contain As.WRAPPER_OBJECT type information for class "+baseTypeName());
+        }
+        // should always get field name, but just in case...
+        if (jp.nextToken() != JsonToken.FIELD_NAME) {
+            throw ctxt.wrongTokenException(jp, JsonToken.FIELD_NAME,
+                    "need JSON String that contains type id (for subtype of "+baseTypeName()+")");
+        }
+        final String typeId = jp.getText();
+        JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
+        jp.nextToken();
+
+        // Minor complication: we may need to merge type id in?
+        if (_typeIdVisible && jp.getCurrentToken() == JsonToken.START_OBJECT) {
+            // but what if there's nowhere to add it in? Error? Or skip? For now, skip.
+            @SuppressWarnings("resource")
+            TokenBuffer tb = new TokenBuffer(null);
+            tb.writeStartObject(); // recreate START_OBJECT
+            tb.writeFieldName(_typePropertyName);
+            tb.writeString(typeId);
+            jp = JsonParserSequence.createFlattened(tb.asParser(jp), jp);
+            jp.nextToken();
+        }
+        
+        Object value = deser.deserialize(jp, ctxt);
+        // And then need the closing END_OBJECT
+        if (jp.nextToken() != JsonToken.END_OBJECT) {
+            throw ctxt.wrongTokenException(jp, 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
new file mode 100644
index 0000000..0f4a4ae
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/AsWrapperTypeSerializer.java
@@ -0,0 +1,174 @@
+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;
+
+/**
+ * Type wrapper that tries to use an extra JSON Object, with a single
+ * entry that has type name as key, to serialize type information.
+ * If this is not possible (value is serialize as array or primitive),
+ * will use {@link As#WRAPPER_ARRAY} mechanism as fallback: that is,
+ * just use a wrapping array with type information as the first element
+ * and value as second.
+ * 
+ * @author tatu
+ */
+public class AsWrapperTypeSerializer
+    extends TypeSerializerBase
+{
+    public AsWrapperTypeSerializer(TypeIdResolver idRes, BeanProperty property)
+    {
+        super(idRes, property);
+    }
+
+    @Override
+    public AsWrapperTypeSerializer forProperty(BeanProperty prop) {
+        if (_property == prop) return this;
+        return new AsWrapperTypeSerializer(this._idResolver, prop);
+    }
+    
+    @Override
+    public As getTypeInclusion() { return As.WRAPPER_OBJECT; }
+    
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // wrapper
+        jgen.writeStartObject();
+        // and then JSON Object start caller wants
+        jgen.writeObjectFieldStart(idFromValue(value));
+    }
+
+    @Override
+    public void writeTypePrefixForObject(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        // wrapper
+        jgen.writeStartObject();
+        // and then JSON Object start caller wants
+        jgen.writeObjectFieldStart(idFromValueAndType(value, type));
+    }
+    
+    @Override
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // can still wrap ok
+        jgen.writeStartObject();
+        // and then JSON Array start caller wants
+        jgen.writeArrayFieldStart(idFromValue(value));
+    }
+
+    @Override
+    public void writeTypePrefixForArray(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        // can still wrap ok
+        jgen.writeStartObject();
+        // and then JSON Array start caller wants
+        jgen.writeArrayFieldStart(idFromValueAndType(value, type));
+    }
+
+    @Override
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // can still wrap ok
+        jgen.writeStartObject();
+        jgen.writeFieldName(idFromValue(value));
+    }
+
+    @Override
+    public void writeTypePrefixForScalar(Object value, JsonGenerator jgen,
+            Class<?> type)
+        throws IOException, JsonProcessingException
+    {
+        // can still wrap ok
+        jgen.writeStartObject();
+        jgen.writeFieldName(idFromValueAndType(value, type));
+    }
+    
+    @Override
+    public void writeTypeSuffixForObject(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // first close JSON Object caller used
+        jgen.writeEndObject();
+        // and then wrapper
+        jgen.writeEndObject();
+    }
+
+    @Override
+    public void writeTypeSuffixForArray(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // first close array caller needed
+        jgen.writeEndArray();
+        // then wrapper object
+        jgen.writeEndObject();
+    }
+    
+    @Override
+    public void writeTypeSuffixForScalar(Object value, JsonGenerator jgen)
+        throws IOException, JsonProcessingException
+    {
+        // just need to close the wrapper object
+        jgen.writeEndObject();
+    }
+
+    /*
+    /**********************************************************
+    /* Writing with custom type id
+    /**********************************************************
+     */
+    
+    @Override
+    public void writeCustomTypePrefixForObject(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        jgen.writeObjectFieldStart(typeId);
+    }
+    
+    @Override
+    public void writeCustomTypePrefixForArray(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        jgen.writeArrayFieldStart(typeId);
+    }
+
+    @Override
+    public void writeCustomTypePrefixForScalar(Object value, JsonGenerator jgen, String typeId)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        jgen.writeFieldName(typeId);
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForObject(Object value, JsonGenerator jgen, String typeId)
+            throws IOException, JsonProcessingException {
+        writeTypeSuffixForObject(value, jgen); // standard impl works fine
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForArray(Object value, JsonGenerator jgen, String typeId)
+            throws IOException, JsonProcessingException {
+        writeTypeSuffixForArray(value, jgen); // standard impl works fine
+    }
+
+    @Override
+    public void writeCustomTypeSuffixForScalar(Object value, JsonGenerator jgen, String typeId)
+            throws IOException, JsonProcessingException {
+        writeTypeSuffixForScalar(value, jgen); // standard impl works fine
+    }
+}
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
new file mode 100644
index 0000000..6e3f732
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/ClassNameIdResolver.java
@@ -0,0 +1,132 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver} implementation
+ * that converts between fully-qualified
+ * Java class names and (JSON) Strings.
+ */
+public class ClassNameIdResolver
+    extends TypeIdResolverBase
+{
+    public ClassNameIdResolver(JavaType baseType, TypeFactory typeFactory) {
+        super(baseType, typeFactory);
+    }
+
+    @Override
+    public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.CLASS; }
+
+    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());
+    }
+
+    @Override
+    public String idFromValueAndType(Object value, Class<?> type) {
+        return _idFrom(value, type);
+    }
+
+    @Override
+    public JavaType typeFromId(String id)
+    {
+        /* 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 (id.indexOf('<') > 0) {
+            JavaType t = _typeFactory.constructFromCanonical(id);
+            // note: may want to try combining with specialization (esp for EnumMap)?
+            return t;
+        }
+        try {
+            Class<?> cls =  ClassUtil.findClass(id);
+            return _typeFactory.constructSpecializedType(_baseType, cls);
+        } catch (ClassNotFoundException e) {
+            throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): no such class found");
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid type id '"+id+"' (for id type 'Id.class'): "+e.getMessage(), e);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    protected final String _idFrom(Object value, Class<?> cls)
+    {
+        // [JACKSON-380] Need to ensure that "enum subtypes" work too
+        if (Enum.class.isAssignableFrom(cls)) {
+            if (!cls.isEnum()) { // means that it's sub-class of base enum, so:
+                cls = cls.getSuperclass();
+            }
+        }
+        String str = cls.getName();
+        if (str.startsWith("java.util")) {
+            /* 25-Jan-2009, tatu: There are some internal classes that
+             *   we can not access as is. We need better mechanism; for
+             *   now this has to do...
+             */
+            /* Enum sets and maps are problematic since we MUST know
+             * type of contained enums, to be able to deserialize.
+             * In addition, EnumSet is not a concrete type either
+             */
+            if (value instanceof EnumSet<?>) { // Regular- and JumboEnumSet...
+                Class<?> enumClass = ClassUtil.findEnumType((EnumSet<?>) value);
+                // not optimal: but EnumSet is not a customizable type so this is sort of ok
+                str = TypeFactory.defaultInstance().constructCollectionType(EnumSet.class, enumClass).toCanonical();
+            } else if (value instanceof EnumMap<?,?>) {
+                Class<?> enumClass = ClassUtil.findEnumType((EnumMap<?,?>) value);
+                Class<?> valueClass = Object.class;
+                // not optimal: but EnumMap is not a customizable type so this is sort of ok
+                str = TypeFactory.defaultInstance().constructMapType(EnumMap.class, enumClass, valueClass).toCanonical();
+            } else {
+                String end = str.substring(9);
+                if ((end.startsWith(".Arrays$") || end.startsWith(".Collections$"))
+                       && str.indexOf("List") >= 0) {
+                    /* 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
+                     * NOTE: chances are there are plenty of similar cases
+                     * for other wrappers... (immutable, singleton, synced etc)
+                     */
+                    str = "java.util.ArrayList";
+                }
+            }
+        } else if (str.indexOf('$') >= 0) {
+            /* Other special handling may be needed for inner classes, [JACKSON-584].
+             * The best way to handle would be to find 'hidden' constructor; pass parent
+             * value etc (which is actually done for non-anonymous static classes!),
+             * but that is just not possible due to various things. So, we will instead
+             * try to generalize type into something we will be more likely to be able
+             * construct.
+             */
+            Class<?> outer = ClassUtil.getOuterClass(cls);
+            if (outer != null) {
+                /* one more check: let's actually not worry if the declared
+                 * static type is non-static as well; if so, deserializer does
+                 * have a chance at figuring it all out.
+                 */
+                Class<?> staticType = _baseType.getRawClass();
+                if (ClassUtil.getOuterClass(staticType) == null) {
+                    // Is this always correct? Seems like it should be...
+                    cls = _baseType.getRawClass();
+                    str = cls.getName();
+                }
+            }
+        }
+        return str;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/FailingDeserializer.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/FailingDeserializer.java
new file mode 100644
index 0000000..76005f0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/FailingDeserializer.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+/**
+ * Special bogus "serializer" that will throw
+ * {@link JsonMappingException} if an attempt is made to deserialize
+ * a value. This is used as placeholder to avoid NPEs for uninitialized
+ * structured serializers or handlers.
+ */
+public class FailingDeserializer extends StdDeserializer<Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final String _message;
+
+    public FailingDeserializer(String m) {
+        super(Object.class);
+        _message = m;
+    }
+    
+    @Override
+    public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws JsonMappingException
+    {
+        throw ctxt.mappingException(_message);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
new file mode 100644
index 0000000..a377489
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/MinimalClassNameIdResolver.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class MinimalClassNameIdResolver
+    extends ClassNameIdResolver
+{
+    /**
+     * Package name of the base class, to be used for determining common
+     * prefix that can be omitted from included type id.
+     * Does not include the trailing dot.
+     */
+    protected final String _basePackageName;
+
+    /**
+     * Same as {@link #_basePackageName}, but includes trailing dot.
+     */
+    protected final String _basePackagePrefix;
+    
+    protected MinimalClassNameIdResolver(JavaType baseType, TypeFactory typeFactory)
+    {
+        super(baseType, typeFactory);
+        String base = baseType.getRawClass().getName();
+        int ix = base.lastIndexOf('.');
+        if (ix < 0) { // can this ever occur?
+            _basePackageName = "";
+            _basePackagePrefix = ".";
+        } else {
+            _basePackagePrefix = base.substring(0, ix+1);
+            _basePackageName = base.substring(0, ix);
+        }
+    }
+
+    @Override
+    public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.MINIMAL_CLASS; }
+    
+    @Override
+    public String idFromValue(Object value)
+    {
+        String n = value.getClass().getName();
+        if (n.startsWith(_basePackagePrefix)) {
+            // note: we will leave the leading dot in there
+            return n.substring(_basePackagePrefix.length()-1);
+        }
+        return n;
+    }
+
+    @Override
+    public JavaType typeFromId(String id)
+    {
+        if (id.startsWith(".")) {
+            StringBuilder sb = new StringBuilder(id.length() + _basePackageName.length());
+            if  (_basePackageName.length() == 0) {
+                // no package; must remove leading '.' from id
+                sb.append(id.substring(1));
+            } else {
+                // otherwise just concatenate package, with leading-dot-partial name
+                sb.append(_basePackageName).append(id);
+            }
+            id = sb.toString();
+        }
+        return super.typeFromId(id);
+    }
+}
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
new file mode 100644
index 0000000..b1763ed
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdSubtypeResolver.java
@@ -0,0 +1,173 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.util.*;
+
+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.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.SubtypeResolver;
+
+/**
+ * Standard {@link SubtypeResolver} implementation.
+ */
+public class StdSubtypeResolver
+    extends SubtypeResolver
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected LinkedHashSet<NamedType> _registeredSubtypes;
+
+    public StdSubtypeResolver() { }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override    
+    public void registerSubtypes(NamedType... types)
+    {
+        if (_registeredSubtypes == null) {
+            _registeredSubtypes = new LinkedHashSet<NamedType>();
+        }
+        for (NamedType type : types) {
+            _registeredSubtypes.add(type);
+        }
+    }
+
+    @Override
+    public void registerSubtypes(Class<?>... classes)
+    {
+        NamedType[] types = new NamedType[classes.length];
+        for (int i = 0, len = classes.length; i < len; ++i) {
+            types[i] = new NamedType(classes[i]);
+        }
+        registerSubtypes(types);
+    }
+
+    /**
+     * @deprecated Since 2.1
+     */
+    @Deprecated
+    @Override
+    public Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
+        MapperConfig<?> config, AnnotationIntrospector ai)
+    {
+        return collectAndResolveSubtypes(property, config, ai, null);
+    }
+    
+    /**
+     * 
+     * @param property Base member to use for type resolution: either annotated type (class),
+     *    or property (field, getter/setter)
+     *    
+     * @since 2.1
+     */
+    @Override
+    public Collection<NamedType> collectAndResolveSubtypes(AnnotatedMember property,
+        MapperConfig<?> config, AnnotationIntrospector ai, JavaType baseType)
+    {
+        // for backwards compatibility, must allow null here:
+        Class<?> rawBase = (baseType == null) ? property.getRawType() : baseType.getRawClass();
+        
+        HashMap<NamedType, NamedType> collected = new HashMap<NamedType, NamedType>();
+        // start with registered subtypes (which have 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(), ai, config);
+                    _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(), ai, config);
+                _collectAndResolve(ac, nt, config, ai, collected);
+            }            
+        }
+        
+        NamedType rootType = new NamedType(rawBase, null);
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(rawBase, ai, config);
+            
+        // and finally subtypes via annotations from base type (recursively)
+        _collectAndResolve(ac, rootType, config, ai, collected);
+        return new ArrayList<NamedType>(collected.values());
+    }
+
+    @Override
+    public Collection<NamedType> collectAndResolveSubtypes(AnnotatedClass type,
+            MapperConfig<?> config, AnnotationIntrospector ai)
+    {
+        HashMap<NamedType, NamedType> subtypes = new HashMap<NamedType, NamedType>();
+        // [JACKSON-257] then consider registered subtypes (which have precedence over annotations)
+        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(), ai, config);
+                    _collectAndResolve(curr, subtype, config, ai, subtypes);
+                }
+            }
+        }
+        // and then check subtypes via annotations from base type (recursively)
+        NamedType rootType = new NamedType(type.getRawType(), null);
+        _collectAndResolve(type, rootType, config, ai, subtypes);
+        return new ArrayList<NamedType>(subtypes.values());
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to find subtypes for a specific type (class)
+     */
+    protected void _collectAndResolve(AnnotatedClass annotatedType, NamedType namedType,
+            MapperConfig<?> config, AnnotationIntrospector ai,
+            HashMap<NamedType, NamedType> collectedSubtypes)
+    {
+        if (!namedType.hasName()) {
+            String name = ai.findTypeName(annotatedType);
+            if (name != null) {
+                namedType = new NamedType(namedType.getType(), name);
+            }
+        }
+
+        // First things first: is base type itself included?
+        if (collectedSubtypes.containsKey(namedType)) {
+            // if so, no recursion; however, may need to update name?
+            if (namedType.hasName()) {
+                NamedType prev = collectedSubtypes.get(namedType);
+                if (!prev.hasName()) {
+                    collectedSubtypes.put(namedType, namedType);
+                }
+            }
+            return;
+        }
+        // if it wasn't, add and check subtypes recursively
+        collectedSubtypes.put(namedType, namedType);
+        Collection<NamedType> st = ai.findSubtypes(annotatedType);
+        if (st != null && !st.isEmpty()) {
+            for (NamedType subtype : st) {
+                AnnotatedClass subtypeClass = AnnotatedClass.constructWithoutSuperTypes(subtype.getType(), ai, config);
+                // One more thing: name may be either in reference, or in subtype:
+                if (!subtype.hasName()) {
+                    subtype = new NamedType(subtype.getType(), ai.findTypeName(subtypeClass));
+                }
+                _collectAndResolve(subtypeClass, subtype, config, ai, collectedSubtypes);
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..2b16989
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/StdTypeResolverBuilder.java
@@ -0,0 +1,215 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Default {@link TypeResolverBuilder} implementation.
+ *
+ * @author tatu
+ */
+public class StdTypeResolverBuilder
+    implements TypeResolverBuilder<StdTypeResolverBuilder>
+{
+    // Configuration settings:
+
+    protected JsonTypeInfo.Id _idType;
+
+    protected JsonTypeInfo.As _includeAs;
+
+    protected String _typeProperty;
+
+    /**
+     * Whether type id should be exposed to deserializers or not
+     */
+    protected boolean _typeIdVisible = false;
+    
+    /**
+     * Default class to use in case type information is not available
+     * or is broken.
+     */
+    protected Class<?> _defaultImpl;
+    
+    // Objects
+    
+    protected TypeIdResolver _customIdResolver;
+    
+    /*
+    /**********************************************************
+    /* Construction, initialization, actual building
+    /**********************************************************
+     */
+
+    public StdTypeResolverBuilder() { }
+
+    public static StdTypeResolverBuilder noTypeInfoBuilder() {
+        return new StdTypeResolverBuilder().init(JsonTypeInfo.Id.NONE, null);
+    }
+
+    @Override
+    public StdTypeResolverBuilder init(JsonTypeInfo.Id idType, TypeIdResolver idRes)
+    {
+        // sanity checks
+        if (idType == null) {
+            throw new IllegalArgumentException("idType can not be null");
+        }
+        _idType = idType;
+        _customIdResolver = idRes;
+        // Let's also initialize property name as per idType default
+        _typeProperty = idType.getDefaultPropertyName();
+        return this;
+    }
+
+    @Override
+    public TypeSerializer buildTypeSerializer(SerializationConfig config,
+            JavaType baseType, Collection<NamedType> subtypes)
+    {
+        if (_idType == JsonTypeInfo.Id.NONE) {
+            return null;
+        }
+        TypeIdResolver idRes = idResolver(config, baseType, subtypes, true, false);
+        switch (_includeAs) {
+        case WRAPPER_ARRAY:
+            return new AsArrayTypeSerializer(idRes, null);
+        case PROPERTY:
+            return new AsPropertyTypeSerializer(idRes, null,
+                    _typeProperty);
+        case WRAPPER_OBJECT:
+            return new AsWrapperTypeSerializer(idRes, null);
+        case EXTERNAL_PROPERTY:
+            return new AsExternalTypeSerializer(idRes, null,
+                    _typeProperty);
+        }
+        throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
+    }
+
+    @Override
+    public TypeDeserializer buildTypeDeserializer(DeserializationConfig config,
+            JavaType baseType, Collection<NamedType> subtypes)
+    {
+        if (_idType == JsonTypeInfo.Id.NONE) {
+            return null;
+        }
+
+        TypeIdResolver idRes = idResolver(config, baseType, subtypes, false, true);
+        
+        // First, method for converting type info to type id:
+        switch (_includeAs) {
+        case WRAPPER_ARRAY:
+            return new AsArrayTypeDeserializer(baseType, idRes,
+                    _typeProperty, _typeIdVisible, _defaultImpl);
+        case PROPERTY:
+            return new AsPropertyTypeDeserializer(baseType, idRes,
+                    _typeProperty, _typeIdVisible, _defaultImpl);
+        case WRAPPER_OBJECT:
+            return new AsWrapperTypeDeserializer(baseType, idRes,
+                    _typeProperty, _typeIdVisible, _defaultImpl);
+        case EXTERNAL_PROPERTY:
+            return new AsExternalTypeDeserializer(baseType, idRes,
+                    _typeProperty, _typeIdVisible, _defaultImpl);
+        }
+        throw new IllegalStateException("Do not know how to construct standard type serializer for inclusion type: "+_includeAs);
+    }
+
+    /*
+    /**********************************************************
+    /* Construction, configuration
+    /**********************************************************
+     */
+
+    @Override
+    public StdTypeResolverBuilder inclusion(JsonTypeInfo.As includeAs) {
+        if (includeAs == null) {
+            throw new IllegalArgumentException("includeAs can not be null");
+        }
+        _includeAs = includeAs;
+        return this;
+    }
+
+    /**
+     * Method for constructing an instance with specified type property name
+     * (property name to use for type id when using "as-property" inclusion).
+     */
+    @Override
+    public StdTypeResolverBuilder typeProperty(String typeIdPropName)
+    {
+        // ok to have null/empty; will restore to use defaults
+        if (typeIdPropName == null || typeIdPropName.length() == 0) {
+            typeIdPropName = _idType.getDefaultPropertyName();
+        }
+        _typeProperty = typeIdPropName;
+        return this;
+    }
+
+    @Override
+    public StdTypeResolverBuilder defaultImpl(Class<?> defaultImpl)
+    {
+        _defaultImpl = defaultImpl;
+        return this;
+    }
+
+    @Override
+    public StdTypeResolverBuilder typeIdVisibility(boolean isVisible) {
+        _typeIdVisible = isVisible;
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    public String getTypeProperty() { return _typeProperty; }
+
+    @Override
+    public Class<?> getDefaultImpl() {
+        return _defaultImpl;
+    }
+
+    public boolean isTypeIdVisible() { return _typeIdVisible; }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method that will either return configured custom
+     * type id resolver, or construct a standard resolver
+     * given configuration.
+     */
+    protected TypeIdResolver idResolver(MapperConfig<?> config,
+            JavaType baseType, Collection<NamedType> subtypes,
+            boolean forSer, boolean forDeser)
+    {
+        // Custom id resolver?
+        if (_customIdResolver != null) {
+            return _customIdResolver;
+        }
+        if (_idType == null) {
+            throw new IllegalStateException("Can not build, 'init()' not yet called");
+        }
+        switch (_idType) {
+        case CLASS:
+            return new ClassNameIdResolver(baseType, config.getTypeFactory());
+        case MINIMAL_CLASS:
+            return new MinimalClassNameIdResolver(baseType, config.getTypeFactory());
+        case NAME:
+            return TypeNameIdResolver.construct(config, baseType, subtypes, forSer, forDeser);
+        case NONE: // hmmh. should never get this far with 'none'
+            return null;
+        case CUSTOM: // need custom resolver...
+        }
+        throw new IllegalStateException("Do not know how to construct standard type id resolver for idType: "+_idType);
+    }
+}
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
new file mode 100644
index 0000000..30a84e6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeDeserializerBase.java
@@ -0,0 +1,206 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.*;
+
+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.annotation.NoClass;
+import com.fasterxml.jackson.databind.deser.std.NullifyingDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+
+/**
+ * Base class for all standard Jackson {@link TypeDeserializer}s.
+ */
+public abstract class TypeDeserializerBase
+    extends TypeDeserializer
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 278445030337366675L;
+    
+    protected final TypeIdResolver _idResolver;
+    
+    protected final JavaType _baseType;
+
+    /**
+     * Property that contains value for which type information
+     * is included; null if value is a root value.
+     * Note that this value is not assigned during construction
+     * but only when {@link #forProperty} is called to create
+     * a copy.
+     */
+    protected final BeanProperty _property;
+
+    /**
+     * Type to use as the default implementation, if type id is
+     * missing or can not be resolved.
+     */
+    protected final JavaType _defaultImpl;
+
+    /**
+     * Name of type property used; needed for non-property versions too,
+     * in cases where type id is to be exposed as part of JSON.
+     */
+    protected final String _typePropertyName;
+    
+    protected final boolean _typeIdVisible;
+    
+    /**
+     * For efficient operation we will lazily build mappings from type ids
+     * to actual deserializers, once needed.
+     */
+    protected final HashMap<String,JsonDeserializer<Object>> _deserializers;
+
+    protected JsonDeserializer<Object> _defaultImplDeserializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected TypeDeserializerBase(JavaType baseType, TypeIdResolver idRes,
+            String typePropertyName, boolean typeIdVisible, Class<?> defaultImpl)
+    {
+        _baseType = baseType;
+        _idResolver = idRes;
+        _typePropertyName = typePropertyName;
+        _typeIdVisible = typeIdVisible;
+        _deserializers = new HashMap<String,JsonDeserializer<Object>>();
+        if (defaultImpl == null) {
+            _defaultImpl = null;
+        } else {
+            /* 16-Oct-2011, tatu: should call this via TypeFactory; this is
+             *    not entirely safe... however, since Collections/Maps are
+             *    seldom (if ever) base types, may be ok.
+             */
+            _defaultImpl = baseType.forcedNarrowBy(defaultImpl);
+        }
+
+        _property = null;
+    }
+
+    protected TypeDeserializerBase(TypeDeserializerBase src, BeanProperty property)
+    {
+        _baseType = src._baseType;
+        _idResolver = src._idResolver;
+        _typePropertyName = src._typePropertyName;
+        _typeIdVisible = src._typeIdVisible;
+        _deserializers = src._deserializers;
+        _defaultImpl = src._defaultImpl;
+        _defaultImplDeserializer = src._defaultImplDeserializer;
+
+        _property = property;
+    }
+
+    @Override
+    public abstract TypeDeserializer forProperty(BeanProperty prop);
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    @Override
+    public abstract JsonTypeInfo.As getTypeInclusion();
+
+    public String baseTypeName() { return _baseType.getRawClass().getName(); }
+
+    @Override
+    public final String getPropertyName() { return _typePropertyName; }
+    
+    @Override    
+    public TypeIdResolver getTypeIdResolver() { return _idResolver; }
+
+    @Override    
+    public Class<?> getDefaultImpl() {
+        return (_defaultImpl == null) ? null : _defaultImpl.getRawClass();
+    }
+    
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append('[').append(getClass().getName());
+        sb.append("; base-type:").append(_baseType);
+        sb.append("; id-resolver: ").append(_idResolver);
+    	    sb.append(']');
+    	    return sb.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods for sub-classes
+    /**********************************************************
+     */
+
+    protected final JsonDeserializer<Object> _findDeserializer(DeserializationContext ctxt,
+            String typeId)
+        throws IOException, JsonProcessingException
+    {
+        JsonDeserializer<Object> deser;
+
+        synchronized (_deserializers) {
+            deser = _deserializers.get(typeId);
+            if (deser == null) {
+                JavaType type = _idResolver.typeFromId(typeId);
+                if (type == null) {
+                    // As per [JACKSON-614], use the default impl if no type id available:
+                    if (_defaultImpl == null) {
+                        throw ctxt.unknownTypeException(_baseType, typeId);
+                    }
+                    deser = _findDefaultImplDeserializer(ctxt);
+                } else {
+                    /* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters,
+                     *   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
+                     *   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)
+                     */
+                    if (_baseType != null && _baseType.getClass() == type.getClass()) {
+                        type = _baseType.narrowBy(type.getRawClass());
+                    }
+                    deser = ctxt.findContextualValueDeserializer(type, _property);
+                }
+                _deserializers.put(typeId, deser);
+            }
+        }
+        return deser;
+    }
+
+    protected final JsonDeserializer<Object> _findDefaultImplDeserializer(DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        /* 06-Feb-2013, tatu: As per [Issue#148], consider default implementation value of
+         *   {@link NoClass} to mean "serialize as null"; as well as DeserializationFeature
+         *   to do swift mapping to null
+         */
+        if (_defaultImpl == null) {
+            if (!ctxt.isEnabled(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE)) {
+                return NullifyingDeserializer.instance;
+            }
+            return null;
+        }
+        if (_defaultImpl.getRawClass() == NoClass.class) {
+            return NullifyingDeserializer.instance;
+        }
+        
+        synchronized (_defaultImpl) {
+            if (_defaultImplDeserializer == null) {
+                _defaultImplDeserializer = ctxt.findContextualValueDeserializer(
+                        _defaultImpl, _property);
+            }
+            return _defaultImplDeserializer;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java
new file mode 100644
index 0000000..0f8bce2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeIdResolverBase.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public abstract class TypeIdResolverBase
+    implements TypeIdResolver
+{
+    protected final TypeFactory _typeFactory;
+
+    /**
+     * Common base type for all polymorphic instances handled.
+     */
+    protected final JavaType _baseType;
+
+    protected TypeIdResolverBase(JavaType baseType, TypeFactory typeFactory)
+    {
+        _baseType = baseType;
+        _typeFactory = typeFactory;
+    }
+
+    @Override
+    public void init(JavaType bt) {
+        // Standard type id resolvers do not need this: only useful for custom ones.
+    }
+
+    @Override
+    public String idFromBaseType()
+    {
+        /* By default we will just defer to regular handling, handing out the
+         * base type; and since there is no value, must just pass null here
+         * assuming that implementations can deal with it.
+         * Alternative would be to pass a bogus Object, but that does not seem right.
+         */
+        return idFromValueAndType(null, _baseType.getRawClass());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java
new file mode 100644
index 0000000..0de35d1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeNameIdResolver.java
@@ -0,0 +1,158 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+
+public class TypeNameIdResolver
+    extends TypeIdResolverBase
+{
+    protected final MapperConfig<?> _config;
+    
+    /**
+     * Mappings from class name to type id, used for serialization
+     */
+    protected final HashMap<String, String> _typeToId;
+
+    /**
+     * Mappings from type id to JavaType, used for deserialization
+     */
+    protected final HashMap<String, JavaType> _idToType;
+    
+    protected TypeNameIdResolver(MapperConfig<?> config, JavaType baseType,
+            HashMap<String, String> typeToId, HashMap<String, JavaType> idToType)
+    {
+        super(baseType, config.getTypeFactory());
+        _config = config;
+        _typeToId = typeToId;
+        _idToType = idToType;
+    }
+ 
+    public static TypeNameIdResolver construct(MapperConfig<?> config,
+            JavaType baseType,
+            Collection<NamedType> subtypes, boolean forSer, boolean forDeser)
+    {
+        // sanity check
+        if (forSer == forDeser) throw new IllegalArgumentException();
+        HashMap<String, String> typeToId = null;
+        HashMap<String, JavaType> idToType = null;
+
+        if (forSer) {
+            typeToId = new HashMap<String, String>();
+        }
+        if (forDeser) {
+            idToType = new HashMap<String, JavaType>();
+        }
+        if (subtypes != null) {
+            for (NamedType t : subtypes) {
+                /* no name? Need to figure out default; for now, let's just
+                 * use non-qualified class name
+                 */
+                Class<?> cls = t.getType();
+                String id = t.hasName() ? t.getName() : _defaultTypeId(cls);
+                if (forSer) {
+                    typeToId.put(cls.getName(), id);
+                }
+                if (forDeser) {
+                    /* 24-Feb-2011, tatu: [JACKSON-498] One more problem; sometimes
+                     *   we have same name for multiple types; if so, use most specific
+                     *   one.
+                     */
+                    JavaType prev = idToType.get(id);
+                    if (prev != null) { // Can only override if more specific
+                        if (cls.isAssignableFrom(prev.getRawClass())) { // nope, more generic (or same)
+                            continue;
+                        }
+                    }
+                    idToType.put(id, config.constructType(cls));
+                }
+            }
+        }
+        return new TypeNameIdResolver(config, baseType, typeToId, idToType);
+    }
+
+    @Override
+    public JsonTypeInfo.Id getMechanism() { return JsonTypeInfo.Id.NAME; }
+
+    @Override
+    public String idFromValue(Object value)
+    {
+        Class<?> cls = value.getClass();
+        final String key = cls.getName();
+        String name;
+        synchronized (_typeToId) {
+            name = _typeToId.get(key);
+            if (name == null) {
+                // 24-Feb-2011, tatu: As per [JACKSON-498], may need to dynamically look up name
+                // can either throw an exception, or use default name...
+                if (_config.isAnnotationProcessingEnabled()) {
+                    BeanDescription beanDesc = _config.introspectClassAnnotations(cls);
+                    name = _config.getAnnotationIntrospector().findTypeName(beanDesc.getClassInfo());
+                }
+                if (name == null) {
+                    // And if still not found, let's choose default?
+                    name = _defaultTypeId(cls);
+                }
+                _typeToId.put(key, name);
+            }
+        }
+        return name;
+    }
+
+    @Override
+    public String idFromValueAndType(Object value, Class<?> type)
+    {
+        /* 18-Jan-2013, tatu: We may be called with null value occasionally
+         *   it seems; nothing much we can figure out that way.
+         */
+        if (value == null) {
+            return null;
+        }
+        return idFromValue(value);
+    }
+    
+    @Override
+    public JavaType typeFromId(String id)
+        throws IllegalArgumentException
+    {
+        JavaType t = _idToType.get(id);
+        /* Now: if no type is found, should we try to locate it by
+         * some other means? (specifically, if in same package as base type,
+         * could just try Class.forName)
+         * For now let's not add any such workarounds; can add if need be
+         */
+        return t;
+    }    
+
+    @Override
+    public String toString()
+    {
+    	StringBuilder sb = new StringBuilder();
+    	sb.append('[').append(getClass().getName());
+    	sb.append("; id-to-type=").append(_idToType);
+    	sb.append(']');
+    	return sb.toString();
+    }
+    
+    /*
+    /*********************************************************
+    /* Helper methods
+    /*********************************************************
+     */
+    
+    /**
+     * If no name was explicitly given for a class, we will just
+     * use non-qualified class name
+     */
+    protected static String _defaultTypeId(Class<?> cls)
+    {
+        String n = cls.getName();
+        int ix = n.lastIndexOf('.');
+        return (ix < 0) ? n : n.substring(ix+1);
+    }
+}
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
new file mode 100644
index 0000000..851a924
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/TypeSerializerBase.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.databind.jsontype.impl;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+public abstract class TypeSerializerBase extends TypeSerializer
+{
+    protected final TypeIdResolver _idResolver;
+
+    protected final BeanProperty _property;
+    
+    protected TypeSerializerBase(TypeIdResolver idRes, BeanProperty property)
+    {
+        _idResolver = idRes;
+        _property = property;
+    }
+
+    @Override
+    public abstract JsonTypeInfo.As getTypeInclusion();
+
+    @Override
+    public String getPropertyName() { return null; }
+    
+    @Override
+    public TypeIdResolver getTypeIdResolver() { return _idResolver; }
+
+    /*
+    /**********************************************************
+    /* Helper methods for subclasses
+    /**********************************************************
+     */
+
+    protected String idFromValue(Object value) {
+        return _idResolver.idFromValue(value);
+    }
+
+    protected String idFromValueAndType(Object value, Class<?> type) {
+        return _idResolver.idFromValueAndType(value, type);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/package-info.java b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/package-info.java
new file mode 100644
index 0000000..8bb399d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Package that contains standard implementations for
+ * {@link com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder}
+ * and
+ * {@link com.fasterxml.jackson.databind.jsontype.TypeIdResolver}.
+ */
+package com.fasterxml.jackson.databind.jsontype.impl;
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
new file mode 100644
index 0000000..fd931b0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/jsontype/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * 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
+ * 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
new file mode 100644
index 0000000..7e6c901
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleAbstractTypeResolver.java
@@ -0,0 +1,89 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.AbstractTypeResolver;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.ClassKey;
+
+/**
+ * Simple {@link AbstractTypeResolver} implementation, which is
+ * based on static mapping from abstract super types into
+ * sub types (concrete or abstract), but retaining generic
+ * parameterization.
+ * Can be used for things like specifying which implementation of
+ * {@link java.util.Collection} to use:
+ *<pre>
+ *  SimpleAbstractTypeResolver resolver = new SimpleAbstractTypeResolver();
+ *  // To make all properties declared as Collection, List, to LinkedList
+ *  resolver.addMapping(Collection.class, LinkedList.class);
+ *  resolver.addMapping(List.class, LinkedList.class);
+ *</pre>
+ * Can also be used as an alternative to per-class annotations when defining
+ * concrete implementations; however, only works with abstract types (since
+ * this is only called for abstract types)
+ */
+public class SimpleAbstractTypeResolver
+    extends AbstractTypeResolver
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 8635483102371490919L;
+
+    /**
+     * Mappings from super types to subtypes
+     */
+    protected final HashMap<ClassKey,Class<?>> _mappings = new HashMap<ClassKey,Class<?>>();
+
+    /**
+     * Method for adding a mapping from super type to specific subtype.
+     * Arguments will be checked by method, to ensure that <code>superType</code>
+     * is abstract (since resolver is never called for concrete classes);
+     * as well as to ensure that there is supertype/subtype relationship
+     * (to ensure there won't be cycles during resolution).
+     * 
+     * @param superType Abstract type to resolve
+     * @param subType Sub-class of superType, to map superTo to
+     * 
+     * @return This resolver, to allow chaining of initializations
+     */
+    public <T> SimpleAbstractTypeResolver addMapping(Class<T> superType, Class<? extends T> subType)
+    {
+        // Sanity checks, just in case someone tries to force typing...
+        if (superType == subType) {
+            throw new IllegalArgumentException("Can not add mapping from class to itself");
+        }
+        if (!superType.isAssignableFrom(subType)) {
+            throw new IllegalArgumentException("Can not 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()
+                    +" since it is not abstract");
+        }
+        _mappings.put(new ClassKey(superType), subType);
+        return this;
+    }
+
+    @Override
+    public JavaType findTypeMapping(DeserializationConfig config, JavaType type)
+    {
+        // this is the main mapping base, so let's 
+        Class<?> src = type.getRawClass();
+        Class<?> dst = _mappings.get(new ClassKey(src));
+        if (dst == null) {
+            return null;
+        }
+        return type.narrowBy(dst);
+    }
+
+    
+    @Override
+    public JavaType resolveAbstractType(DeserializationConfig config, JavaType type)
+    {
+        // never materialize anything, so:
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java
new file mode 100644
index 0000000..41676be
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleDeserializers.java
@@ -0,0 +1,145 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.Deserializers;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.*;
+
+/**
+ * Simple implementation {@link Deserializers} which allows registration of
+ * deserializers based on raw (type erased class).
+ * It can work well for basic bean and scalar type deserializers, but is not
+ * a good fit for handling generic types (like {@link Map}s and {@link Collection}s
+ * or array types).
+ *<p>
+ * Unlike {@link SimpleSerializers}, this class does not currently support generic mappings;
+ * all mappings must be to exact declared deserialization type.
+ */
+public class SimpleDeserializers
+   implements Deserializers, java.io.Serializable
+{
+    private static final long serialVersionUID = -3006673354353448880L;
+
+    protected HashMap<ClassKey,JsonDeserializer<?>> _classMappings = null;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction and configuring
+    /**********************************************************
+     */
+    
+    public SimpleDeserializers() { }
+
+    /**
+     * @since 2.1
+     */
+    public SimpleDeserializers(Map<Class<?>,JsonDeserializer<?>> desers) {
+        addDeserializers(desers);
+    }
+    
+    public <T> void addDeserializer(Class<T> forClass, JsonDeserializer<? extends T> deser)
+    {
+        ClassKey key = new ClassKey(forClass);
+        if (_classMappings == null) {
+            _classMappings = new HashMap<ClassKey,JsonDeserializer<?>>();
+        }
+        _classMappings.put(key, deser);
+    }
+
+    /**
+     * @since 2.1
+     */
+    @SuppressWarnings("unchecked")
+    public void addDeserializers(Map<Class<?>,JsonDeserializer<?>> desers)
+    {
+        for (Map.Entry<Class<?>,JsonDeserializer<?>> entry : desers.entrySet()) {
+            Class<?> cls = entry.getKey();
+            // what a mess... nominal generics safety...
+            JsonDeserializer<Object> deser = (JsonDeserializer<Object>) entry.getValue();
+            addDeserializer((Class<Object>) cls, deser);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Serializers implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonDeserializer<?> findArrayDeserializer(ArrayType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+
+    @Override
+    public JsonDeserializer<?> findBeanDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+
+    @Override
+    public JsonDeserializer<?> findCollectionDeserializer(CollectionType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer,
+            JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+
+    @Override
+    public JsonDeserializer<?> findCollectionLikeDeserializer(CollectionLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer,
+            JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+    
+    @Override
+    public JsonDeserializer<?> findEnumDeserializer(Class<?> type,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type));
+    }
+
+    @Override
+    public JsonDeserializer<?> findMapDeserializer(MapType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer,
+            JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+
+    @Override
+    public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer,
+            JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(type.getRawClass()));
+    }
+    
+    @Override
+    public JsonDeserializer<?> findTreeNodeDeserializer(Class<? extends JsonNode> nodeType,
+            DeserializationConfig config, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        return (_classMappings == null) ? null : _classMappings.get(new ClassKey(nodeType));
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java
new file mode 100644
index 0000000..9e660c7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleKeyDeserializers.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.KeyDeserializers;
+import com.fasterxml.jackson.databind.type.ClassKey;
+
+/**
+ * Simple implementation {@link KeyDeserializers} which allows registration of
+ * deserializers based on raw (type erased class).
+ * It can work well for basic bean and scalar type deserializers, but is not
+ * a good fit for handling generic types (like {@link Map}s and {@link Collection}s
+ * or array types).
+ *<p>
+ * Unlike {@link SimpleSerializers}, this class does not currently support generic mappings;
+ * all mappings must be to exact declared deserialization type.
+ */
+public class SimpleKeyDeserializers
+    implements KeyDeserializers, java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = -6786398737835438187L;
+
+    protected HashMap<ClassKey,KeyDeserializer> _classMappings = null;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction and configuring
+    /**********************************************************
+     */
+    
+    public SimpleKeyDeserializers() { }
+
+    public SimpleKeyDeserializers addDeserializer(Class<?> forClass, KeyDeserializer deser)
+    {
+        if (_classMappings == null) {
+            _classMappings = new HashMap<ClassKey,KeyDeserializer>();
+        }
+        _classMappings.put(new ClassKey(forClass), deser);
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Serializers implementation
+    /**********************************************************
+     */
+
+    @Override
+    public KeyDeserializer findKeyDeserializer(JavaType type,
+            DeserializationConfig config, BeanDescription beanDesc)
+    {
+        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
new file mode 100644
index 0000000..dffa33d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleModule.java
@@ -0,0 +1,400 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.Version;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+
+/**
+ * Simple {@link Module} implementation that allows registration
+ * of serializers and deserializers, and bean serializer
+ * and deserializer modifiers.
+ */
+public class SimpleModule
+    extends Module
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 3132264350026957446L;
+
+    protected final String _name;
+    protected final Version _version;
+    
+    protected SimpleSerializers _serializers = null;
+    protected SimpleDeserializers _deserializers = null;
+
+    protected SimpleSerializers _keySerializers = null;
+    protected SimpleKeyDeserializers _keyDeserializers = null;
+
+    /**
+     * Lazily-constructed resolver used for storing mappings from
+     * abstract classes to more specific implementing classes
+     * (which may be abstract or concrete)
+     */
+    protected SimpleAbstractTypeResolver _abstractTypes = null;
+
+    /**
+     * Lazily-constructed resolver used for storing mappings from
+     * abstract classes to more specific implementing classes
+     * (which may be abstract or concrete)
+     */
+    protected SimpleValueInstantiators _valueInstantiators = null;
+
+    /**
+     * @since 2.2
+     */
+    protected BeanDeserializerModifier _deserializerModifier = null;
+
+    /**
+     * @since 2.2
+     */
+    protected BeanSerializerModifier _serializerModifier = null;
+
+    /**
+     * Lazily-constructed map that contains mix-in definitions, indexed
+     * by target class, value being mix-in to apply.
+     */
+    protected HashMap<Class<?>, Class<?>> _mixins = null;
+    
+    /**
+     * Set of subtypes to register, if any.
+     */
+    protected LinkedHashSet<NamedType> _subtypes = null;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: creation
+    /**********************************************************
+     */
+
+    /**
+     * Constructors that should only be used for non-reusable
+     * convenience modules used by app code: "real" modules should
+     * use actual name and version number information.
+     */
+    public SimpleModule() {
+        // when passing 'this', can not chain constructors...
+        _name = "SimpleModule-"+System.identityHashCode(this);
+        _version = Version.unknownVersion();
+    }
+    
+    /**
+     * Convenience constructor that will default version to
+     * {@link Version#unknownVersion()}.
+     */
+    public SimpleModule(String name) {
+        this(name, Version.unknownVersion());
+    }
+
+    /**
+     * Convenience constructor that will use specified Version,
+     * including name from {@link Version#getArtifactId()}
+     */
+    public SimpleModule(Version version) {
+        _name = version.getArtifactId();
+        _version = version;
+    }
+    
+    /**
+     * Constructor to use for actual reusable modules.
+     * ObjectMapper may use name as identifier to notice attempts
+     * for multiple registrations of the same module (although it
+     * does not have to).
+     * 
+     * @param name Unique name of the module
+     * @param version Version of the module
+     */
+    public SimpleModule(String name, Version version) {
+        _name = name;
+        _version = version;
+    }
+
+    /**
+     * @since 2.1
+     */
+    public SimpleModule(String name, Version version,
+            Map<Class<?>,JsonDeserializer<?>> deserializers) {
+        this(name, version, deserializers, null);
+    }
+
+    /**
+     * @since 2.1
+     */
+    public SimpleModule(String name, Version version,
+            List<JsonSerializer<?>> serializers) {
+        this(name, version, null, serializers);
+    }
+    
+    /**
+     * @since 2.1
+     */
+    public SimpleModule(String name, Version version,
+            Map<Class<?>,JsonDeserializer<?>> deserializers,
+            List<JsonSerializer<?>> serializers)
+    {
+        _name = name;
+        _version = version;
+        if (deserializers != null) {
+            _deserializers = new SimpleDeserializers(deserializers);
+        }
+        if (serializers != null) {
+            _serializers = new SimpleSerializers(serializers);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Simple setters to allow overriding
+    /**********************************************************
+     */
+
+    /**
+     * Resets all currently configured serializers.
+     */
+    public void setSerializers(SimpleSerializers s) {
+        _serializers = s;
+    }
+
+    /**
+     * Resets all currently configured deserializers.
+     */
+    public void setDeserializers(SimpleDeserializers d) {
+        _deserializers = d;
+    }
+
+    /**
+     * Resets all currently configured key serializers.
+     */
+    public void setKeySerializers(SimpleSerializers ks) {
+        _keySerializers = ks;
+    }
+
+    /**
+     * Resets all currently configured key deserializers.
+     */
+    public void setKeyDeserializers(SimpleKeyDeserializers kd) {
+        _keyDeserializers = kd;
+    }
+
+    /**
+     * Resets currently configured abstract type mappings
+     */
+    public void setAbstractTypes(SimpleAbstractTypeResolver atr) {
+        _abstractTypes = atr;        
+    }
+
+    /**
+     * Resets all currently configured value instantiators
+     */
+    public void setValueInstantiators(SimpleValueInstantiators svi) {
+        _valueInstantiators = svi;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public SimpleModule setDeserializerModifier(BeanDeserializerModifier mod) {
+        _deserializerModifier = mod;
+        return this;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public SimpleModule setSerializerModifier(BeanSerializerModifier mod) {
+        _serializerModifier = mod;
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration methods
+    /**********************************************************
+     */
+    
+    public SimpleModule addSerializer(JsonSerializer<?> ser)
+    {
+        if (_serializers == null) {
+            _serializers = new SimpleSerializers();
+        }
+        _serializers.addSerializer(ser);
+        return this;
+    }
+    
+    public <T> SimpleModule addSerializer(Class<? extends T> type, JsonSerializer<T> ser)
+    {
+        if (_serializers == null) {
+            _serializers = new SimpleSerializers();
+        }
+        _serializers.addSerializer(type, ser);
+        return this;
+    }
+
+    public <T> SimpleModule addKeySerializer(Class<? extends T> type, JsonSerializer<T> ser)
+    {
+        if (_keySerializers == null) {
+            _keySerializers = new SimpleSerializers();
+        }
+        _keySerializers.addSerializer(type, ser);
+        return this;
+    }
+    
+    public <T> SimpleModule addDeserializer(Class<T> type, JsonDeserializer<? extends T> deser)
+    {
+        if (_deserializers == null) {
+            _deserializers = new SimpleDeserializers();
+        }
+        _deserializers.addDeserializer(type, deser);
+        return this;
+    }
+
+    public SimpleModule addKeyDeserializer(Class<?> type, KeyDeserializer deser)
+    {
+        if (_keyDeserializers == null) {
+            _keyDeserializers = new SimpleKeyDeserializers();
+        }
+        _keyDeserializers.addDeserializer(type, deser);
+        return this;
+    }
+
+    /**
+     * Lazily-constructed resolver used for storing mappings from
+     * abstract classes to more specific implementing classes
+     * (which may be abstract or concrete)
+     */
+    public <T> SimpleModule addAbstractTypeMapping(Class<T> superType,
+            Class<? extends T> subType)
+    {
+        if (_abstractTypes == null) {
+            _abstractTypes = new SimpleAbstractTypeResolver();
+        }
+        // note: addMapping() will verify arguments
+        _abstractTypes = _abstractTypes.addMapping(superType, subType);
+        return this;
+    }
+
+    /**
+     * 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.
+     */
+    public SimpleModule registerSubtypes(Class<?> ... subtypes)
+    {
+        if (_subtypes == null) {
+            _subtypes = new LinkedHashSet<NamedType>(Math.max(16, subtypes.length));
+        }
+        for (Class<?> subtype : subtypes) {
+            _subtypes.add(new NamedType(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.
+     */
+    public SimpleModule registerSubtypes(NamedType ... subtypes)
+    {
+        if (_subtypes == null) {
+            _subtypes = new LinkedHashSet<NamedType>(Math.max(16, subtypes.length));
+        }
+        for (NamedType subtype : subtypes) {
+            _subtypes.add(subtype);
+        }
+        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!).
+     *<p>
+     * Mix-in annotations are
+     * registered when module is registered for <code>ObjectMapper</code>.
+     */
+    public SimpleModule setMixInAnnotation(Class<?> targetType, Class<?> mixinClass)
+    {
+        if (_mixins == null) {
+            _mixins = new HashMap<Class<?>, Class<?>>();
+        }
+        _mixins.put(targetType, mixinClass);
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Module impl
+    /**********************************************************
+     */
+    
+    @Override
+    public String getModuleName() {
+        return _name;
+    }
+
+    @Override
+    public void setupModule(SetupContext context)
+    {
+        if (_serializers != null) {
+            context.addSerializers(_serializers);
+        }
+        if (_deserializers != null) {
+            context.addDeserializers(_deserializers);
+        }
+        if (_keySerializers != null) {
+            context.addKeySerializers(_keySerializers);
+        }
+        if (_keyDeserializers != null) {
+            context.addKeyDeserializers(_keyDeserializers);
+        }
+        if (_abstractTypes != null) {
+            context.addAbstractTypeResolver(_abstractTypes);
+        }
+        if (_valueInstantiators != null) {
+            context.addValueInstantiators(_valueInstantiators);
+        }
+        if (_deserializerModifier != null) {
+            context.addBeanDeserializerModifier(_deserializerModifier);
+        }
+        if (_serializerModifier != null) {
+            context.addBeanSerializerModifier(_serializerModifier);
+        }
+        if (_subtypes != null && _subtypes.size() > 0) {
+            context.registerSubtypes(_subtypes.toArray(new NamedType[_subtypes.size()]));
+        }
+        if (_mixins != null) {
+            for (Map.Entry<Class<?>,Class<?>> entry : _mixins.entrySet()) {
+                context.setMixInAnnotations(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+    @Override
+    public Version version() {
+        return _version;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java
new file mode 100644
index 0000000..a76bfd6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleSerializers.java
@@ -0,0 +1,226 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.ClassKey;
+import com.fasterxml.jackson.databind.type.CollectionLikeType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.MapType;
+
+/**
+ * Simple implementation {@link Serializers} which allows registration of
+ * serializers based on raw (type erased class).
+ * It can work well for basic bean and scalar type serializers, but is not
+ * a good fit for handling generic types (like {@link Map}s and {@link Collection}s).
+ *<p>
+ * Type registrations are assumed to be general; meaning that registration of serializer
+ * for a super type will also be used for handling subtypes, unless an exact match
+ * is found first. As an example, handler for {@link CharSequence} would also be used
+ * serializing {@link StringBuilder} instances, unless a direct mapping was found.
+ */
+public class SimpleSerializers
+    extends Serializers.Base
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 8531646511998456779L;
+
+    /**
+     * Class-based mappings that are used both for exact and
+     * sub-class matches.
+     */
+    protected HashMap<ClassKey,JsonSerializer<?>> _classMappings = null;
+
+    /**
+     * Interface-based matches.
+     */
+    protected HashMap<ClassKey,JsonSerializer<?>> _interfaceMappings = null;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction and configuring
+    /**********************************************************
+     */
+    
+    public SimpleSerializers() { }
+
+    /**
+     * @since 2.1
+     */
+    public SimpleSerializers(List<JsonSerializer<?>> sers) {
+        addSerializers(sers);
+    }
+    
+    /**
+     * Method for adding given serializer for type that {@link JsonSerializer#handledType}
+     * specifies (which MUST return a non-null class; and can NOT be {@link Object}, as a
+     * sanity check).
+     * For serializers that do not declare handled type, use the variant that takes
+     * two arguments.
+     * 
+     * @param ser
+     */
+    public void addSerializer(JsonSerializer<?> ser)
+    {
+        // Interface to match?
+        Class<?> cls = ser.handledType();
+        if (cls == null || cls == Object.class) {
+            throw new IllegalArgumentException("JsonSerializer of type "+ser.getClass().getName()
+                    +" does not define valid handledType() -- must either register with method that takes type argument "
+                    +" or make serializer extend 'com.fasterxml.jackson.databind.ser.std.StdSerializer'"); 
+        }
+        _addSerializer(cls, ser);
+    }
+
+    public <T> void addSerializer(Class<? extends T> type, JsonSerializer<T> ser)
+    {
+        _addSerializer(type, ser);
+    }
+
+    /**
+     * @since 2.1
+     */
+    public void addSerializers(List<JsonSerializer<?>> sers) {
+        for (JsonSerializer<?> ser : sers) {
+            addSerializer(ser);
+        }
+    }
+
+    private void _addSerializer(Class<?> cls, JsonSerializer<?> ser)
+    {
+        ClassKey key = new ClassKey(cls);
+        // Interface or class type?
+        if (cls.isInterface()) {
+            if (_interfaceMappings == null) {
+                _interfaceMappings = new HashMap<ClassKey,JsonSerializer<?>>();
+            }
+            _interfaceMappings.put(key, ser);
+        } else { // nope, class:
+            if (_classMappings == null) {
+                _classMappings = new HashMap<ClassKey,JsonSerializer<?>>();
+            }
+            _classMappings.put(key, ser);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Serializers implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonSerializer<?> findSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc)
+    {
+        Class<?> cls = type.getRawClass();
+        ClassKey key = new ClassKey(cls);
+        JsonSerializer<?> ser = null;
+
+        // First: direct match?
+        if (cls.isInterface()) {
+            if (_interfaceMappings != null) {
+                ser = _interfaceMappings.get(key);
+                if (ser != null) {
+                    return ser;
+                }
+            }
+        } else {
+            if (_classMappings != null) {
+                ser = _classMappings.get(key);
+                if (ser != null) {
+                    return ser;
+                }
+                // If not direct match, maybe super-class match?
+                for (Class<?> curr = cls; (curr != null); curr = curr.getSuperclass()) {
+                    key.reset(curr);
+                    ser = _classMappings.get(key);
+                    if (ser != null) {
+                        return ser;
+                    }
+                }
+            }
+        }
+        // No direct match? How about super-interfaces?
+        if (_interfaceMappings != null) {
+            ser = _findInterfaceMapping(cls, key);
+            if (ser != null) {
+                return ser;
+            }
+            // still no matches? Maybe interfaces of super classes
+            if (!cls.isInterface()) {
+                while ((cls = cls.getSuperclass()) != null) {
+                    ser = _findInterfaceMapping(cls, key);
+                    if (ser != null) {
+                        return ser;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public JsonSerializer<?> findArraySerializer(SerializationConfig config,
+            ArrayType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) {
+        return findSerializer(config, type, beanDesc);
+    }
+
+    @Override
+    public JsonSerializer<?> findCollectionSerializer(SerializationConfig config,
+            CollectionType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) {
+        return findSerializer(config, type, beanDesc);
+    }
+
+    @Override
+    public JsonSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
+            CollectionLikeType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) {
+        return findSerializer(config, type, beanDesc);
+    }
+        
+    @Override
+    public JsonSerializer<?> findMapSerializer(SerializationConfig config,
+            MapType type, BeanDescription beanDesc,
+            JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) {
+        return findSerializer(config, type, beanDesc);
+    }
+
+    @Override
+    public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
+            MapLikeType type, BeanDescription beanDesc,
+            JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) {
+        return findSerializer(config, type, beanDesc);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    protected JsonSerializer<?> _findInterfaceMapping(Class<?> cls, ClassKey key)
+    {
+        for (Class<?> iface : cls.getInterfaces()) {
+            key.reset(iface);
+            JsonSerializer<?> ser = _interfaceMappings.get(key);
+            if (ser != null) {
+                return ser;
+            }
+            ser = _findInterfaceMapping(iface, key);
+            if (ser != null) {
+                return ser;
+            }
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java b/src/main/java/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java
new file mode 100644
index 0000000..00af428
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/SimpleValueInstantiators.java
@@ -0,0 +1,48 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.HashMap;
+
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.DeserializationConfig;
+import com.fasterxml.jackson.databind.deser.ValueInstantiator;
+import com.fasterxml.jackson.databind.deser.ValueInstantiators;
+import com.fasterxml.jackson.databind.type.ClassKey;
+
+public class SimpleValueInstantiators
+    extends ValueInstantiators.Base
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = -8929386427526115130L;
+
+    /**
+     * Mappings from raw (type-erased, i.e. non-generic) types
+     * to matching {@link ValueInstantiator} instances.
+     */
+    protected HashMap<ClassKey,ValueInstantiator> _classMappings;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction and configuring
+    /**********************************************************
+     */
+
+    public SimpleValueInstantiators()
+    {
+        _classMappings = new HashMap<ClassKey,ValueInstantiator>();        
+    }
+    
+    public SimpleValueInstantiators addValueInstantiator(Class<?> forType,
+            ValueInstantiator inst)
+    {
+        _classMappings.put(new ClassKey(forType), inst);
+        return this;
+    }
+    
+    @Override
+    public ValueInstantiator findValueInstantiator(DeserializationConfig config,
+            BeanDescription beanDesc, ValueInstantiator defaultInstantiator)
+    {
+        ValueInstantiator inst = _classMappings.get(new ClassKey(beanDesc.getBeanClass()));
+        return (inst == null) ? defaultInstantiator : inst;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/module/package-info.java b/src/main/java/com/fasterxml/jackson/databind/module/package-info.java
new file mode 100644
index 0000000..75cf295
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/module/package-info.java
@@ -0,0 +1,14 @@
+/**
+ * Package that contains classes and interfaces to help implement
+ * custom extension {@link com.fasterxml.jackson.databind.Module}s
+ * (which are registered using
+ * {@link com.fasterxml.jackson.databind.ObjectMapper#registerModule}.
+ *<p>
+ * Note that classes in the package only support registering
+ * handlers for non-generic types (types without type
+ * parameterization) -- hence "simple" -- which works for
+ * many cases, but not all. So if you will need to register
+ * handlers for generic types, you will usually need to either
+ * sub-class handlers, or implement/extend base types directly.
+ */
+package com.fasterxml.jackson.databind.module;
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
new file mode 100644
index 0000000..4972dd2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
@@ -0,0 +1,774 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Node class that represents Arrays mapped from Json content.
+ */
+public final class ArrayNode
+    extends ContainerNode<ArrayNode>
+{
+    private final List<JsonNode> _children = new ArrayList<JsonNode>();
+
+    public ArrayNode(JsonNodeFactory nc) { super(nc); }
+
+    // note: co-variant to allow caller-side type safety
+    @SuppressWarnings("unchecked")
+    @Override
+    public ArrayNode deepCopy()
+    {
+        ArrayNode ret = new ArrayNode(_nodeFactory);
+
+        for (JsonNode element: _children)
+            ret._children.add(element.deepCopy());
+
+        return ret;
+    }
+
+    /*
+    /**********************************************************
+    /* Implementation of core JsonNode API
+    /**********************************************************
+     */
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.ARRAY;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.START_ARRAY; }
+
+    @Override
+    public int size()
+    {
+        return _children.size();
+    }
+
+    @Override
+    public Iterator<JsonNode> elements()
+    {
+        return _children.iterator();
+    }
+
+    @Override
+    public JsonNode get(int index)
+    {
+        if (index >= 0 && index < _children.size()) {
+            return _children.get(index);
+        }
+        return null;
+    }
+
+    @Override
+    public JsonNode get(String fieldName) { return null; }
+
+    @Override
+    public JsonNode path(String fieldName) { return MissingNode.getInstance(); }
+
+    @Override
+    public JsonNode path(int index)
+    {
+        if (index >= 0 && index < _children.size()) {
+            return _children.get(index);
+        }
+        return MissingNode.getInstance();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, serialization
+    /**********************************************************
+     */
+
+    @Override
+    public void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeStartArray();
+        for (JsonNode n : _children) {
+            /* 17-Feb-2009, tatu: Can we trust that all nodes will always
+             *   extend BaseJsonNode? Or if not, at least implement
+             *   JsonSerializable? Let's start with former, change if
+             *   we must.
+             */
+            ((BaseJsonNode)n).serialize(jg, provider);
+        }
+        jg.writeEndArray();
+    }
+
+    @Override
+    public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        typeSer.writeTypePrefixForArray(this, jg);
+        for (JsonNode n : _children) {
+            ((BaseJsonNode)n).serialize(jg, provider);
+        }
+        typeSer.writeTypeSuffixForArray(this, jg);
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, finding value nodes
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonNode findValue(String fieldName)
+    {
+        for (JsonNode node : _children) {
+            JsonNode value = node.findValue(fieldName);
+            if (value != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar)
+    {
+        for (JsonNode node : _children) {
+            foundSoFar = node.findValues(fieldName, foundSoFar);
+        }
+        return foundSoFar;
+    }
+
+    @Override
+    public List<String> findValuesAsText(String fieldName, List<String> foundSoFar)
+    {
+        for (JsonNode node : _children) {
+            foundSoFar = node.findValuesAsText(fieldName, foundSoFar);
+        }
+        return foundSoFar;
+    }
+    
+    @Override
+    public ObjectNode findParent(String fieldName)
+    {
+        for (JsonNode node : _children) {
+            JsonNode parent = node.findParent(fieldName);
+            if (parent != null) {
+                return (ObjectNode) parent;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar)
+    {
+        for (JsonNode node : _children) {
+            foundSoFar = node.findParents(fieldName, foundSoFar);
+        }
+        return foundSoFar;
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended ObjectNode API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Method that will set specified field, replacing old value,
+     * if any.
+     *
+     * @param value to set field to; if null, will be converted
+     *   to a {@link NullNode} first  (to remove field entry, call
+     *   {@link #remove} instead)
+     *
+     * @return Old value of the field, if any; null if there was no
+     *   old value.
+     */
+    public JsonNode set(int index, JsonNode value)
+    {
+        if (value == null) { // let's not store 'raw' nulls but nodes
+            value = nullNode();
+        }
+        if (index < 0 || index >= _children.size()) {
+            throw new IndexOutOfBoundsException("Illegal index "+ index +", array size "+size());
+        }
+        return _children.set(index, value);
+    }
+
+    /**
+     * Method for adding specified node at the end of this array.
+     * 
+     * @return This node, to allow chaining
+     */
+    public ArrayNode add(JsonNode value)
+    {
+        if (value == null) { // let's not store 'raw' nulls but nodes
+            value = nullNode();
+        }
+        _add(value);
+        return this;
+    }
+
+    /**
+     * Method for adding all child nodes of given Array, appending to
+     * child nodes this array contains
+     * 
+     * @param other Array to add contents from
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ArrayNode addAll(ArrayNode other)
+    {
+        _children.addAll(other._children);
+        return this;
+    }
+
+    /**
+     * Method for adding given nodes as child nodes of this array node.
+     * 
+     * @param nodes Nodes to add
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ArrayNode addAll(Collection<JsonNode> nodes)
+    {
+        _children.addAll(nodes);
+        return this;
+    }
+    
+    /**
+     * Method for inserting specified child node as an element
+     * of this Array. If index is 0 or less, it will be inserted as
+     * the first element; if >= size(), appended at the end, and otherwise
+     * inserted before existing element in specified index.
+     * No exceptions are thrown for any index.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ArrayNode insert(int index, JsonNode value)
+    {
+        if (value == null) {
+            value = nullNode();
+        }
+        _insert(index, value);
+        return this;
+    }
+
+    /**
+     * Method for removing an entry from this ArrayNode.
+     * Will return value of the entry at specified index, if entry existed;
+     * null if not.
+     * 
+     * @return Node removed, if any; null if none
+     */
+    public JsonNode remove(int index)
+    {
+        if (index >= 0 && index < _children.size()) {
+            return _children.remove(index);
+        }
+        return null;
+    }
+
+    /**
+     * Method for removing all elements of this array, leaving the
+     * array empty.
+     * 
+     * @return This node (to allow chaining)
+     */
+    @Override
+    public ArrayNode removeAll()
+    {
+        _children.clear();
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended ObjectNode API, mutators, generic; addXxx()/insertXxx()
+    /**********************************************************
+     */
+
+    /**
+     * Method that will construct an ArrayNode and add it as a
+     * field of this ObjectNode, replacing old value, if any.
+     *
+     * @return Newly constructed ArrayNode
+     */
+    public ArrayNode addArray()
+    {
+        ArrayNode n  = arrayNode();
+        _add(n);
+        return n;
+    }
+
+    /**
+     * Method that will construct an ObjectNode and add it at the end
+     * of this array node.
+     *
+     * @return Newly constructed ObjectNode
+     */
+    public ObjectNode addObject()
+    {
+        ObjectNode n  = objectNode();
+        _add(n);
+        return n;
+    }
+
+    /**
+     * Method that will construct a POJONode and add it at the end
+     * of this array node.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode addPOJO(Object value)
+    {
+        if (value == null) {
+            addNull();
+        } else {
+            _add(pojoNode(value));
+        }
+        return this;
+    }
+
+    /**
+     * Method that will add a null value at the end of this array node.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode addNull()
+    {
+        _add(nullNode());
+        return this;
+    }
+
+    /**
+     * Method for adding specified number at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(int v) {
+        _add(numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(Integer value) {
+        if (value == null) {
+            return addNull();
+        }
+        return _add(numberNode(value.intValue()));
+    }
+    
+    /**
+     * Method for adding specified number at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(long v) { return _add(numberNode(v)); }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(Long value) {
+        if (value == null) {
+            return addNull();
+        }
+        return _add(numberNode(value.longValue()));
+    }
+    
+    /**
+     * Method for adding specified number at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(float v) {
+        return _add(numberNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(Float value) {
+        if (value == null) {
+            return addNull();
+        }
+        return _add(numberNode(value.floatValue()));
+    }
+    
+    /**
+     * Method for adding specified number at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(double v) {
+        return _add(numberNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(Double value) {
+        if (value == null) {
+            return addNull();
+        }
+        return _add(numberNode(value.doubleValue()));
+    }
+    
+    /**
+     * Method for adding specified number at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(BigDecimal 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
+     */
+    public ArrayNode add(String v) {
+        if (v == null) {
+            return addNull();
+        }
+        return _add(textNode(v));
+    }
+
+    /**
+     * Method for adding specified boolean value at the end of this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(boolean v) {
+        return _add(booleanNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(Boolean value) {
+        if (value == null) {
+            return addNull();
+        }
+        return _add(booleanNode(value.booleanValue()));
+    }
+    
+    /**
+     * Method for adding specified binary value at the end of this array
+     * (note: when serializing as JSON, will be output Base64 encoded)
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode add(byte[] v) {
+        if (v == null) {
+            return addNull();
+        }
+        return _add(binaryNode(v));
+    }
+
+    /**
+     * Method for creating an array node, inserting it at the
+     * specified point in the array,
+     * and returning the <b>newly created array</b>
+     * (note: NOT 'this' array)
+     */
+    public ArrayNode insertArray(int index)
+    {
+        ArrayNode n  = arrayNode();
+        _insert(index, n);
+        return n;
+    }
+
+    /**
+     * Method for creating an {@link ObjectNode}, appending it at the end
+     * of this array, and returning the <b>newly created node</b>
+     * (note: NOT 'this' array)
+     * 
+     * @return Newly constructed ObjectNode
+     */
+    public ObjectNode insertObject(int index)
+    {
+        ObjectNode n  = objectNode();
+        _insert(index, n);
+        return n;
+    }
+
+    /**
+     * Method that will construct a POJONode and
+     * insert it at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insertPOJO(int index, Object value)
+    {
+        if (value == null) {
+            return insertNull(index);
+        }
+        return _insert(index, pojoNode(value));
+    }
+
+    /**
+     * Method that will insert a null value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insertNull(int index)
+    {
+        _insert(index, nullNode());
+        return this;
+    }
+
+    /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, int v) {
+        _insert(index, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, Integer value) {
+        if (value == null) {
+            insertNull(index);
+        } else {
+            _insert(index, numberNode(value.intValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, long v) {
+        return _insert(index, numberNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, Long value) {
+        if (value == null) {
+            return insertNull(index);
+        }
+        return _insert(index, numberNode(value.longValue()));
+    }
+    
+    /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, float v) {
+        return _insert(index, numberNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, Float value) {
+        if (value == null) {
+            return insertNull(index);
+        }
+        return _insert(index, numberNode(value.floatValue()));
+    }
+    
+    /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, double v) {
+        return _insert(index, numberNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, Double value) {
+        if (value == null) {
+            return insertNull(index);
+        }
+        return _insert(index, numberNode(value.doubleValue()));
+    }
+
+    /**
+     * Method that will insert specified numeric value
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, BigDecimal v) {
+        if (v == null) {
+            return insertNull(index);
+        }
+        return _insert(index, numberNode(v));
+    }
+
+    /**
+     * Method that will insert specified String
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, String v) {
+        if (v == null) {
+            return insertNull(index);
+        }
+        return _insert(index, textNode(v));
+    }
+
+    /**
+     * Method that will insert specified String
+     * at specified position in this array.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, boolean v) {
+        return _insert(index, booleanNode(v));
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, Boolean value) {
+        if (value == null) {
+            return insertNull(index);
+        }
+        return _insert(index, booleanNode(value.booleanValue()));
+    }
+    
+    /**
+     * Method that will insert specified binary value
+     * at specified position in this array
+     * (note: when written as JSON, will be Base64 encoded)
+     * 
+     * @return This array node, to allow chaining
+     */
+    public ArrayNode insert(int index, byte[] v) {
+        if (v == null) {
+            return insertNull(index);
+        }
+        return _insert(index, binaryNode(v));
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (getClass() != o.getClass()) {
+            return false;
+        }
+        return _children.equals(((ArrayNode) o)._children);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return _children.hashCode();
+    }
+
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(16 + (size() << 4));
+        sb.append('[');
+        for (int i = 0, len = _children.size(); i < len; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(_children.get(i).toString());
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private ArrayNode _add(JsonNode node)
+    {
+        _children.add(node);
+        return this;
+    }
+
+    private ArrayNode _insert(int index, JsonNode node)
+    {
+        if (index < 0) {
+            _children.add(0, node);
+        } else if (index >= _children.size()) {
+            _children.add(node);
+        } else {
+            _children.add(index, node);
+        }
+        return this;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java
new file mode 100644
index 0000000..9277219
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/BaseJsonNode.java
@@ -0,0 +1,98 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+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;
+
+/**
+ * Abstract base class common to all standard {@link JsonNode}
+ * implementations.
+ * The main addition here is that we declare that sub-classes must
+ * implement {@link JsonSerializable}.
+ * This simplifies object mapping aspects a bit, as no external serializers are needed.
+ */
+public abstract class BaseJsonNode
+    extends JsonNode
+    implements JsonSerializable
+{
+    protected BaseJsonNode() { }
+
+    /*
+    /**********************************************************
+    /* Basic definitions for non-container types
+    /**********************************************************
+     */
+
+    @Override
+    public final JsonNode findPath(String fieldName)
+    {
+        JsonNode value = findValue(fieldName);
+        if (value == null) {
+            return MissingNode.getInstance();
+        }
+        return value;
+    }
+
+    /*
+    /**********************************************************
+    /* Support for traversal-as-stream
+    /**********************************************************
+     */
+
+    @Override
+    public JsonParser traverse() {
+        return new TreeTraversingParser(this);
+    }
+
+    @Override
+    public JsonParser traverse(ObjectCodec codec) {
+        return new TreeTraversingParser(this, codec);
+    }
+    
+    /**
+     * Method that can be used for efficient type detection
+     * when using stream abstraction for traversing nodes.
+     * Will return the first {@link JsonToken} that equivalent
+     * stream event would produce (for most nodes there is just
+     * one token but for structured/container types multiple)
+     */
+    @Override
+    public abstract JsonToken asToken();
+
+    /**
+     * Returns code that identifies type of underlying numeric
+     * value, if (and only if) node is a number node.
+     */
+    @Override
+    public JsonParser.NumberType numberType() {
+        // most types non-numeric, so:
+        return null; 
+    }
+
+    /*
+    /**********************************************************
+    /* JsonSerializable
+    /**********************************************************
+     */
+
+    /**
+     * Method called to serialize node instances using given generator.
+     */
+    @Override
+    public abstract void serialize(JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Type information is needed, even if JsonNode instances are "plain" JSON,
+     * since they may be mixed with other types.
+     */
+   @Override
+    public abstract void serializeWithType(JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException;
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BigIntegerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BigIntegerNode.java
new file mode 100644
index 0000000..e86ba60
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/BigIntegerNode.java
@@ -0,0 +1,122 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Numeric node that contains simple 64-bit integer values.
+ */
+public final class BigIntegerNode
+    extends NumericNode
+{
+    private final static BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
+    private final static BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
+    private final static BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
+    private final static BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
+    
+    final protected BigInteger _value;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public BigIntegerNode(BigInteger v) { _value = v; }
+
+    public static BigIntegerNode valueOf(BigInteger v) { return new BigIntegerNode(v); }
+
+    /* 
+    /**********************************************************
+    /* Overrridden JsonNode methods
+    /**********************************************************
+     */
+
+    @Override
+    public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.BIG_INTEGER; }
+
+    @Override
+    public boolean isIntegralNumber() { return true; }
+
+    @Override
+    public boolean isBigInteger() { return true; }
+
+    @Override public boolean canConvertToInt() {
+        return (_value.compareTo(MIN_INTEGER) >= 0) && (_value.compareTo(MAX_INTEGER) <= 0);
+    }
+    @Override public boolean canConvertToLong() {
+        return (_value.compareTo(MIN_LONG) >= 0) && (_value.compareTo(MAX_LONG) <= 0);
+    }
+    
+    @Override
+    public Number numberValue() {
+        return _value;
+    }
+
+    @Override
+    public short shortValue() { return _value.shortValue(); }
+
+    @Override
+    public int intValue() { return _value.intValue(); }
+
+    @Override
+    public long longValue() { return _value.longValue(); }
+
+    @Override
+    public BigInteger bigIntegerValue() { return _value; }
+
+    @Override
+    public float floatValue() { return _value.floatValue(); }
+
+    @Override
+    public double doubleValue() { return _value.doubleValue(); }
+
+    @Override
+    public BigDecimal decimalValue() { return new BigDecimal(_value); }
+
+    /* 
+    /**********************************************************
+    /* General type coercions
+    /**********************************************************
+     */
+    
+    @Override
+    public String asText() {
+        return _value.toString();
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        return !BigInteger.ZERO.equals(_value);
+    }
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((BigIntegerNode) o)._value.equals(_value);
+    }
+
+    @Override
+    public int hashCode() {
+        return _value.hashCode();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BinaryNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BinaryNode.java
new file mode 100644
index 0000000..215d21c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/BinaryNode.java
@@ -0,0 +1,123 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Value node that contains Base64 encoded binary value, which will be
+ * output and stored as Json String value.
+ */
+public final class BinaryNode
+    extends ValueNode
+{
+    final static BinaryNode EMPTY_BINARY_NODE = new BinaryNode(new byte[0]);
+
+    final byte[] _data;
+
+    public BinaryNode(byte[] data)
+    {
+        _data = data;
+    }
+
+    public BinaryNode(byte[] data, int offset, int length)
+    {
+        if (offset == 0 && length == data.length) {
+            _data = data;
+        } else {
+            _data = new byte[length];
+            System.arraycopy(data, offset, _data, 0, length);
+        }
+    }
+
+    public static BinaryNode valueOf(byte[] data)
+    {
+        if (data == null) {
+            return null;
+        }
+        if (data.length == 0) {
+            return EMPTY_BINARY_NODE;
+        }
+        return new BinaryNode(data);
+    }
+
+    public static BinaryNode valueOf(byte[] data, int offset, int length)
+    {
+        if (data == null) {
+            return null;
+        }
+        if (length == 0) {
+            return EMPTY_BINARY_NODE;
+        }
+        return new BinaryNode(data, offset, length);
+    }
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.BINARY;
+    }
+
+    @Override
+    public JsonToken asToken() {
+        /* No distinct type; could use one for textual values,
+         * but given that it's not in text form at this point,
+         * embedded-object is closest
+         */
+        return JsonToken.VALUE_EMBEDDED_OBJECT;
+    }
+
+    /**
+     *<p>
+     * Note: caller is not to modify returned array in any way, since
+     * it is not a copy but reference to the underlying byte array.
+     */
+    @Override
+    public byte[] binaryValue() { return _data; }
+
+    /**
+     * Hmmh. This is not quite as efficient as using {@link #serialize},
+     * but will work correctly.
+     */
+    @Override
+    public String asText() {
+        return Base64Variants.getDefaultVariant().encode(_data, false);
+    }
+
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeBinary(provider.getConfig().getBase64Variant(),
+                _data, 0, _data.length);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return Arrays.equals(((BinaryNode) o)._data, _data);
+    }
+
+    @Override
+    public int hashCode() {
+        return (_data == null) ? -1 : _data.length;
+    }
+
+    /**
+     * Different from other values, since contents need to be surrounded
+     * by (double) quotes.
+     */
+    @Override
+    public String toString()
+    {
+        return Base64Variants.getDefaultVariant().encode(_data, true);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java b/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java
new file mode 100644
index 0000000..2001989
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/BooleanNode.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * This concrete value class is used to contain boolean (true / false)
+ * values. Only two instances are ever created, to minimize memory
+ * usage
+ */
+public final class BooleanNode
+    extends ValueNode
+{
+    // // Just need two instances...
+
+    public final static BooleanNode TRUE = new BooleanNode(true);
+    public final static BooleanNode FALSE = new BooleanNode(false);
+
+    private final boolean _value;
+    
+    private BooleanNode(boolean v) { _value = v; }
+
+    public static BooleanNode getTrue() { return TRUE; }
+    public static BooleanNode getFalse() { return FALSE; }
+
+    public static BooleanNode valueOf(boolean b) { return b ? TRUE : FALSE; }
+
+    @Override
+    public JsonNodeType getNodeType() {
+        return JsonNodeType.BOOLEAN;
+    }
+
+    @Override public JsonToken asToken() {
+        return _value ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
+    }
+
+    @Override
+    public boolean booleanValue() {
+        return _value;
+    }
+
+    @Override
+    public String asText() {
+        return _value ? "true" : "false";
+    }
+
+    @Override
+    public boolean asBoolean() {
+        return _value;
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        return _value;
+    }
+    
+    @Override
+    public int asInt(int defaultValue) {
+        return _value ? 1 : 0;
+    }
+    @Override
+    public long asLong(long defaultValue) {
+        return _value ? 1L : 0L;
+    }
+    @Override
+    public double asDouble(double defaultValue) {
+        return _value ? 1.0 : 0.0;
+    }
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeBoolean(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+    	/* 11-Mar-2013, tatu: Apparently ClassLoaders can manage to load
+    	 *    different instances, rendering identity comparisons broken.
+    	 *    So let's use value instead.
+         */
+    	if (o == this) return true;
+    	if (o == null) return false;
+    	if (o.getClass() != getClass()) {
+    		return false;
+    	}
+    	return (_value == ((BooleanNode) o)._value);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
new file mode 100644
index 0000000..842a1e8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ContainerNode.java
@@ -0,0 +1,145 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * This intermediate base class is used for all container nodes,
+ * specifically, array and object nodes.
+ */
+public abstract class ContainerNode<T extends ContainerNode<T>>
+    extends BaseJsonNode
+    implements JsonNodeCreator
+{
+    /**
+     * We will keep a reference to the Object (usually TreeMapper)
+     * that can construct instances of nodes to add to this container
+     * node.
+     */
+    protected final JsonNodeFactory _nodeFactory;
+
+    protected ContainerNode(JsonNodeFactory nc)
+    {
+        _nodeFactory = nc;
+    }
+
+    // all containers are mutable: can't define:
+//    @Override public abstract <T extends JsonNode> T deepCopy();
+
+    @Override
+    public abstract JsonToken asToken();
+    
+    @Override
+    public String asText() { return ""; }
+
+    /*
+    /**********************************************************
+    /* Methods reset as abstract to force real implementation
+    /**********************************************************
+     */
+
+    @Override
+    public abstract int size();
+
+    @Override
+    public abstract JsonNode get(int index);
+
+    @Override
+    public abstract JsonNode get(String fieldName);
+
+    /*
+    /**********************************************************
+    /* JsonNodeCreator implementation, just dispatch to
+    /* the real creator
+    /**********************************************************
+     */
+
+    /**
+     * Factory method that constructs and returns an empty {@link ArrayNode}
+     * Construction is done using registered {@link JsonNodeFactory}.
+     */
+    @Override
+    public final ArrayNode arrayNode() { return _nodeFactory.arrayNode(); }
+
+    /**
+     * Factory method that constructs and returns an empty {@link ObjectNode}
+     * Construction is done using registered {@link JsonNodeFactory}.
+     */
+    @Override
+    public final ObjectNode objectNode() { return _nodeFactory.objectNode(); }
+
+    @Override
+    public final NullNode nullNode() { return _nodeFactory.nullNode(); }
+
+    @Override
+    public final BooleanNode booleanNode(boolean v) { return _nodeFactory.booleanNode(v); }
+
+    @Override
+    public final NumericNode numberNode(byte v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final NumericNode numberNode(short v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final NumericNode numberNode(int v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final NumericNode numberNode(long v) { 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(Byte v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final ValueNode numberNode(Short v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final ValueNode numberNode(Integer v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final ValueNode numberNode(Long v) { return _nodeFactory.numberNode(v); }
+
+    @Override
+    public final ValueNode numberNode(Float v) { return _nodeFactory.numberNode(v); }
+    @Override
+    public final ValueNode numberNode(Double v) { return _nodeFactory.numberNode(v); }
+    
+    @Override
+    public final TextNode textNode(String text) { return _nodeFactory.textNode(text); }
+
+    @Override
+    public final BinaryNode binaryNode(byte[] data) { return _nodeFactory.binaryNode(data); }
+    @Override
+    public final BinaryNode binaryNode(byte[] data, int offset, int length) { return _nodeFactory.binaryNode(data, offset, length); }
+
+    @Override
+    public final ValueNode pojoNode(Object pojo) { return _nodeFactory.pojoNode(pojo); }
+
+    /**
+     * @deprecated Since 2.3 Use {@link #pojoNode} instead.
+     */
+    @Deprecated
+    public final POJONode POJONode(Object pojo) { return (POJONode) _nodeFactory.pojoNode(pojo); }
+    
+    /*
+    /**********************************************************
+    /* Common mutators
+    /**********************************************************
+     */
+
+    /**
+     * Method for removing all children container has (if any)
+     *
+     * @return Container node itself (to allow method call chaining)
+     */
+    public abstract T removeAll();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/DecimalNode.java b/src/main/java/com/fasterxml/jackson/databind/node/DecimalNode.java
new file mode 100644
index 0000000..ab79c1e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/DecimalNode.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Numeric node that contains values that do not fit in simple
+ * integer (int, long) or floating point (double) values.
+ */
+public final class DecimalNode
+    extends NumericNode
+{
+    public static final DecimalNode ZERO = new DecimalNode(BigDecimal.ZERO);
+
+    private final static BigDecimal MIN_INTEGER = BigDecimal.valueOf(Integer.MIN_VALUE);
+    private final static BigDecimal MAX_INTEGER = BigDecimal.valueOf(Integer.MAX_VALUE);
+    private final static BigDecimal MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE);
+    private final static BigDecimal MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE);
+
+    final protected BigDecimal _value;
+
+    /* 
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public DecimalNode(BigDecimal v) { _value = v; }
+
+    public static DecimalNode valueOf(BigDecimal d) { return new DecimalNode(d); }
+
+    /* 
+    /**********************************************************
+    /* BaseJsonNode extended API
+    /**********************************************************
+     */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.BIG_DECIMAL; }
+
+    /* 
+    /**********************************************************
+    /* Overrridden JsonNode methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isFloatingPointNumber() { return true; }
+    
+    @Override
+    public boolean isBigDecimal() { return true; }
+
+    @Override public boolean canConvertToInt() {
+        return (_value.compareTo(MIN_INTEGER) >= 0) && (_value.compareTo(MAX_INTEGER) <= 0);
+    }
+    @Override public boolean canConvertToLong() {
+        return (_value.compareTo(MIN_LONG) >= 0) && (_value.compareTo(MAX_LONG) <= 0);
+    }
+    
+    @Override
+    public Number numberValue() { return _value; }
+
+    @Override
+    public short shortValue() { return _value.shortValue(); }
+
+    @Override
+    public int intValue() { return _value.intValue(); }
+
+    @Override
+    public long longValue() { return _value.longValue(); }
+
+
+    @Override
+    public BigInteger bigIntegerValue() { return _value.toBigInteger(); }
+
+    @Override
+    public float floatValue() { return _value.floatValue(); }
+    
+    @Override
+    public double doubleValue() { return _value.doubleValue(); }
+
+    @Override
+    public BigDecimal decimalValue() { return _value; }
+
+    @Override
+    public String asText() {
+        return _value.toString();
+    }
+
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((DecimalNode) o)._value.equals(_value);
+    }
+
+    @Override
+    public int hashCode() { return _value.hashCode(); }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java b/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java
new file mode 100644
index 0000000..3adf80e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/DoubleNode.java
@@ -0,0 +1,124 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Numeric node that contains 64-bit ("double precision")
+ * floating point values simple 32-bit integer values.
+ */
+public final class DoubleNode
+    extends NumericNode
+{
+    protected final double _value;
+
+    /* 
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public DoubleNode(double v) { _value = v; }
+
+    public static DoubleNode valueOf(double v) { return new DoubleNode(v); }
+
+    /* 
+    /**********************************************************
+    /* BaseJsonNode extended API
+    /**********************************************************
+     */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.DOUBLE; }
+
+    /* 
+    /**********************************************************
+    /* Overrridden JsonNode methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isFloatingPointNumber() { return true; }
+
+    @Override
+    public boolean isDouble() { return true; }
+
+    @Override public boolean canConvertToInt() {
+        return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE);
+    }
+    @Override public boolean canConvertToLong() {
+        return (_value >= Long.MIN_VALUE && _value <= Long.MAX_VALUE);
+    }
+    
+    @Override
+    public Number numberValue() {
+        return Double.valueOf(_value);
+    }
+
+    @Override
+    public short shortValue() { return (short) _value; }
+
+    @Override
+    public int intValue() { return (int) _value; }
+
+    @Override
+    public long longValue() { return (long) _value; }
+
+    @Override
+    public float floatValue() { return (float) _value; }
+    
+    @Override
+    public double doubleValue() { return _value; }
+
+    @Override
+    public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return decimalValue().toBigInteger();
+    }
+
+    @Override
+    public String asText() {
+        return NumberOutput.toString(_value);
+    }
+
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+
+        // We must account for NaNs: NaN does not equal NaN, therefore we have
+        // to use Double.compare().
+        final double otherValue = ((DoubleNode) o)._value;
+        return Double.compare(_value, otherValue) == 0;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        // same as hashCode Double.class uses
+        long l = Double.doubleToLongBits(_value);
+        return ((int) l) ^ (int) (l >> 32);
+
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
new file mode 100644
index 0000000..c1be58b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/FloatNode.java
@@ -0,0 +1,121 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * <code>JsonNode</code> implementation for efficiently containing 32-bit
+ * `float` values.
+ * 
+ * @since 2.2
+ */
+public final class FloatNode extends NumericNode
+{
+    protected final float _value;
+
+    /* 
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public FloatNode(float v) { _value = v; }
+
+    public static FloatNode valueOf(float v) { return new FloatNode(v); }
+
+    /* 
+    /**********************************************************
+    /* BaseJsonNode extended API
+    /**********************************************************
+     */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_FLOAT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.FLOAT; }
+
+    /* 
+    /**********************************************************
+    /* Overrridden JsonNode methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isFloatingPointNumber() { return true; }
+
+    @Override
+    public boolean isFloat() { return true; }
+
+    @Override public boolean canConvertToInt() {
+        return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE);
+    }
+
+    @Override public boolean canConvertToLong() {
+        return (_value >= Long.MIN_VALUE && _value <= Long.MAX_VALUE);
+    }
+    
+    @Override
+    public Number numberValue() {
+        return Float.valueOf(_value);
+    }
+
+    @Override
+    public short shortValue() { return (short) _value; }
+
+    @Override
+    public int intValue() { return (int) _value; }
+
+    @Override
+    public long longValue() { return (long) _value; }
+
+    @Override
+    public float floatValue() { return (float) _value; }
+    
+    @Override
+    public double doubleValue() { return _value; }
+
+    @Override
+    public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); }
+
+    @Override
+    public BigInteger bigIntegerValue() {
+        return decimalValue().toBigInteger();
+    }
+
+    @Override
+    public String asText() {
+        return NumberOutput.toString(_value);
+    }
+
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+
+        // We must account for NaNs: NaN does not equal NaN, therefore we have
+        // to use Double.compare().
+        final float otherValue = ((FloatNode) o)._value;
+        return Float.compare(_value, otherValue) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Float.floatToIntBits(_value);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/IntNode.java b/src/main/java/com/fasterxml/jackson/databind/node/IntNode.java
new file mode 100644
index 0000000..8f16695
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/IntNode.java
@@ -0,0 +1,133 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Numeric node that contains simple 32-bit integer values.
+ */
+public final class IntNode
+    extends NumericNode
+{
+    // // // Let's cache small set of common value
+
+    final static int MIN_CANONICAL = -1;
+    final static int MAX_CANONICAL = 10;
+
+    private final static IntNode[] CANONICALS;
+    static {
+        int count = MAX_CANONICAL - MIN_CANONICAL + 1;
+        CANONICALS = new IntNode[count];
+        for (int i = 0; i < count; ++i) {
+            CANONICALS[i] = new IntNode(MIN_CANONICAL + i);
+        }
+    }
+
+    /**
+     * Integer value this node contains
+     */
+    final int _value;
+
+    /* 
+    ************************************************
+    * Construction
+    ************************************************
+    */
+
+    public IntNode(int v) { _value = v; }
+
+    public static IntNode valueOf(int i) {
+        if (i > MAX_CANONICAL || i < MIN_CANONICAL) return new IntNode(i);
+        return CANONICALS[i - MIN_CANONICAL];
+    }
+
+    /* 
+    /**********************************************************
+    /* BaseJsonNode extended API
+    /**********************************************************
+     */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.INT; }
+
+    /* 
+    /**********************************************************
+    /* Overrridden JsonNode methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isIntegralNumber() { return true; }
+
+    @Override
+    public boolean isInt() { return true; }
+
+    @Override public boolean canConvertToInt() { return true; }
+    @Override public boolean canConvertToLong() { return true; }
+    
+    @Override
+    public Number numberValue() {
+        return Integer.valueOf(_value);
+    }
+
+    @Override
+    public short shortValue() { return (short) _value; }
+
+    @Override
+    public int intValue() { return _value; }
+
+    @Override
+    public long longValue() { return (long) _value; }
+
+    @Override
+    public float floatValue() { return (float) _value; }
+    
+    @Override
+    public double doubleValue() { return (double) _value; }
+
+    
+    @Override
+    public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); }
+
+    @Override
+    public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); }
+
+    @Override
+    public String asText() {
+        return NumberOutput.toString(_value);
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        return _value != 0;
+    }
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((IntNode) o)._value == _value;
+    }
+
+    @Override
+        public int hashCode() { return _value; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java
new file mode 100644
index 0000000..d73499f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeCreator.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Interface that defines common "creator" functionality implemented
+ * both by {@link JsonNodeFactory} and {@link ContainerNode} (that is,
+ * JSON Object and Array nodes).
+ * 
+ * @since 2.3
+ */
+public interface JsonNodeCreator
+{
+    // Enumerated/singleton types
+    
+    public ValueNode booleanNode(boolean v);
+    public ValueNode nullNode();
+
+    // Numeric types
+
+    public ValueNode numberNode(byte v);
+    public ValueNode numberNode(Byte value);
+    public ValueNode numberNode(short v);
+    public ValueNode numberNode(Short value);
+    public ValueNode numberNode(int v);
+    public ValueNode numberNode(Integer value);
+    public ValueNode numberNode(long v);
+    public ValueNode numberNode(Long value);
+    public ValueNode numberNode(BigInteger v);
+    public ValueNode numberNode(float v);
+    public ValueNode numberNode(Float value);
+    public ValueNode numberNode(double v);
+    public ValueNode numberNode(Double value);
+    public ValueNode numberNode(BigDecimal v);
+
+    // Textual nodes, other value (non-structured) nodes
+
+    public ValueNode textNode(String text);
+    public ValueNode binaryNode(byte[] data);
+    public ValueNode binaryNode(byte[] data, int offset, int length);
+    public ValueNode pojoNode(Object pojo);
+
+    // Structured nodes:
+    // (bit unkosher, due to forward references... but has to do for now)
+
+    public ArrayNode arrayNode();
+    public ObjectNode objectNode();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
new file mode 100644
index 0000000..096ed32
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeFactory.java
@@ -0,0 +1,326 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Base class that specifies methods for getting access to
+ * Node instances (newly constructed, or shared, depending
+ * on type), as well as basic implementation of the methods. 
+ * Designed to be sub-classed if extended functionality (additions
+ * to behavior of node types, mostly) is needed.
+ */
+public class JsonNodeFactory
+    implements java.io.Serializable // since 2.1
+        ,JsonNodeCreator // since 2.3
+{
+    // with 2.2
+    private static final long serialVersionUID = -3271940633258788634L;
+
+    private final boolean _cfgBigDecimalExact;
+
+    private static final JsonNodeFactory decimalsNormalized
+        = new JsonNodeFactory(false);
+    private static final JsonNodeFactory decimalsAsIs
+        = new JsonNodeFactory(true);
+
+    /**
+     * Default singleton instance that construct "standard" node instances:
+     * given that this class is stateless, a globally shared singleton
+     * can be used.
+     */
+    public final static JsonNodeFactory instance = decimalsNormalized;
+
+    /**
+     * Main constructor
+     *
+     * <p>The only argument to this constructor is a boolean telling whether
+     * {@link DecimalNode} instances must be built with exact representations of
+     * {@link BigDecimal} instances.</p>
+     *
+     * <p>This has quite an influence since, for instance, a BigDecimal (and,
+     * therefore, a DecimalNode) constructed from input string {@code "1.0"} and
+     * another constructed with input string {@code "1.00"} <b>will not</b> be
+     * equal, since their scale differs (1 in the first case, 2 in the second
+     * case).</p>
+     *
+     * <p>Note that setting the argument to {@code true} does <i>not</i>
+     * guarantee a strict inequality between JSON representations: input texts
+     * {@code "0.1"} and {@code "1e-1"}, for instance, yield two equivalent
+     * BigDecimal instances since they have the same scale (1).</p>
+     *
+     * <p>The no-arg constructor (and the default {@link #instance}) calls this
+     * constructor with {@code false} as an argument.</p>
+     *
+     * @param bigDecimalExact see description
+     *
+     * @see BigDecimal
+     */
+    public JsonNodeFactory(boolean bigDecimalExact)
+    {
+        _cfgBigDecimalExact = bigDecimalExact;
+    }
+
+    /**
+     * Default constructor
+     *
+     * <p>This calls {@link #JsonNodeFactory(boolean)} with {@code false}
+     * as an argument.</p>
+     */
+    protected JsonNodeFactory()
+    {
+        this(false);
+    }
+
+    /**
+     * Return a factory instance with the desired behavior for BigDecimals
+     * <p>See {@link #JsonNodeFactory(boolean)} for a full description.</p>
+     *
+     * @param bigDecimalExact see description
+     * @return a factory instance
+     */
+    public static JsonNodeFactory withExactBigDecimals(boolean bigDecimalExact)
+    {
+        return bigDecimalExact ? decimalsAsIs : decimalsNormalized;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods for literal values
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for getting an instance of JSON boolean value
+     * (either literal 'true' or 'false')
+     */
+    @Override
+    public BooleanNode booleanNode(boolean v) {
+        return v ? BooleanNode.getTrue() : BooleanNode.getFalse();
+    }
+
+    /**
+     * Factory method for getting an instance of JSON null node (which
+     * represents literal null value)
+     */
+    @Override
+    public NullNode nullNode() { return NullNode.getInstance(); }
+
+    /*
+    /**********************************************************
+    /* Factory methods for numeric values
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 8-bit value
+     */
+    @Override
+    public NumericNode numberNode(byte v) { return IntNode.valueOf(v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may
+     * be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Byte value) {
+        return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 16-bit integer value
+     */
+    @Override
+    public NumericNode numberNode(short v) { return ShortNode.valueOf(v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may
+     * be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Short value) {
+        return (value == null) ? nullNode() : ShortNode.valueOf(value);
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 32-bit integer value
+     */
+    @Override
+    public NumericNode numberNode(int v) { return IntNode.valueOf(v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may
+     * be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Integer value) {
+        return (value == null) ? nullNode() : IntNode.valueOf(value.intValue());
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 64-bit integer value
+     */
+    @Override
+    public NumericNode numberNode(long v) { return LongNode.valueOf(v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Long value) {
+        return (value == null) ? nullNode() : LongNode.valueOf(value.longValue());
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given unlimited range integer value
+     */
+    @Override
+    public NumericNode numberNode(BigInteger v) { return BigIntegerNode.valueOf(v); }
+
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 32-bit floating point value
+     */
+    @Override
+    public NumericNode numberNode(float v) { return FloatNode.valueOf((float) v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may
+     * be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Float value) {
+        return (value == null) ? nullNode() : FloatNode.valueOf(value.floatValue());
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given 64-bit floating point value
+     */
+    @Override
+    public NumericNode numberNode(double v) { return DoubleNode.valueOf(v); }
+
+    /**
+     * Alternate factory method that will handle wrapper value, which may
+     * be null.
+     * Due to possibility of null, returning type is not guaranteed to be
+     * {@link NumericNode}, but just {@link ValueNode}.
+     */
+    @Override
+    public ValueNode numberNode(Double value) {
+        return (value == null) ? nullNode() : DoubleNode.valueOf(value.doubleValue());
+    }
+    
+    /**
+     * Factory method for getting an instance of JSON numeric value
+     * that expresses given unlimited precision floating point value
+     *
+     * <p>In the event that the factory has been built to normalize decimal
+     * values, the BigDecimal argument will be stripped off its trailing zeroes,
+     * using {@link BigDecimal#stripTrailingZeros()}.</p>
+     *
+     * @see #JsonNodeFactory(boolean)
+     */
+    @Override
+    public NumericNode numberNode(BigDecimal v)
+    {
+        /*
+         * If the user wants the exact representation of this big decimal,
+         * return the value directly
+         */
+        if (_cfgBigDecimalExact)
+            return DecimalNode.valueOf(v);
+
+        /*
+         * If the user has asked to strip trailing zeroes, however, there is
+         * this bug to account for:
+         *
+         * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6480539
+         *
+         * In short: zeroes are never stripped out of 0! We therefore _have_
+         * to compare with BigDecimal.ZERO...
+         */
+        return v.compareTo(BigDecimal.ZERO) == 0 ? DecimalNode.ZERO
+            : DecimalNode.valueOf(v.stripTrailingZeros());
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods for textual values
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for constructing a node that represents JSON
+     * String value
+     */
+    @Override
+    public TextNode textNode(String text) { return TextNode.valueOf(text); }
+
+    /**
+     * Factory method for constructing a node that represents given
+     * binary data, and will get serialized as equivalent base64-encoded
+     * String value
+     */
+    @Override
+    public BinaryNode binaryNode(byte[] data) { return BinaryNode.valueOf(data); }
+
+    /**
+     * Factory method for constructing a node that represents given
+     * binary data, and will get serialized as equivalent base64-encoded
+     * String value
+     */
+    @Override
+    public BinaryNode binaryNode(byte[] data, int offset, int length) {
+        return BinaryNode.valueOf(data, offset, length);
+    }
+
+    /*
+    /**********************************************************
+    /* Factory method for structured values
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for constructing an empty JSON Array node
+     */
+    @Override
+    public ArrayNode arrayNode() { return new ArrayNode(this); }
+
+    /**
+     * Factory method for constructing an empty JSON Object ("struct") node
+     */
+    @Override
+    public ObjectNode objectNode() { return new ObjectNode(this); }
+
+    /**
+     * Factory method for constructing a wrapper for POJO
+     * ("Plain Old Java Object") objects; these will get serialized
+     * using data binding, usually as JSON Objects, but in some
+     * cases as JSON Strings or other node types.
+     */
+    @Override
+    public ValueNode pojoNode(Object pojo) { return new POJONode(pojo); }
+
+    /**
+     * @deprecated Since 2.3 Use {@link #pojoNode} instead.
+     */
+    @Deprecated
+    public POJONode POJONode(Object pojo) { return new POJONode(pojo); }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeType.java b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeType.java
new file mode 100644
index 0000000..86fd015
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/JsonNodeType.java
@@ -0,0 +1,28 @@
+package com.fasterxml.jackson.databind.node;
+
+/**
+ * Enumeration of JSON types.
+ * Covers all JSON types defined by <a
+ * href="http://tools.ietf.org/html/rfc4627">RFC 4627</a> (array, boolean,
+ * null, number, object and string) but also Jackson-specific types: binary,
+ * missing and POJO; although does not distinguish between more granular
+ * types.
+ *
+ * @see BinaryNode
+ * @see MissingNode
+ * @see POJONode
+ * 
+ * @since 2.2
+ */
+public enum JsonNodeType
+{
+    ARRAY,
+    BINARY,
+    BOOLEAN,
+    MISSING,
+    NULL,
+    NUMBER,
+    OBJECT,
+    POJO,
+    STRING
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/LongNode.java b/src/main/java/com/fasterxml/jackson/databind/node/LongNode.java
new file mode 100644
index 0000000..a53a1e2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/LongNode.java
@@ -0,0 +1,111 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Numeric node that contains simple 64-bit integer values.
+ */
+public final class LongNode
+    extends NumericNode
+{
+    final long _value;
+
+    /* 
+    ************************************************
+    * Construction
+    ************************************************
+    */
+
+    public LongNode(long v) { _value = v; }
+
+    public static LongNode valueOf(long l) { return new LongNode(l); }
+
+    /* 
+    ************************************************
+    * Overrridden JsonNode methods
+    ************************************************
+    */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.LONG; }
+
+
+    @Override
+    public boolean isIntegralNumber() { return true; }
+
+    @Override
+    public boolean isLong() { return true; }
+
+    @Override public boolean canConvertToInt() {
+        return (_value >= Integer.MIN_VALUE && _value <= Integer.MAX_VALUE);
+    }
+    @Override public boolean canConvertToLong() { return true; }
+    
+    @Override
+    public Number numberValue() {
+        return Long.valueOf(_value);
+    }
+
+    @Override
+    public short shortValue() { return (short) _value; }
+
+    @Override
+    public int intValue() { return (int) _value; }
+
+    @Override
+    public long longValue() { return _value; }
+
+    @Override
+    public float floatValue() { return _value; }
+
+    @Override
+    public double doubleValue() { return _value; }
+
+    @Override
+    public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); }
+
+    @Override
+    public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); }
+
+    @Override
+    public String asText() {
+        return NumberOutput.toString(_value);
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        return _value != 0;
+    }
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((LongNode) o)._value == _value;
+    }
+
+    @Override
+    public int hashCode() {
+        return ((int) _value) ^ (int) (_value >> 32);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java
new file mode 100644
index 0000000..2423f2d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/MissingNode.java
@@ -0,0 +1,98 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * This singleton node class is generated to denote "missing nodes"
+ * along paths that do not exist. For example, if a path via
+ * element of an array is requested for an element outside range
+ * of elements in the array; or for a non-array value, result
+ * will be reference to this node.
+ *<p>
+ * In most respects this placeholder node will act as {@link NullNode};
+ * for example, for purposes of value conversions, value is considered
+ * to be null and represented as value zero when used for numeric
+ * conversions. 
+ */
+public final class MissingNode
+    extends ValueNode
+{
+    private final static MissingNode instance = new MissingNode();
+
+    private MissingNode() { }
+
+    // Immutable: no need to copy
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends JsonNode> T deepCopy() { return (T) this; }
+    
+    public static MissingNode getInstance() { return instance; }
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.MISSING;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.NOT_AVAILABLE; }
+
+    @Override
+    public String asText() { return ""; }
+
+    // // Note: not a numeric node, hence default 'asXxx()' are fine:
+    
+    /*
+    public int asInt(int defaultValue);
+    public long asLong(long defaultValue);
+    public double asDouble(double defaultValue);
+    public boolean asBoolean(boolean defaultValue);
+    */
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        /* 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
+         * been written out.
+         */
+        jg.writeNull();
+    }
+
+    @Override
+    public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNull();
+    }
+    
+    @Override
+    public boolean equals(Object o)
+    {
+        /* Hmmh. Since there's just a singleton instance, this
+         * fails in all cases but with identity comparison.
+         * However: if this placeholder value was to be considered
+         * similar to Sql NULL, it shouldn't even equal itself?
+         * That might cause problems when dealing with collections
+         * like Sets... so for now, let's let identity comparison
+         * return true.
+         */
+        return (o == this);
+    }
+
+    @Override
+    public String toString()
+    {
+        // toString() should never return null
+        return "";
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java b/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java
new file mode 100644
index 0000000..17d760d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java
@@ -0,0 +1,234 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Helper class used by {@link TreeTraversingParser} to keep track
+ * of current location within traversed JSON tree.
+ */
+abstract class NodeCursor
+    extends JsonStreamContext
+{
+    /**
+     * Parent cursor of this cursor, if any; null for root
+     * cursors.
+     */
+    protected final NodeCursor _parent;
+
+    /**
+     * Current field name
+     */
+    protected String _currentName;
+    
+    public NodeCursor(int contextType, NodeCursor p)
+    {
+        super();
+        _type = contextType;
+        _index = -1;
+        _parent = p;
+    }
+
+    /*
+    /**********************************************************
+    /* JsonStreamContext impl
+    /**********************************************************
+     */
+
+    // note: co-variant return type
+    @Override
+    public final NodeCursor getParent() { return _parent; }
+
+    @Override
+    public final String getCurrentName() {
+        return _currentName;
+    }
+
+    /**
+     * @since 2.0
+     */
+    public void overrideCurrentName(String name) {
+        _currentName = name;
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    public abstract JsonToken nextToken();
+    public abstract JsonToken nextValue();
+    public abstract JsonToken endToken();
+
+    public abstract JsonNode currentNode();
+    public abstract boolean currentHasChildren();
+    
+    /**
+     * Method called to create a new context for iterating all
+     * contents of the current structured value (JSON array or object)
+     */
+    public final NodeCursor iterateChildren() {
+        JsonNode n = currentNode();
+        if (n == null) throw new IllegalStateException("No current node");
+        if (n.isArray()) { // false since we have already returned START_ARRAY
+            return new Array(n, this);
+        }
+        if (n.isObject()) {
+            return new Object(n, this);
+        }
+        throw new IllegalStateException("Current node of type "+n.getClass().getName());
+    }
+
+    /*
+    /**********************************************************
+    /* Concrete implementations
+    /**********************************************************
+     */
+
+    /**
+     * Context matching root-level value nodes (i.e. anything other
+     * than JSON Object and Array).
+     * Note that context is NOT created for leaf values.
+     */
+    protected final static class RootValue
+        extends NodeCursor
+    {
+        protected JsonNode _node;
+
+        protected boolean _done = false;
+
+        public RootValue(JsonNode n, NodeCursor p) {
+            super(JsonStreamContext.TYPE_ROOT, p);
+            _node = n;
+        }
+
+        @Override
+        public void overrideCurrentName(String name) {
+            
+        }
+        
+        @Override
+        public JsonToken nextToken() {
+            if (!_done) {
+                _done = true;
+                return _node.asToken();
+            }
+            _node = null;
+            return null;
+        }
+        
+        @Override
+        public JsonToken nextValue() { return nextToken(); }
+        @Override
+        public JsonToken endToken() { return null; }
+        @Override
+        public JsonNode currentNode() { return _node; }
+        @Override
+        public boolean currentHasChildren() { return false; }
+    }
+
+    /**
+     * Cursor used for traversing non-empty JSON Array nodes
+     */
+    protected final static class Array
+        extends NodeCursor
+    {
+        protected Iterator<JsonNode> _contents;
+
+        protected JsonNode _currentNode;
+
+        public Array(JsonNode n, NodeCursor p) {
+            super(JsonStreamContext.TYPE_ARRAY, p);
+            _contents = n.elements();
+        }
+
+        @Override
+        public JsonToken nextToken()
+        {
+            if (!_contents.hasNext()) {
+                _currentNode = null;
+                return null;
+            }
+            _currentNode = _contents.next();
+            return _currentNode.asToken();
+        }
+
+        @Override
+        public JsonToken nextValue() { return nextToken(); }
+        @Override
+        public JsonToken endToken() { return JsonToken.END_ARRAY; }
+
+        @Override
+        public JsonNode currentNode() { return _currentNode; }
+        @Override
+        public boolean currentHasChildren() {
+            // note: ONLY to be called for container nodes
+            return ((ContainerNode<?>) currentNode()).size() > 0;
+        }
+    }
+
+    /**
+     * Cursor used for traversing non-empty JSON Object nodes
+     */
+    protected final static class Object
+        extends NodeCursor
+    {
+        protected Iterator<Map.Entry<String, JsonNode>> _contents;
+        protected Map.Entry<String, JsonNode> _current;
+
+        protected boolean _needEntry;
+        
+        public Object(JsonNode n, NodeCursor p)
+        {
+            super(JsonStreamContext.TYPE_OBJECT, p);
+            _contents = ((ObjectNode) n).fields();
+            _needEntry = true;
+        }
+
+        @Override
+        public JsonToken nextToken()
+        {
+            // Need a new entry?
+            if (_needEntry) {
+                if (!_contents.hasNext()) {
+                    _currentName = null;
+                    _current = null;
+                    return null;
+                }
+                _needEntry = false;
+                _current = _contents.next();
+                _currentName = (_current == null) ? null : _current.getKey();
+                return JsonToken.FIELD_NAME;
+            }
+            _needEntry = true;
+            return _current.getValue().asToken();
+        }
+
+        @Override
+        public JsonToken nextValue()
+        {
+            JsonToken t = nextToken();
+            if (t == JsonToken.FIELD_NAME) {
+                t = nextToken();
+            }
+            return t;
+        }
+
+        @Override
+        public JsonToken endToken() { return JsonToken.END_OBJECT; }
+
+        @Override
+        public JsonNode currentNode() {
+            return (_current == null) ? null : _current.getValue();
+        }
+        @Override
+        public boolean currentHasChildren() {
+            // note: ONLY to be called for container nodes
+            return ((ContainerNode<?>) currentNode()).size() > 0;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java
new file mode 100644
index 0000000..9a7c5a0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/NullNode.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * This singleton value class is used to contain explicit JSON null
+ * value.
+ */
+public final class NullNode
+    extends ValueNode
+{
+    // // Just need a fly-weight singleton
+
+    public final static NullNode instance = new NullNode();
+
+    private NullNode() { }
+
+    public static NullNode getInstance() { return instance; }
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.NULL;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NULL; }
+
+    @Override
+    public String asText() {
+        return "null";
+    }
+
+    // as with MissingNode, not considered number node; hence defaults are returned if provided
+    
+    /*
+    public int asInt(int defaultValue);
+    public long asLong(long defaultValue);
+    public double asDouble(double defaultValue);
+    public boolean asBoolean(boolean defaultValue);
+    */
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNull();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return (o == this);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java
new file mode 100644
index 0000000..3a80971
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/NumericNode.java
@@ -0,0 +1,72 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.JsonParser;
+
+/**
+ * Intermediate value node used for numeric nodes.
+ */
+public abstract class NumericNode
+    extends ValueNode
+{
+    protected NumericNode() { }
+
+    @Override
+    public final JsonNodeType getNodeType()
+    {
+        return JsonNodeType.NUMBER;
+    }
+
+    // // // Let's re-abstract so sub-classes handle them
+
+    @Override
+    public abstract JsonParser.NumberType numberType();
+
+    @Override public abstract Number numberValue();
+    @Override public abstract int intValue();
+    @Override public abstract long longValue();
+    @Override public abstract double doubleValue();
+    @Override public abstract BigDecimal decimalValue();
+    @Override public abstract BigInteger bigIntegerValue();
+
+    @Override public abstract boolean canConvertToInt();
+    @Override public abstract boolean canConvertToLong();
+    
+    /* 
+    /**********************************************************
+    /* General type coercions
+    /**********************************************************
+     */
+    
+    @Override
+    public abstract String asText();
+
+    @Override
+    public final int asInt() {
+        return intValue();
+    }
+    @Override
+    public final int asInt(int defaultValue) {
+        return intValue();
+    }
+
+    @Override
+    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();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
new file mode 100644
index 0000000..80902c8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
@@ -0,0 +1,811 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Node that maps to JSON Object structures in JSON content.
+ */
+public final class ObjectNode
+    extends ContainerNode<ObjectNode>
+{
+    // Note: LinkedHashMap for backwards compatibility
+    private final Map<String, JsonNode> _children
+        = new LinkedHashMap<String, JsonNode>();
+
+    public ObjectNode(JsonNodeFactory nc) { super(nc); }
+
+    /* Question: should this delegate to `JsonNodeFactory`? It does not absolutely
+     * have to, as long as sub-types override the method but...
+     */
+    // note: co-variant for type safety
+    @SuppressWarnings("unchecked")
+    @Override
+    public ObjectNode deepCopy()
+    {
+        ObjectNode ret = new ObjectNode(_nodeFactory);
+
+        for (Map.Entry<String, JsonNode> entry: _children.entrySet())
+            ret._children.put(entry.getKey(), entry.getValue().deepCopy());
+
+        return ret;
+    }
+
+    /*
+    /**********************************************************
+    /* Implementation of core JsonNode API
+    /**********************************************************
+     */
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.OBJECT;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.START_OBJECT; }
+
+    @Override
+    public int size() {
+        return _children.size();
+    }
+
+    @Override
+    public Iterator<JsonNode> elements()
+    {
+        return _children.values().iterator();
+    }
+
+    @Override
+    public JsonNode get(int index) { return null; }
+
+    @Override
+    public JsonNode get(String fieldName)
+    {
+        return _children.get(fieldName);
+    }
+
+    @Override
+    public Iterator<String> fieldNames()
+    {
+        return _children.keySet().iterator();
+    }
+
+    @Override
+    public JsonNode path(int index)
+    {
+        return MissingNode.getInstance();
+    }
+
+    @Override
+    public JsonNode path(String fieldName)
+    {
+        JsonNode n = _children.get(fieldName);
+        if (n != null) {
+            return n;
+        }
+        return MissingNode.getInstance();
+    }
+
+    /**
+     * Method to use for accessing all fields (with both names
+     * and values) of this JSON Object.
+     */
+    @Override
+    public Iterator<Map.Entry<String, JsonNode>> fields()
+    {
+        return _children.entrySet().iterator();
+    }
+
+    @Override
+    public ObjectNode with(String propertyName)
+    {
+        JsonNode n = _children.get(propertyName);
+        if (n != null) {
+            if (n instanceof ObjectNode) {
+                return (ObjectNode) n;
+            }
+            throw new UnsupportedOperationException("Property '" + propertyName
+                + "' has value that is not of type ObjectNode (but " + n
+                .getClass().getName() + ")");
+        }
+        ObjectNode result = objectNode();
+        _children.put(propertyName, result);
+        return result;
+    }
+
+    @Override
+    public ArrayNode withArray(String propertyName)
+    {
+        JsonNode n = _children.get(propertyName);
+        if (n != null) {
+            if (n instanceof ArrayNode) {
+                return (ArrayNode) n;
+            }
+            throw new UnsupportedOperationException("Property '" + propertyName
+                + "' has value that is not of type ArrayNode (but " + n
+                .getClass().getName() + ")");
+        }
+        ArrayNode result = arrayNode();
+        _children.put(propertyName, result);
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, finding value nodes
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonNode findValue(String fieldName)
+    {
+        for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
+            if (fieldName.equals(entry.getKey())) {
+                return entry.getValue();
+            }
+            JsonNode value = entry.getValue().findValue(fieldName);
+            if (value != null) {
+                return value;
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar)
+    {
+        for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
+            if (fieldName.equals(entry.getKey())) {
+                if (foundSoFar == null) {
+                    foundSoFar = new ArrayList<JsonNode>();
+                }
+                foundSoFar.add(entry.getValue());
+            } else { // only add children if parent not added
+                foundSoFar = entry.getValue().findValues(fieldName, foundSoFar);
+            }
+        }
+        return foundSoFar;
+    }
+
+    @Override
+    public List<String> findValuesAsText(String fieldName, List<String> foundSoFar)
+    {
+        for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
+            if (fieldName.equals(entry.getKey())) {
+                if (foundSoFar == null) {
+                    foundSoFar = new ArrayList<String>();
+                }
+                foundSoFar.add(entry.getValue().asText());
+            } else { // only add children if parent not added
+                foundSoFar = entry.getValue().findValuesAsText(fieldName,
+                    foundSoFar);
+            }
+        }
+        return foundSoFar;
+    }
+    
+    @Override
+    public ObjectNode findParent(String fieldName)
+    {
+        for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
+            if (fieldName.equals(entry.getKey())) {
+                return this;
+            }
+            JsonNode value = entry.getValue().findParent(fieldName);
+            if (value != null) {
+                return (ObjectNode) value;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar)
+    {
+        for (Map.Entry<String, JsonNode> entry : _children.entrySet()) {
+            if (fieldName.equals(entry.getKey())) {
+                if (foundSoFar == null) {
+                    foundSoFar = new ArrayList<JsonNode>();
+                }
+                foundSoFar.add(this);
+            } else { // only add children if parent not added
+                foundSoFar = entry.getValue()
+                    .findParents(fieldName, foundSoFar);
+            }
+        }
+        return foundSoFar;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, serialization
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to serialize this node and
+     * all of its descendants using specified JSON generator.
+     */
+    @Override
+    public void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeStartObject();
+        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
+            jg.writeFieldName(en.getKey());
+                /* 17-Feb-2009, tatu: Can we trust that all nodes will always
+                 *   extend BaseJsonNode? Or if not, at least implement
+                 *   JsonSerializable? Let's start with former, change if
+                 *   we must.
+                 */
+            ((BaseJsonNode) en.getValue()).serialize(jg, provider);
+        }
+        jg.writeEndObject();
+    }
+
+    @Override
+    public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        typeSer.writeTypePrefixForObject(this, jg);
+        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
+            jg.writeFieldName(en.getKey());
+            ((BaseJsonNode) en.getValue()).serialize(jg, provider);
+        }
+        typeSer.writeTypeSuffixForObject(this, jg);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended ObjectNode API, mutators, since 2.1
+    /**********************************************************
+     */
+
+    /**
+     * Method that will set specified field, replacing old value, if any.
+     * Note that this is identical to {@link #replace(String, JsonNode)},
+     * except for return value.
+     *<p>
+     * NOTE: added to replace those uses of {@link #put(String, JsonNode)}
+     * where chaining with 'this' is desired.
+     *
+     * @param value to set field to; if null, will be converted
+     *   to a {@link NullNode} first  (to remove field entry, call
+     *   {@link #remove} instead)
+     *
+     * @return This node after adding/replacing property value (to allow chaining)
+     *
+     * @since 2.1
+     */
+    public JsonNode set(String fieldName, JsonNode value)
+    {
+        if (value == null) {
+            value = nullNode();
+        }
+        _children.put(fieldName, value);
+        return this;
+    }
+
+    /**
+     * Method for adding given properties to this object node, overriding
+     * any existing values for those properties.
+     * 
+     * @param properties Properties to add
+     * 
+     * @return This node after adding/replacing property values (to allow chaining)
+     *
+     * @since 2.1
+     */
+    public JsonNode setAll(Map<String,JsonNode> properties)
+    {
+        for (Map.Entry<String, JsonNode> en : properties.entrySet()) {
+            JsonNode n = en.getValue();
+            if (n == null) {
+                n = nullNode();
+            }
+            _children.put(en.getKey(), n);
+        }
+        return this;
+    }
+
+    /**
+     * Method for adding all properties of the given Object, overriding
+     * any existing values for those properties.
+     * 
+     * @param other Object of which properties to add to this object
+     *
+     * @return This node after addition (to allow chaining)
+     *
+     * @since 2.1
+     */
+    public JsonNode setAll(ObjectNode other)
+    {
+        _children.putAll(other._children);
+        return this;
+    }
+    
+    /**
+     * Method for replacing value of specific property with passed
+     * value, and returning value (or null if none).
+     *
+     * @param fieldName Property of which value to replace
+     * @param value Value to set property to, replacing old value if any
+     * 
+     * @return Old value of the property; null if there was no such property
+     *   with value
+     * 
+     * @since 2.1
+     */
+    public JsonNode replace(String fieldName, JsonNode value)
+    {
+        if (value == null) { // let's not store 'raw' nulls but nodes
+            value = nullNode();
+        }
+        return _children.put(fieldName, value);
+    }
+
+    /**
+     * Method for removing field entry from this ObjectNode, and
+     * returning instance after removal.
+     * 
+     * @return This node after removing entry (if any)
+     * 
+     * @since 2.1
+     */
+    public JsonNode without(String fieldName)
+    {
+        _children.remove(fieldName);
+        return this;
+    }
+
+    /**
+     * Method for removing specified field properties out of
+     * this ObjectNode.
+     * 
+     * @param fieldNames Names of fields to remove
+     * 
+     * @return This node after removing entries
+     * 
+     * @since 2.1
+     */
+    public ObjectNode without(Collection<String> fieldNames)
+    {
+        _children.keySet().removeAll(fieldNames);
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended ObjectNode API, mutators, generic
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will set specified field, replacing old value, if any.
+     *
+     * @param value to set field to; if null, will be converted
+     *   to a {@link NullNode} first  (to remove field entry, call
+     *   {@link #remove} instead)
+     *<p>
+     * NOTE: this method will be <b>deprecated</b> in 2.2; and should
+     * be replace with either
+     * {@link #set(String,JsonNode)} or {@link #replace(String,JsonNode)},
+     * depending on which return value is desired for possible chaining.
+     *   
+     * @return Old value of the field, if any; null if there was no
+     *   old value.
+     */
+    public JsonNode put(String fieldName, JsonNode value)
+    {
+        if (value == null) { // let's not store 'raw' nulls but nodes
+            value = nullNode();
+        }
+        return _children.put(fieldName, value);
+    }
+    
+    /**
+     * Method for removing field entry from this ObjectNode.
+     * Will return value of the field, if such field existed;
+     * null if not.
+     * 
+     * @return Value of specified field, if it existed; null if not
+     */
+    public JsonNode remove(String fieldName)
+    {
+        return _children.remove(fieldName);
+    }
+
+    /**
+     * Method for removing specified field properties out of
+     * this ObjectNode.
+     * 
+     * @param fieldNames Names of fields to remove
+     * 
+     * @return This node after removing entries
+     */
+    public ObjectNode remove(Collection<String> fieldNames)
+    {
+        _children.keySet().removeAll(fieldNames);
+        return this;
+    }
+    
+    /**
+     * Method for removing all field properties, such that this
+     * ObjectNode will contain no properties after call.
+     * 
+     * @return This node after removing all entries
+     */
+    @Override
+    public ObjectNode removeAll()
+    {
+        _children.clear();
+        return this;
+    }
+
+    /**
+     * Method for adding given properties to this object node, overriding
+     * any existing values for those properties.
+     *<p>
+     * NOTE: this method will be <b>deprecated</b> in 2.2; and should
+     * be replace with {@link #setAll(Map)}.
+     * 
+     * @param properties Properties to add
+     * 
+     * @return This node after adding/replacing property values (to allow chaining)
+     */
+    public JsonNode putAll(Map<String,JsonNode> properties) {
+        return setAll(properties);
+    }
+
+    /**
+     * Method for adding all properties of the given Object, overriding
+     * any existing values for those properties.
+     *<p>
+     * NOTE: this method will be <b>deprecated</b> in 2.2; and should
+     * be replace with {@link #setAll(ObjectNode)}.
+     * 
+     * @param other Object of which properties to add to this object
+     * 
+     * @return This node (to allow chaining)
+     */
+    public JsonNode putAll(ObjectNode other) {
+        return setAll(other);
+    }
+
+    /**
+     * Method for removing all field properties out of this ObjectNode
+     * <b>except</b> for ones specified in argument.
+     * 
+     * @param fieldNames Fields to <b>retain</b> in this ObjectNode
+     * 
+     * @return This node (to allow call chaining)
+     */
+    public ObjectNode retain(Collection<String> fieldNames)
+    {
+        _children.keySet().retainAll(fieldNames);
+        return this;
+    }
+
+    /**
+     * Method for removing all field properties out of this ObjectNode
+     * <b>except</b> for ones specified in argument.
+     * 
+     * @param fieldNames Fields to <b>retain</b> in this ObjectNode
+     * 
+     * @return This node (to allow call chaining)
+     */
+    public ObjectNode retain(String... fieldNames) {
+        return retain(Arrays.asList(fieldNames));
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended ObjectNode API, mutators, typed
+    /**********************************************************
+     */
+
+    /**
+     * Method that will construct an ArrayNode and add it as a
+     * field of this ObjectNode, replacing old value, if any.
+     *<p>
+     * <b>NOTE</b>: Unlike all <b>put(...)</b> methods, return value
+     * is <b>NOT</b> this <code>ObjectNode</code>, but the
+     * <b>newly created</b> <code>ArrayNode</code> instance.
+     *
+     * @return Newly constructed ArrayNode (NOT the old value,
+     *   which could be of any type)
+     */
+    public ArrayNode putArray(String fieldName)
+    {
+        ArrayNode n  = arrayNode();
+        _children.put(fieldName, n);
+        return n;
+    }
+
+    /**
+     * Method that will construct an ObjectNode and add it as a
+     * field of this ObjectNode, replacing old value, if any.
+     *<p>
+     * <b>NOTE</b>: Unlike all <b>put(...)</b> methods, return value
+     * is <b>NOT</b> this <code>ObjectNode</code>, but the
+     * <b>newly created</b> <code>ObjectNode</code> instance.
+     *
+     * @return Newly constructed ObjectNode (NOT the old value,
+     *   which could be of any type)
+     */
+    public ObjectNode putObject(String fieldName)
+    {
+        ObjectNode n  = objectNode();
+        _children.put(fieldName, n);
+        return n;
+    }
+
+    /**
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode putPOJO(String fieldName, Object pojo) {
+        _children.put(fieldName, pojoNode(pojo));
+        return this;
+    }
+
+    /**
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode putNull(String fieldName)
+    {
+        _children.put(fieldName, nullNode());
+        return this;
+    }
+
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, short v) {
+        _children.put(fieldName, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Short value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, numberNode(value.shortValue()));
+        }
+        return this;
+    }
+
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, int v) {
+        _children.put(fieldName, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Integer value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, numberNode(value.intValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, long v) {
+        _children.put(fieldName, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Long value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, numberNode(value.longValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, float v) {
+        _children.put(fieldName, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Float value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, numberNode(value.floatValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, double v) {
+        _children.put(fieldName, numberNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Double value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, numberNode(value.doubleValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method for setting value of a field to specified numeric value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, BigDecimal v) {
+        if (v == null) {
+            putNull(fieldName);
+        } else {
+            _children.put(fieldName, numberNode(v));
+        }
+        return this;
+    }
+
+    /**
+     * Method for setting value of a field to specified String value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, String v) {
+        if (v == null) {
+            putNull(fieldName);
+        } else {
+            _children.put(fieldName, textNode(v));
+        }
+        return this;
+    }
+
+    /**
+     * Method for setting value of a field to specified String value.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, boolean v) {
+        _children.put(fieldName, booleanNode(v));
+        return this;
+    }
+
+    /**
+     * Alternative method that we need to avoid bumping into NPE issues
+     * with auto-unboxing.
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, Boolean value) {
+        if (value == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, booleanNode(value.booleanValue()));
+        }
+        return this;
+    }
+    
+    /**
+     * Method for setting value of a field to specified binary value
+     * 
+     * @return This node (to allow chaining)
+     */
+    public ObjectNode put(String fieldName, byte[] v) {
+        if (v == null) {
+            _children.put(fieldName, nullNode());
+        } else {
+            _children.put(fieldName, binaryNode(v));
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        
+        // minor improvement, wrt [Issue#70]
+        if (getClass() != o.getClass()) {
+            return false;
+        }
+        return _children.equals(((ObjectNode) o)._children);
+    }
+    
+    @Override
+    public int hashCode()
+    {
+        return _children.hashCode();
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(32 + (size() << 4));
+        sb.append("{");
+        int count = 0;
+        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
+            if (count > 0) {
+                sb.append(",");
+            }
+            ++count;
+            TextNode.appendQuoted(sb, en.getKey());
+            sb.append(':');
+            sb.append(en.getValue().toString());
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java
new file mode 100644
index 0000000..9c2b7a0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/POJONode.java
@@ -0,0 +1,153 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Value node that contains a wrapped POJO, to be serialized as
+ * a JSON constructed through data mapping (usually done by
+ * calling {@link com.fasterxml.jackson.databind.ObjectMapper}).
+ */
+public final class POJONode
+    extends ValueNode
+{
+    protected final Object _value;
+
+    public POJONode(Object v) { _value = v; }
+
+    /*
+    /**********************************************************
+    /* Base class overrides
+    /**********************************************************
+     */
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.POJO;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_EMBEDDED_OBJECT; }
+
+    /**
+     * As it is possible that some implementations embed byte[] as POJONode
+     * (despite optimal being {@link BinaryNode}), let's add support for exposing
+     * binary data here too.
+     */
+    @Override
+    public byte[] binaryValue() throws IOException
+    {
+        if (_value instanceof byte[]) {
+            return (byte[]) _value;
+        }
+        return super.binaryValue();
+    }
+    
+    /* 
+    /**********************************************************
+    /* General type coercions
+    /**********************************************************
+     */
+
+    @Override
+    public String asText() {
+        return (_value == null) ? "null" : _value.toString();
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue)
+    {
+        if (_value != null && _value instanceof Boolean) {
+            return ((Boolean) _value).booleanValue();
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public int asInt(int defaultValue)
+    {
+        if (_value instanceof Number) {
+            return ((Number) _value).intValue();
+        }
+        return defaultValue;
+    }
+
+    @Override
+    public long asLong(long defaultValue)
+    {
+        if (_value instanceof Number) {
+            return ((Number) _value).longValue();
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public double asDouble(double defaultValue)
+    {
+        if (_value instanceof Number) {
+            return ((Number) _value).doubleValue();
+        }
+        return defaultValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, serialization
+    /**********************************************************
+     */
+
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        if (_value == null) {
+            jg.writeNull();
+        } else {
+            jg.writeObject(_value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to access the POJO this node wraps.
+     */
+    public Object getPojo() { return _value; }
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        POJONode other = (POJONode) o;
+        if (_value == null) {
+            return other._value == null;
+        }
+        return _value.equals(other._value);
+    }
+
+    @Override
+    public int hashCode() { return _value.hashCode(); }
+
+    @Override
+    public String toString()
+    {
+        return String.valueOf(_value);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ShortNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ShortNode.java
new file mode 100644
index 0000000..f7fc62a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ShortNode.java
@@ -0,0 +1,109 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+
+/**
+ * Numeric node that contains simple 16-bit integer values.
+ */
+public final class ShortNode
+    extends NumericNode
+{
+    final short _value;
+
+    /* 
+    ************************************************
+    * Construction
+    ************************************************
+    */
+
+    public ShortNode(short v) { _value = v; }
+
+    public static ShortNode valueOf(short l) { return new ShortNode(l); }
+
+    /* 
+    ************************************************
+    * Overridden JsonNode methods
+    ************************************************
+    */
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_NUMBER_INT; }
+
+    @Override
+    public JsonParser.NumberType numberType() { return JsonParser.NumberType.INT; }			// should be SHORT
+
+
+    @Override
+    public boolean isIntegralNumber() { return true; }
+
+    @Override
+    public boolean isShort() { return true; }
+
+    @Override public boolean canConvertToInt() { return true; }
+    @Override public boolean canConvertToLong() { return true; }
+    
+    @Override
+    public Number numberValue() {
+        return Short.valueOf(_value);
+    }
+
+    @Override
+    public short shortValue() { return _value; }
+
+    @Override
+    public int intValue() { return _value; }
+
+    @Override
+    public long longValue() { return _value; }
+
+    @Override
+    public float floatValue() { return _value; }
+
+    @Override
+    public double doubleValue() { return _value; }
+
+    @Override
+    public BigDecimal decimalValue() { return BigDecimal.valueOf(_value); }
+
+    @Override
+    public BigInteger bigIntegerValue() { return BigInteger.valueOf(_value); }
+
+    @Override
+    public String asText() {
+        return NumberOutput.toString(_value);
+    }
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        return _value != 0;
+    }
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jg.writeNumber(_value);
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((ShortNode) o)._value == _value;
+    }
+
+    @Override
+    public int hashCode() {
+        return _value;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
new file mode 100644
index 0000000..053861e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/TextNode.java
@@ -0,0 +1,301 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+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;
+
+
+/**
+ * Value node that contains a text value.
+ */
+public final class TextNode
+    extends ValueNode
+{
+    final static int INT_SPACE = ' ';
+
+    final static TextNode EMPTY_STRING_NODE = new TextNode("");
+
+    final String _value;
+
+    public TextNode(String v) { _value = v; }
+
+    /**
+     * Factory method that should be used to construct instances.
+     * For some common cases, can reuse canonical instances: currently
+     * this is the case for empty Strings, in future possible for
+     * others as well. If null is passed, will return null.
+     *
+     * @return Resulting {@link TextNode} object, if <b>v</b>
+     *   is NOT null; null if it is.
+     */
+    public static TextNode valueOf(String v)
+    {
+        if (v == null) {
+            return null;
+        }
+        if (v.length() == 0) {
+            return EMPTY_STRING_NODE;
+        }
+        return new TextNode(v);
+    }
+
+    @Override
+    public JsonNodeType getNodeType()
+    {
+        return JsonNodeType.STRING;
+    }
+
+    @Override public JsonToken asToken() { return JsonToken.VALUE_STRING; }
+
+    @Override
+    public String textValue() {
+        return _value;
+    }
+
+    /**
+     * Method for accessing textual contents assuming they were
+     * base64 encoded; if so, they are decoded and resulting binary
+     * data is returned.
+     */
+    public byte[] getBinaryValue(Base64Variant b64variant)
+        throws IOException
+    {
+        @SuppressWarnings("resource")
+        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_SPACE);
+            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);
+            }
+        }
+        return builder.toByteArray();
+    }
+
+    @Override
+    public byte[] binaryValue() throws IOException
+    {
+        return getBinaryValue(Base64Variants.getDefaultVariant());
+    }
+    
+    /* 
+    /**********************************************************
+    /* General type coercions
+    /**********************************************************
+     */
+
+    @Override
+    public String asText() {
+        return _value;
+    }
+
+    // note: neither fast nor elegant, but these work for now:
+
+    @Override
+    public boolean asBoolean(boolean defaultValue) {
+        if (_value != null) {
+            if ("true".equals(_value.trim())) {
+                return true;
+            }
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public int asInt(int defaultValue) {
+        return NumberInput.parseAsInt(_value, defaultValue);
+    }
+
+    @Override
+    public long asLong(long defaultValue) {
+        return NumberInput.parseAsLong(_value, defaultValue);
+    }
+    
+    @Override
+    public double asDouble(double defaultValue) {
+        return NumberInput.parseAsDouble(_value, defaultValue);
+    }
+    
+    /* 
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public final void serialize(JsonGenerator jg, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        if (_value == null) {
+            jg.writeNull();
+        } else {
+            jg.writeString(_value);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+    
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) { // final class, can do this
+            return false;
+        }
+        return ((TextNode) o)._value.equals(_value);
+    }
+    
+    @Override
+    public int hashCode() { return _value.hashCode(); }
+
+    /**
+     * Different from other values, Strings need quoting
+     */
+    @Override
+    public String toString()
+    {
+        int len = _value.length();
+        len = len + 2 + (len >> 4);
+        StringBuilder sb = new StringBuilder(len);
+        appendQuoted(sb, _value);
+        return sb.toString();
+    }
+
+    protected static void appendQuoted(StringBuilder sb, String content)
+    {
+        sb.append('"');
+        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 <= INT_SPACE) {
+            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(base, JsonLocation.NA);
+    }
+
+    protected void _reportBase64EOF()
+        throws JsonParseException
+    {
+        throw new JsonParseException("Unexpected end-of-String when base64 content", JsonLocation.NA);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java
new file mode 100644
index 0000000..5a5931c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java
@@ -0,0 +1,410 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserMinimalBase;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+/**
+ * Facade over {@link JsonNode} that implements {@link JsonParser} to allow
+ * accessing contents of JSON tree in alternate form (stream of tokens).
+ * Useful when a streaming source is expected by code, such as data binding
+ * functionality.
+ * 
+ * @author tatu
+ */
+public class TreeTraversingParser extends ParserMinimalBase
+{
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Traversal context within tree
+     */
+    protected NodeCursor _nodeCursor;
+
+    /*
+    /**********************************************************
+    /* State
+    /**********************************************************
+     */
+
+    /**
+     * Sometimes parser needs to buffer a single look-ahead token; if so,
+     * it'll be stored here. This is currently used for handling 
+     */
+    protected JsonToken _nextToken;
+
+    /**
+     * Flag needed to handle recursion into contents of child
+     * Array/Object nodes.
+     */
+    protected boolean _startContainer;
+    
+    /**
+     * Flag that indicates whether parser is closed or not. Gets
+     * set when parser is either closed by explicit call
+     * ({@link #close}) or when end-of-input is reached.
+     */
+    protected boolean _closed;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public TreeTraversingParser(JsonNode n) { this(n, null); }
+
+    public TreeTraversingParser(JsonNode n, ObjectCodec codec)
+    {
+        super(0);
+        _objectCodec = codec;
+        if (n.isArray()) {
+            _nextToken = JsonToken.START_ARRAY;
+            _nodeCursor = new NodeCursor.Array(n, null);
+        } else if (n.isObject()) {
+            _nextToken = JsonToken.START_OBJECT;
+            _nodeCursor = new NodeCursor.Object(n, null);
+        } else { // value node
+            _nodeCursor = new NodeCursor.RootValue(n, null);
+        }
+    }
+
+    @Override
+    public void setCodec(ObjectCodec c) {
+        _objectCodec = c;
+    }
+
+    @Override
+    public ObjectCodec getCodec() {
+        return _objectCodec;
+    }
+
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+    
+    /*
+    /**********************************************************
+    /* Closeable implementation
+    /**********************************************************
+     */
+
+    @Override
+    public void close() throws IOException
+    {
+        if (!_closed) {
+            _closed = true;
+            _nodeCursor = null;
+            _currToken = null;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, traversal
+    /**********************************************************
+     */
+
+    @Override
+    public JsonToken nextToken() throws IOException, JsonParseException
+    {
+        if (_nextToken != null) {
+            _currToken = _nextToken;
+            _nextToken = null;
+            return _currToken;
+        }
+        // are we to descend to a container child?
+        if (_startContainer) {
+            _startContainer = false;
+            // minor optimization: empty containers can be skipped
+            if (!_nodeCursor.currentHasChildren()) {
+                _currToken = (_currToken == JsonToken.START_OBJECT) ?
+                    JsonToken.END_OBJECT : JsonToken.END_ARRAY;
+                return _currToken;
+            }
+            _nodeCursor = _nodeCursor.iterateChildren();
+            _currToken = _nodeCursor.nextToken();
+            if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
+                _startContainer = true;
+            }
+            return _currToken;
+        }
+        // No more content?
+        if (_nodeCursor == null) {
+            _closed = true; // if not already set
+            return null;
+        }
+        // Otherwise, next entry from current cursor
+        _currToken = _nodeCursor.nextToken();
+        if (_currToken != null) {
+            if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
+                _startContainer = true;
+            }
+            return _currToken;
+        }
+        // null means no more children; need to return end marker
+        _currToken = _nodeCursor.endToken();
+        _nodeCursor = _nodeCursor.getParent();
+        return _currToken;
+    }
+    
+    // default works well here:
+    //public JsonToken nextValue() throws IOException, JsonParseException
+
+    @Override
+    public JsonParser skipChildren() throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.START_OBJECT) {
+            _startContainer = false;
+            _currToken = JsonToken.END_OBJECT;
+        } else if (_currToken == JsonToken.START_ARRAY) {
+            _startContainer = false;
+            _currToken = JsonToken.END_ARRAY;
+        }
+        return this;
+    }
+
+    @Override
+    public boolean isClosed() {
+        return _closed;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, token accessors
+    /**********************************************************
+     */
+
+    @Override
+    public String getCurrentName() {
+        return (_nodeCursor == null) ? null : _nodeCursor.getCurrentName();
+    }
+
+    @Override
+    public void overrideCurrentName(String name)
+    {
+        if (_nodeCursor != null) {
+            _nodeCursor.overrideCurrentName(name);
+        }
+    }
+    
+    @Override
+    public JsonStreamContext getParsingContext() {
+        return _nodeCursor;
+    }
+
+    @Override
+    public JsonLocation getTokenLocation() {
+        return JsonLocation.NA;
+    }
+
+    @Override
+    public JsonLocation getCurrentLocation() {
+        return JsonLocation.NA;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, access to textual content
+    /**********************************************************
+     */
+
+    @Override
+    public String getText()
+    {
+        if (_closed) {
+            return null;
+        }
+        // need to separate handling a bit...
+        switch (_currToken) {
+        case FIELD_NAME:
+            return _nodeCursor.getCurrentName();
+        case VALUE_STRING:
+            return currentNode().textValue();
+        case VALUE_NUMBER_INT:
+        case VALUE_NUMBER_FLOAT:
+            return String.valueOf(currentNode().numberValue());
+        case VALUE_EMBEDDED_OBJECT:
+            JsonNode n = currentNode();
+            if (n != null && n.isBinary()) {
+                // this will convert it to base64
+                return n.asText();
+            }
+        default:
+        	return (_currToken == null) ? null : _currToken.asString();
+        }
+    }
+
+    @Override
+    public char[] getTextCharacters() throws IOException, JsonParseException {
+        return getText().toCharArray();
+    }
+
+    @Override
+    public int getTextLength() throws IOException, JsonParseException {
+        return getText().length();
+    }
+
+    @Override
+    public int getTextOffset() throws IOException, JsonParseException {
+        return 0;
+    }
+
+    @Override
+    public boolean hasTextCharacters() {
+        // generally we do not have efficient access as char[], hence:
+        return false;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, typed non-text access
+    /**********************************************************
+     */
+
+    //public byte getByteValue() throws IOException, JsonParseException
+
+    @Override
+    public NumberType getNumberType() throws IOException, JsonParseException {
+        JsonNode n = currentNumericNode();
+        return (n == null) ? null : n.numberType();
+    }
+
+    @Override
+    public BigInteger getBigIntegerValue() throws IOException, JsonParseException
+    {
+        return currentNumericNode().bigIntegerValue();
+    }
+
+    @Override
+    public BigDecimal getDecimalValue() throws IOException, JsonParseException {
+        return currentNumericNode().decimalValue();
+    }
+
+    @Override
+    public double getDoubleValue() throws IOException, JsonParseException {
+        return currentNumericNode().doubleValue();
+    }
+
+    @Override
+    public float getFloatValue() throws IOException, JsonParseException {
+        return (float) currentNumericNode().doubleValue();
+    }
+
+    @Override
+    public long getLongValue() throws IOException, JsonParseException {
+        return currentNumericNode().longValue();
+    }
+
+    @Override
+    public int getIntValue() throws IOException, JsonParseException {
+        return currentNumericNode().intValue();
+    }
+
+    @Override
+    public Number getNumberValue() throws IOException, JsonParseException {
+        return currentNumericNode().numberValue();
+    }
+
+    @Override
+    public Object getEmbeddedObject()
+    {
+        if (!_closed) {
+            JsonNode n = currentNode();
+            if (n != null) {
+                if (n.isPojo()) {
+                    return ((POJONode) n).getPojo();
+                }
+                if (n.isBinary()) {
+                    return ((BinaryNode) n).binaryValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, typed binary (base64) access
+    /**********************************************************
+     */
+
+    @Override
+    public byte[] getBinaryValue(Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        // Multiple possibilities...
+        JsonNode n = currentNode();
+        if (n != null) { // binary node?
+            byte[] data = n.binaryValue();
+            // (or TextNode, which can also convert automatically!)
+            if (data != null) {
+                return data;
+            }
+            // Or maybe byte[] as POJO?
+            if (n.isPojo()) {
+                Object ob = ((POJONode) n).getPojo();
+                if (ob instanceof byte[]) {
+                    return (byte[]) ob;
+                }
+            }
+        }
+        // otherwise return null to mark we have no binary content
+        return null;
+    }
+
+
+    @Override
+    public int readBinaryValue(Base64Variant b64variant, OutputStream out)
+            throws IOException, JsonParseException
+    {
+        byte[] data = getBinaryValue(b64variant);
+        if (data != null) {
+            out.write(data, 0, data.length);
+            return data.length;
+        }
+        return 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected JsonNode currentNode() {
+        if (_closed || _nodeCursor == null) {
+            return null;
+        }
+        return _nodeCursor.currentNode();
+    }
+
+    protected JsonNode currentNumericNode()
+        throws JsonParseException
+    {
+        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");
+        }
+        return n;
+    }
+
+    @Override
+    protected void _handleEOF() throws JsonParseException {
+        _throwInternal(); // should never get called
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java
new file mode 100644
index 0000000..9865a3c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/ValueNode.java
@@ -0,0 +1,141 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.util.List;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * This intermediate base class is used for all leaf nodes, that is,
+ * all non-container (array or object) nodes, except for the
+ * "missing node".
+ */
+public abstract class ValueNode
+    extends BaseJsonNode
+{
+    protected ValueNode() { }
+
+    /**
+     * All current value nodes are immutable, so we can just return
+     * them as is.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends JsonNode> T deepCopy() { return (T) this; }
+    
+    @Override public abstract JsonToken asToken();
+
+    @Override
+    public void serializeWithType(JsonGenerator jg, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        typeSer.writeTypePrefixForScalar(this, jg);
+        serialize(jg, provider);
+        typeSer.writeTypeSuffixForScalar(this, jg);
+    }
+
+    /*
+    /**********************************************************************
+    /* Base impls for standard methods
+    /**********************************************************************
+     */
+
+    @Override
+    public String toString() { return asText(); }
+
+    /*
+     **********************************************************************
+     * Navigation methods
+     **********************************************************************
+     */
+
+    @Override
+    public final JsonNode get(int index)
+    {
+        return null;
+    }
+
+    @Override
+    public final JsonNode path(int index)
+    {
+        return MissingNode.getInstance();
+    }
+
+    @Override
+    public final boolean has(int index)
+    {
+        return false;
+    }
+
+    @Override
+    public final boolean hasNonNull(int index)
+    {
+        return false;
+    }
+
+    @Override
+    public final JsonNode get(String fieldName)
+    {
+        return null;
+    }
+
+    @Override
+    public final JsonNode path(String fieldName)
+    {
+        return MissingNode.getInstance();
+    }
+
+    @Override
+    public final boolean has(String fieldName)
+    {
+        return false;
+    }
+
+    @Override
+    public final boolean hasNonNull(String fieldName)
+    {
+        return false;
+    }
+
+    /*
+     **********************************************************************
+     * Find methods: all "leaf" nodes return the same for these
+     **********************************************************************
+     */
+
+    @Override
+    public final JsonNode findValue(String fieldName)
+    {
+        return null;
+    }
+
+    // note: co-variant return type
+    @Override
+    public final ObjectNode findParent(String fieldName)
+    {
+        return null;
+    }
+
+    @Override
+    public final List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar)
+    {
+        return foundSoFar;
+    }
+
+    @Override
+    public final List<String> findValuesAsText(String fieldName, List<String> foundSoFar)
+    {
+        return foundSoFar;
+    }
+
+    @Override
+    public final List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar)
+    {
+        return foundSoFar;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/node/package-info.java b/src/main/java/com/fasterxml/jackson/databind/node/package-info.java
new file mode 100644
index 0000000..7732ebc
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/node/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Contains concrete {@link com.fasterxml.jackson.databind.JsonNode} implementations
+ * Jackson uses for the Tree model.
+ * These classes are public since concrete type will be needed
+ * for most operations that modify node trees. For read-only access concrete
+ * types are usually not needed.
+ */
+package com.fasterxml.jackson.databind.node;
diff --git a/src/main/java/com/fasterxml/jackson/databind/package-info.java b/src/main/java/com/fasterxml/jackson/databind/package-info.java
new file mode 100644
index 0000000..70d19f8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/package-info.java
@@ -0,0 +1,34 @@
+/**
+Contains basic mapper (conversion) functionality that
+allows for converting between regular streaming json content and
+Java objects (beans or Tree Model: support for both is via
+{@link com.fasterxml.jackson.databind.ObjectMapper} class, as well
+as convenience methods included in
+{@link com.fasterxml.jackson.core.JsonParser}
+<p>
+Object mapper will convert Json content to ant from
+basic Java wrapper types (Integer, Boolean, Double),
+Collection types (List, Map), Java Beans,
+Strings and nulls.
+<p>
+Tree mapper builds dynamically typed tree of {@link com.fasterxml.jackson.databind.JsonNode}s
+from JSON content (and writes such trees as JSON),
+similar to how DOM model works with XML.
+Main benefits over Object mapping are:
+<ul>
+ <li>No null checks are needed (dummy
+nodes are created as necessary to represent "missing" Object fields
+and Array elements)
+  </li>
+ <li>No type casts are usually needed: all public access methods are defined
+in basic <code>JsonNode</code> class, and when "incompatible" method (such as Array
+element access on, say, Boolean node) is used, returned node is
+virtual "missing" node.
+  </li>
+</ul>
+Because of its dynamic nature, Tree mapping is often convenient
+for basic path access and tree navigation, where structure of
+the resulting tree is known in advance.
+*/
+
+package com.fasterxml.jackson.databind;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
new file mode 100644
index 0000000..787fcee
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/AnyGetterWriter.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.ser.std.MapSerializer;
+
+/**
+ * Class similar to {@link BeanPropertyWriter}, but that will be used
+ * for serializing {@link com.fasterxml.jackson.annotation.JsonAnyGetter} annotated
+ * (Map) properties
+ */
+public class AnyGetterWriter
+{
+    protected final BeanProperty _property;
+
+    /**
+     * Method (or field) that represents the "any getter"
+     */
+    protected final AnnotatedMember _accessor;
+    
+    protected MapSerializer _serializer;
+    
+    public AnyGetterWriter(BeanProperty property,
+            AnnotatedMember accessor, MapSerializer serializer)
+    {
+        _accessor = accessor;
+        _property = property;
+        _serializer = serializer;
+    }
+
+    public void getAndSerialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws Exception
+    {
+        Object value = _accessor.getValue(bean);
+        if (value == null) {
+            return;
+        }
+        if (!(value instanceof Map<?,?>)) {
+            throw new JsonMappingException("Value returned by 'any-getter' ("
+                    +_accessor.getName()+"()) not java.util.Map but "+value.getClass().getName());
+        }
+        _serializer.serializeFields((Map<?,?>) value, jgen, provider);
+    }
+
+    // Note: NOT part of ResolvableSerializer...
+    public void resolve(SerializerProvider provider) throws JsonMappingException
+    {
+        _serializer = (MapSerializer) _serializer.createContextual(provider, _property);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
new file mode 100644
index 0000000..82cb60d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BasicSerializerFactory.java
@@ -0,0 +1,999 @@
+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.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
+import com.fasterxml.jackson.databind.ext.OptionalHandlerFactory;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.impl.IndexedStringListSerializer;
+import com.fasterxml.jackson.databind.ser.impl.StringArraySerializer;
+import com.fasterxml.jackson.databind.ser.impl.StringCollectionSerializer;
+import com.fasterxml.jackson.databind.ser.std.*;
+import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.Converter;
+import com.fasterxml.jackson.databind.util.EnumValues;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Factory class that can provide serializers for standard JDK classes,
+ * as well as custom classes that extend standard classes or implement
+ * one of "well-known" interfaces (such as {@link java.util.Collection}).
+ *<p>
+ * Since all the serializers are eagerly instantiated, and there is
+ * no additional introspection or customizability of these types,
+ * this factory is essentially stateless.
+ */
+ at SuppressWarnings("serial")
+public abstract class BasicSerializerFactory
+    extends SerializerFactory
+    implements java.io.Serializable
+{
+    /*
+    /**********************************************************
+    /* Configuration, lookup tables/maps
+    /**********************************************************
+     */
+
+    /**
+     * Since these are all JDK classes, we shouldn't have to worry
+     * about ClassLoader used to load them. Rather, we can just
+     * use the class name, and keep things simple and efficient.
+     */
+    protected final static HashMap<String, JsonSerializer<?>> _concrete =
+        new HashMap<String, JsonSerializer<?>>();
+    
+    /**
+     * Actually it may not make much sense to eagerly instantiate all
+     * kinds of serializers: so this Map actually contains class references,
+     * not instances
+     */
+    protected final static HashMap<String, Class<? extends JsonSerializer<?>>> _concreteLazy =
+        new HashMap<String, Class<? extends JsonSerializer<?>>>();
+    
+    static {
+        /* String and string-like types (note: date types explicitly
+         * not included -- can use either textual or numeric serialization)
+         */
+        _concrete.put(String.class.getName(), new StringSerializer());
+        final ToStringSerializer sls = ToStringSerializer.instance;
+        _concrete.put(StringBuffer.class.getName(), sls);
+        _concrete.put(StringBuilder.class.getName(), sls);
+        _concrete.put(Character.class.getName(), sls);
+        _concrete.put(Character.TYPE.getName(), sls);
+
+        // Primitives/wrappers for primitives (primitives needed for Beans)
+        NumberSerializers.addAll(_concrete);
+        _concrete.put(Boolean.TYPE.getName(), new BooleanSerializer(true));
+        _concrete.put(Boolean.class.getName(), new BooleanSerializer(false));
+
+        // Other numbers, more complicated
+        final JsonSerializer<?> ns = new NumberSerializers.NumberSerializer();
+        _concrete.put(BigInteger.class.getName(), ns);
+        _concrete.put(BigDecimal.class.getName(), ns);
+
+        // Other discrete non-container types:
+        // First, Date/Time zoo:
+        _concrete.put(Calendar.class.getName(), CalendarSerializer.instance);
+        DateSerializer dateSer = DateSerializer.instance;
+        _concrete.put(java.util.Date.class.getName(), dateSer);
+        // note: timestamps are very similar to java.util.Date, thus serialized as such
+        _concrete.put(java.sql.Timestamp.class.getName(), dateSer);
+        
+        // leave some of less commonly used ones as lazy, no point in proactive construction
+        _concreteLazy.put(java.sql.Date.class.getName(), SqlDateSerializer.class);
+        _concreteLazy.put(java.sql.Time.class.getName(), SqlTimeSerializer.class);
+
+        // And then other standard non-structured JDK types
+        for (Map.Entry<Class<?>,Object> en : StdJdkSerializers.all()) {
+            Object value = en.getValue();
+            if (value instanceof JsonSerializer<?>) {
+                _concrete.put(en.getKey().getName(), (JsonSerializer<?>) value);
+            } else if (value instanceof Class<?>) {
+                @SuppressWarnings("unchecked")
+                Class<? extends JsonSerializer<?>> cls = (Class<? extends JsonSerializer<?>>) value;
+                _concreteLazy.put(en.getKey().getName(), cls);
+            } else { // should never happen, but:
+                throw new IllegalStateException("Internal error: unrecognized value of type "+en.getClass().getName());
+            }
+        }
+
+        // Jackson-specific type(s)
+        // (Q: can this ever be sub-classed?)
+        _concreteLazy.put(TokenBuffer.class.getName(), TokenBufferSerializer.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+    
+    /**
+     * Configuration settings for this factory; immutable instance (just like this
+     * factory), new version created via copy-constructor (fluent-style)
+     */
+    protected final SerializerFactoryConfig _factoryConfig;
+
+    /*
+    /**********************************************************
+    /* Life cycle
+    /**********************************************************
+     */
+
+    /**
+     * We will provide default constructor to allow sub-classing,
+     * but make it protected so that no non-singleton instances of
+     * the class will be instantiated.
+     */
+    protected BasicSerializerFactory(SerializerFactoryConfig config) {
+        _factoryConfig = (config == null) ? new SerializerFactoryConfig() : config;
+    }
+    
+    /**
+     * Method for getting current {@link SerializerFactoryConfig}.
+      *<p>
+     * Note that since instances are immutable, you can NOT change settings
+     * by accessing an instance and calling methods: this will simply create
+     * new instance of config object.
+     */
+    public SerializerFactoryConfig getFactoryConfig() {
+        return _factoryConfig;
+    }
+
+    /**
+     * Method used for creating a new instance of this factory, but with different
+     * configuration. Reason for specifying factory method (instead of plain constructor)
+     * is to allow proper sub-classing of factories.
+     *<p>
+     * Note that custom sub-classes generally <b>must override</b> implementation
+     * of this method, as it usually requires instantiating a new instance of
+     * factory type. Check out javadocs for
+     * {@link com.fasterxml.jackson.databind.ser.BeanSerializerFactory} for more details.
+     */
+    public abstract SerializerFactory withConfig(SerializerFactoryConfig config);
+
+    /**
+     * Convenience method for creating a new factory instance with an additional
+     * serializer provider.
+     */
+    @Override
+    public final SerializerFactory withAdditionalSerializers(Serializers additional) {
+        return withConfig(_factoryConfig.withAdditionalSerializers(additional));
+    }
+
+    /**
+     * Convenience method for creating a new factory instance with an additional
+     * key serializer provider.
+     */
+    @Override
+    public final SerializerFactory withAdditionalKeySerializers(Serializers additional) {
+        return withConfig(_factoryConfig.withAdditionalKeySerializers(additional));
+    }
+    
+    /**
+     * Convenience method for creating a new factory instance with additional bean
+     * serializer modifier.
+     */
+    @Override
+    public final SerializerFactory withSerializerModifier(BeanSerializerModifier modifier) {
+        return withConfig(_factoryConfig.withSerializerModifier(modifier));
+    }
+
+    /*
+    /**********************************************************
+    /* SerializerFactory impl
+    /**********************************************************
+     */
+    
+    // Implemented by sub-classes
+    @Override
+    public abstract JsonSerializer<Object> createSerializer(SerializerProvider prov,
+            JavaType type)
+        throws JsonMappingException;
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public JsonSerializer<Object> createKeySerializer(SerializationConfig config,
+            JavaType keyType, JsonSerializer<Object> defaultImpl)
+    {
+        // We should not need any member method info; at most class annotations for Map type
+        BeanDescription beanDesc = config.introspectClassAnnotations(keyType.getRawClass());
+        JsonSerializer<?> ser = null;
+        // Minor optimization: to avoid constructing beanDesc, bail out if none registered
+        if (_factoryConfig.hasKeySerializers()) {
+            // Only thing we have here are module-provided key serializers:
+            for (Serializers serializers : _factoryConfig.keySerializers()) {
+                ser = serializers.findSerializer(config, keyType, beanDesc);
+                if (ser != null) {
+                    break;
+                }
+            }
+        }
+        if (ser == null) {
+            ser = defaultImpl;
+            if (ser == null) {
+                ser = StdKeySerializers.getStdKeySerializer(keyType);
+            }
+        }
+        
+        // [Issue#120]: Allow post-processing
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                ser = mod.modifyKeySerializer(config, keyType, beanDesc, ser);
+            }
+        }
+        return (JsonSerializer<Object>) ser;
+    }
+    
+    /**
+     * Method called to construct a type serializer for values with given declared
+     * base type. This is called for values other than those of bean property
+     * types.
+     */
+    @Override
+    public TypeSerializer createTypeSerializer(SerializationConfig config,
+            JavaType baseType)
+    {
+        BeanDescription bean = config.introspectClassAnnotations(baseType.getRawClass());
+        AnnotatedClass ac = bean.getClassInfo();
+        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.
+         */
+        Collection<NamedType> subtypes = null;
+        if (b == null) {
+            b = config.getDefaultTyper(baseType);
+        } else {
+            subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(ac, config, ai);
+        }
+        if (b == null) {
+            return null;
+        }
+        return b.buildTypeSerializer(config, baseType, subtypes);
+    }
+
+    /*
+    /**********************************************************
+    /* Additional API for other core classes
+    /**********************************************************
+     */
+
+    public final JsonSerializer<?> getNullSerializer() {
+        return NullSerializer.instance;
+    }    
+
+    protected abstract Iterable<Serializers> customSerializers();
+    
+    /*
+    /**********************************************************
+    /* Overridable secondary serializer accessor methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will use fast lookup (and identity comparison) methods to
+     * see if we know serializer to use for given type.
+     */
+    protected final JsonSerializer<?> findSerializerByLookup(JavaType type,
+            SerializationConfig config, BeanDescription beanDesc,
+            boolean staticTyping)
+    {
+        Class<?> raw = type.getRawClass();
+        String clsName = raw.getName();
+        JsonSerializer<?> ser = _concrete.get(clsName);
+        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);
+                }
+            }
+        }
+        return ser;
+    }
+
+    /**
+     * Method called to see if one of primary per-class annotations
+     * (or related, like implementing of {@link JsonSerializable})
+     * determines the serializer to use.
+     *<p>
+     * Currently handles things like:
+     *<ul>
+     * <li>If type implements {@link JsonSerializable}, use that
+     *  </li>
+     * <li>If type has {@link com.fasterxml.jackson.annotation.JsonValue} annotation (or equivalent), build serializer
+     *    based on that property
+     *  </li>
+     *</ul>
+     *
+     * @since 2.0
+     */
+    protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider prov, 
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+        // First: JsonSerializable?
+        if (JsonSerializable.class.isAssignableFrom(raw)) {
+            return SerializableSerializer.instance;
+        }
+        // Second: @JsonValue for any type
+        AnnotatedMethod valueMethod = beanDesc.findJsonValueMethod();
+        if (valueMethod != null) {
+            Method m = valueMethod.getAnnotated();
+            if (prov.canOverrideAccessModifiers()) {
+                ClassUtil.checkAndFixAccess(m);
+            }
+            JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueMethod);
+            return new JsonValueSerializer(m, ser);
+        }
+        // No well-known annotations...
+        return null;
+    }
+    
+    /**
+     * Method for checking if we can determine serializer to use based on set of
+     * known primary types, checking for set of known base types (exact matches
+     * having been compared against with <code>findSerializerByLookup</code>).
+     * This does not include "secondary" interfaces, but
+     * mostly concrete or abstract base classes.
+     */
+    protected final JsonSerializer<?> findSerializerByPrimaryType(SerializerProvider prov, 
+            JavaType type, BeanDescription beanDesc,
+            boolean staticTyping)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+        // One unfortunate special case, as per [JACKSON-484]
+        if (InetAddress.class.isAssignableFrom(raw)) {
+            return InetAddressSerializer.instance;
+        }
+        // ... and another one, [JACKSON-522], for TimeZone
+        if (TimeZone.class.isAssignableFrom(raw)) {
+            return TimeZoneSerializer.instance;
+        }
+        // and yet one more [JACKSON-789]
+        if (java.nio.charset.Charset.class.isAssignableFrom(raw)) {
+            return ToStringSerializer.instance;
+        }
+        
+        // Then check for optional/external serializers [JACKSON-386]
+        JsonSerializer<?> ser = findOptionalStdSerializer(prov, type, beanDesc, staticTyping);
+        if (ser != null) {
+            return ser;
+        }
+        
+        if (Number.class.isAssignableFrom(raw)) {
+            return NumberSerializers.NumberSerializer.instance;
+        }
+        if (Enum.class.isAssignableFrom(raw)) {
+            return buildEnumSerializer(prov.getConfig(), type, beanDesc);
+        }
+        if (Calendar.class.isAssignableFrom(raw)) {
+            return CalendarSerializer.instance;
+        }
+        if (java.util.Date.class.isAssignableFrom(raw)) {
+            return DateSerializer.instance;
+        }
+        return null;
+    }
+
+    /**
+     * Overridable method called after checking all other types.
+     * 
+     * @since 2.2
+     */
+    protected JsonSerializer<?> findOptionalStdSerializer(SerializerProvider prov, 
+            JavaType type, BeanDescription beanDesc, boolean staticTyping)
+        throws JsonMappingException
+    {
+        return OptionalHandlerFactory.instance.findSerializer(prov.getConfig(), type, beanDesc);
+    }
+        
+    /**
+     * Reflection-based serialized find method, which checks if
+     * given class implements one of recognized "add-on" interfaces.
+     * Add-on here means a role that is usually or can be a secondary
+     * trait: for example,
+     * bean classes may implement {@link Iterable}, but their main
+     * function is usually something else. The reason for
+     */
+    protected final JsonSerializer<?> findSerializerByAddonType(SerializationConfig config,
+            JavaType javaType, BeanDescription beanDesc,
+            boolean staticTyping)
+        throws JsonMappingException
+    {
+        Class<?> type = javaType.getRawClass();
+
+        // These need to be in decreasing order of specificity...
+        if (Iterator.class.isAssignableFrom(type)) {
+            return buildIteratorSerializer(config, javaType, beanDesc, staticTyping);
+        }
+        if (Iterable.class.isAssignableFrom(type)) {
+            return buildIterableSerializer(config, javaType, beanDesc,  staticTyping);
+        }
+        if (CharSequence.class.isAssignableFrom(type)) {
+            return ToStringSerializer.instance;
+        }
+        return null;
+    }
+    
+    /**
+     * Helper method called to check if a class or method
+     * has an annotation
+     * (@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using)
+     * that tells the class to use for serialization.
+     * Returns null if no such annotation found.
+     */
+    @SuppressWarnings("unchecked")
+    protected JsonSerializer<Object> findSerializerFromAnnotation(SerializerProvider prov,
+            Annotated a)
+        throws JsonMappingException
+    {
+        Object serDef = prov.getAnnotationIntrospector().findSerializer(a);
+        if (serDef == null) {
+            return null;
+        }
+        JsonSerializer<Object> ser = prov.serializerInstance(a, serDef);
+        // One more thing however: may need to also apply a converter:
+        return (JsonSerializer<Object>) findConvertingSerializer(prov, a, ser);
+    }
+
+    /**
+     * Helper method that will check whether given annotated entity (usually class,
+     * but may also be a property accessor) indicates that a {@link Converter} is to
+     * be used; and if so, to construct and return suitable serializer for it.
+     * If not, will simply return given serializer as is.
+     */
+    protected JsonSerializer<?> findConvertingSerializer(SerializerProvider prov,
+            Annotated a, JsonSerializer<?> ser)
+        throws JsonMappingException
+    {
+        Converter<Object,Object> conv = findConverter(prov, a);
+        if (conv == null) {
+            return ser;
+        }
+        JavaType delegateType = conv.getOutputType(prov.getTypeFactory());
+        return new StdDelegatingSerializer(conv, delegateType, ser);
+    }
+
+    protected Converter<Object,Object> findConverter(SerializerProvider prov,
+            Annotated a)
+        throws JsonMappingException
+    {
+        Object convDef = prov.getAnnotationIntrospector().findSerializationConverter(a);
+        if (convDef == null) {
+            return null;
+        }
+        return prov.converterInstance(a, convDef);
+    }
+    
+    /*
+    /**********************************************************
+    /* Factory methods, container types:
+    /**********************************************************
+     */
+
+    /**
+     * Deprecated method; final to help identify problems with sub-classes,
+     * as this method will NOT be called any more in 2.1
+     * 
+     * @deprecated Since 2.1 (removed 'property' argument)
+     */
+    @Deprecated
+    protected final JsonSerializer<?> buildContainerSerializer(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc, BeanProperty property, boolean staticTyping)
+        throws JsonMappingException
+    {
+        return  buildContainerSerializer(prov, type, beanDesc, staticTyping);
+    }
+    
+    /**
+     * @since 2.1
+     */
+    protected JsonSerializer<?> buildContainerSerializer(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc, boolean staticTyping)
+        throws JsonMappingException
+    {
+        final SerializationConfig config = prov.getConfig();
+
+        /* [Issue#23], 15-Mar-2013, tatu: must force static handling of root value type,
+         *   with just one important exception: if value type is "untyped", let's
+         *   leave it as is; no clean way to make it work.
+         */
+        if (!staticTyping && type.useStaticType()) {
+            if (!type.isContainerType() || type.getContentType().getRawClass() != Object.class) {
+                staticTyping = true;
+            }
+        }
+        
+        // Let's see what we can learn about element/content/value type, type serializer for it:
+        JavaType elementType = type.getContentType();
+        TypeSerializer elementTypeSerializer = createTypeSerializer(config,
+                elementType);
+
+        // if elements have type serializer, can not force static typing:
+        if (elementTypeSerializer != null) {
+            staticTyping = false;
+        }
+        JsonSerializer<Object> elementValueSerializer = _findContentSerializer(prov,
+                beanDesc.getClassInfo());
+        
+        if (type.isMapLikeType()) { // implements java.util.Map
+            MapLikeType mlt = (MapLikeType) type;
+            /* 29-Sep-2012, tatu: This is actually too early to (try to) find
+             *  key serializer from property annotations, and can lead to caching
+             *  issues (see [Issue#75]). Instead, must be done from 'createContextual()' call.
+             *  But we do need to check class annotations.
+             */
+            JsonSerializer<Object> keySerializer = _findKeySerializer(prov, beanDesc.getClassInfo());
+            if (mlt.isTrueMapType()) {
+                return buildMapSerializer(config, (MapType) mlt, beanDesc, staticTyping,
+                        keySerializer, elementTypeSerializer, elementValueSerializer);
+            }
+            // Only custom serializers may be available:
+            for (Serializers serializers : customSerializers()) {
+                MapLikeType mlType = (MapLikeType) type;
+                JsonSerializer<?> ser = serializers.findMapLikeSerializer(config,
+                        mlType, beanDesc, keySerializer, elementTypeSerializer, elementValueSerializer);
+                if (ser != null) {
+                    // [Issue#120]: Allow post-processing
+                    if (_factoryConfig.hasSerializerModifiers()) {
+                        for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                            ser = mod.modifyMapLikeSerializer(config, mlType, beanDesc, ser);
+                        }
+                    }
+                    return ser;
+                }
+            }
+            return null;
+        }
+        if (type.isCollectionLikeType()) {
+            CollectionLikeType clt = (CollectionLikeType) type;
+            if (clt.isTrueCollectionType()) {
+                return buildCollectionSerializer(config,  (CollectionType) clt, beanDesc, staticTyping,
+                        elementTypeSerializer, elementValueSerializer);
+            }
+            CollectionLikeType clType = (CollectionLikeType) type;
+            // Only custom variants for this:
+            for (Serializers serializers : customSerializers()) {
+                JsonSerializer<?> ser = serializers.findCollectionLikeSerializer(config,
+                        clType, beanDesc, elementTypeSerializer, elementValueSerializer);
+                if (ser != null) {
+                    // [Issue#120]: Allow post-processing
+                    if (_factoryConfig.hasSerializerModifiers()) {
+                        for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                            ser = mod.modifyCollectionLikeSerializer(config, clType, beanDesc, ser);
+                        }
+                    }
+                    return ser;
+                }
+            }
+            // fall through either way (whether shape dictates serialization as POJO or not)
+            return null;
+        }
+        if (type.isArrayType()) {
+            return buildArraySerializer(config, (ArrayType) type, beanDesc, staticTyping,
+                    elementTypeSerializer, elementValueSerializer);
+        }
+        return null;
+    }
+
+    /**
+     * Deprecated method; final to help identify problems with sub-classes,
+     * as this method will NOT be called any more in 2.1
+     * 
+     * @deprecated Since 2.1
+     */
+    @Deprecated
+    protected final JsonSerializer<?> buildCollectionSerializer(SerializationConfig config,
+            CollectionType type,
+            BeanDescription beanDesc, BeanProperty property,
+            boolean staticTyping,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) 
+        throws JsonMappingException
+    {
+        return buildCollectionSerializer(config, type, beanDesc,
+                staticTyping, elementTypeSerializer, elementValueSerializer);
+    }
+    
+    /**
+     * Helper method that handles configuration details when constructing serializers for
+     * {@link java.util.List} types that support efficient by-index access
+     * 
+     * @since 2.1
+     */
+    protected JsonSerializer<?> buildCollectionSerializer(SerializationConfig config,
+            CollectionType type, BeanDescription beanDesc, boolean staticTyping,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer) 
+        throws JsonMappingException
+    {
+        JsonSerializer<?> ser = null;
+        // Module-provided custom collection serializers?
+        for (Serializers serializers : customSerializers()) {
+            ser = serializers.findCollectionSerializer(config,
+                    type, beanDesc, elementTypeSerializer, elementValueSerializer);
+            if (ser != null) {
+                break;
+            }
+        }
+
+        // As per [Issue#24], may want to use alternate shape, serialize as JSON Object.
+        // Challenge here is that EnumSerializer does not know how to produce
+        // POJO style serialization, so we must handle that special case separately;
+        // otherwise pass it to EnumSerializer.
+        if (ser == null) {
+            JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+            if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) {
+                return null;
+            }
+            Class<?> raw = type.getRawClass();
+            if (EnumSet.class.isAssignableFrom(raw)) {
+                // this may or may not be available (Class doesn't; type of field/method does)
+                JavaType enumType = type.getContentType();
+                // and even if nominally there is something, only use if it really is enum
+                if (!enumType.isEnumType()) {
+                    enumType = null;
+                }
+                ser = StdContainerSerializers.enumSetSerializer(enumType);
+            } else {
+                Class<?> elementRaw = type.getContentType().getRawClass();
+                if (isIndexedList(raw)) {
+                    if (elementRaw == String.class) {
+                        // [JACKSON-829] Must NOT use if we have custom serializer
+                        if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
+                            ser = IndexedStringListSerializer.instance;
+                        }
+                    } else {
+                        ser = StdContainerSerializers.indexedListSerializer(type.getContentType(), staticTyping,
+                            elementTypeSerializer, elementValueSerializer);
+                    }
+                } else if (elementRaw == String.class) {
+                    // [JACKSON-829] Must NOT use if we have custom serializer
+                    if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
+                        ser = StringCollectionSerializer.instance;
+                    }
+                }
+                if (ser == null) {
+                    ser = StdContainerSerializers.collectionSerializer(type.getContentType(), staticTyping,
+                            elementTypeSerializer, elementValueSerializer);
+                }
+            }
+        }
+        // [Issue#120]: Allow post-processing
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                ser = mod.modifyCollectionSerializer(config, type, beanDesc, ser);
+            }
+        }
+        return ser;
+    }
+    
+    protected boolean isIndexedList(Class<?> cls)
+    {
+        return RandomAccess.class.isAssignableFrom(cls);
+    }
+    
+    /*
+    /**********************************************************
+    /* Factory methods, for Maps
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method that handles configuration details when constructing serializers for
+     * {@link java.util.Map} types.
+     */
+    protected JsonSerializer<?> buildMapSerializer(SerializationConfig config,
+            MapType type, BeanDescription beanDesc,
+            boolean staticTyping, JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        throws JsonMappingException
+    {
+        JsonSerializer<?> ser = null;
+        for (Serializers serializers : customSerializers()) {
+            ser = serializers.findMapSerializer(config, type, beanDesc,
+                    keySerializer, elementTypeSerializer, elementValueSerializer);
+            if (ser != null) {
+                break;
+            }
+        }
+        if (ser == null) {
+            if (EnumMap.class.isAssignableFrom(type.getRawClass())) {
+                JavaType keyType = type.getKeyType();
+                // Need to find key enum values...
+                EnumValues enums = null;
+                if (keyType.isEnumType()) { // non-enum if we got it as type erased class (from instance)
+                    @SuppressWarnings("unchecked")
+                    Class<Enum<?>> enumClass = (Class<Enum<?>>) keyType.getRawClass();
+                    enums = EnumValues.construct(enumClass, config.getAnnotationIntrospector());
+                }
+                ser = new EnumMapSerializer(type.getContentType(), staticTyping, enums,
+                    elementTypeSerializer, elementValueSerializer);
+            } else {
+                ser = MapSerializer.construct(config.getAnnotationIntrospector().findPropertiesToIgnore(beanDesc.getClassInfo()),
+                    type, staticTyping, elementTypeSerializer,
+                    keySerializer, elementValueSerializer);
+            }
+        }
+        // [Issue#120]: Allow post-processing
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                ser = mod.modifyMapSerializer(config, type, beanDesc, ser);
+            }
+        }
+        return ser;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods, for Arrays
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method that handles configuration details when constructing serializers for
+     * <code>Object[]</code> (and subtypes, except for String).
+     */
+    protected JsonSerializer<?> buildArraySerializer(SerializationConfig config,
+            ArrayType type, BeanDescription beanDesc,
+            boolean staticTyping,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        throws JsonMappingException
+    {
+        JsonSerializer<?> ser = null;        
+         // Module-provided custom collection serializers?
+         for (Serializers serializers : customSerializers()) {
+             ser = serializers.findArraySerializer(config,
+                     type, beanDesc, elementTypeSerializer, elementValueSerializer);
+             if (ser != null) {
+                 break;
+             }
+         }
+         if (ser == null) {
+             Class<?> raw = type.getRawClass();
+             // Important: do NOT use standard serializers if non-standard element value serializer specified
+             if (elementValueSerializer == null || ClassUtil.isJacksonStdImpl(elementValueSerializer)) {
+                 if (String[].class == raw) {
+                     ser = StringArraySerializer.instance;
+                 } else {
+                     // other standard types?
+                     ser = StdArraySerializers.findStandardImpl(raw);
+                 }
+             }
+             if (ser == null) {
+                 ser = new ObjectArraySerializer(type.getContentType(), staticTyping, elementTypeSerializer,
+                         elementValueSerializer);
+             }
+         }
+         // [Issue#120]: Allow post-processing
+         if (_factoryConfig.hasSerializerModifiers()) {
+             for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                 ser = mod.modifyArraySerializer(config, type, beanDesc, ser);
+             }
+         }
+         return ser;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods, for non-container types
+    /**********************************************************
+     */
+
+    protected JsonSerializer<?> buildIteratorSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc,
+            boolean staticTyping)
+        throws JsonMappingException
+    {
+        // if there's generic type, it'll be the first contained type
+        JavaType valueType = type.containedType(0);
+        if (valueType == null) {
+            valueType = TypeFactory.unknownType();
+        }
+        TypeSerializer vts = createTypeSerializer(config, valueType);
+        return StdContainerSerializers.iteratorSerializer(valueType,
+                usesStaticTyping(config, beanDesc, vts), vts);
+    }
+    
+    protected JsonSerializer<?> buildIterableSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc,
+            boolean staticTyping)
+        throws JsonMappingException
+    {
+        // if there's generic type, it'll be the first contained type
+        JavaType valueType = type.containedType(0);
+        if (valueType == null) {
+            valueType = TypeFactory.unknownType();
+        }
+        TypeSerializer vts = createTypeSerializer(config, valueType);
+        return StdContainerSerializers.iterableSerializer(valueType,
+                usesStaticTyping(config, beanDesc, vts), vts);
+    }
+    
+    protected JsonSerializer<?> buildEnumSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        /* As per [Issue#24], may want to use alternate shape, serialize as JSON Object.
+         * Challenge here is that EnumSerializer does not know how to produce
+         * POJO style serialization, so we must handle that special case separately;
+         * otherwise pass it to EnumSerializer.
+         */
+        JsonFormat.Value format = beanDesc.findExpectedFormat(null);
+        if (format != null && format.getShape() == JsonFormat.Shape.OBJECT) {
+            // one special case: suppress serialization of "getDeclaringClass()"...
+            ((BasicBeanDescription) beanDesc).removeProperty("declaringClass");
+            // returning null will mean that eventually BeanSerializer gets constructed
+            return null;
+        }
+        @SuppressWarnings("unchecked")
+        Class<Enum<?>> enumClass = (Class<Enum<?>>) type.getRawClass();
+        JsonSerializer<?> ser = EnumSerializer.construct(enumClass, config, beanDesc, format);
+        // [Issue#120]: Allow post-processing
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                ser = mod.modifyEnumSerializer(config, type, beanDesc, ser);
+            }
+        }
+        return ser;
+    }
+
+    /*
+    /**********************************************************
+    /* Other helper methods
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method used to encapsulate details of annotation-based type coercion
+     */
+    @SuppressWarnings("unchecked")
+    protected <T extends JavaType> T modifyTypeByAnnotation(SerializationConfig config,
+            Annotated a, T type)
+    {
+        // first: let's check class for the instance itself:
+        Class<?> superclass = config.getAnnotationIntrospector().findSerializationType(a);
+        if (superclass != null) {
+            try {
+                type = (T) type.widenBy(superclass);
+            } catch (IllegalArgumentException iae) {
+                throw new IllegalArgumentException("Failed to widen type "+type+" with concrete-type annotation (value "+superclass.getName()+"), method '"+a.getName()+"': "+iae.getMessage());
+            }
+        }
+        return modifySecondaryTypesByAnnotation(config, a, type);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static <T extends JavaType> T modifySecondaryTypesByAnnotation(SerializationConfig config,
+            Annotated a, T type)
+    {
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        // then key class
+        if (type.isContainerType()) {
+            Class<?> keyClass = intr.findSerializationKeyType(a, type.getKeyType());
+            if (keyClass != null) {
+                // illegal to use on non-Maps
+                if (!(type instanceof MapType)) {
+                    throw new IllegalArgumentException("Illegal key-type annotation: type "+type+" is not a Map type");
+                }
+                try {
+                    type = (T) ((MapType) type).widenKey(keyClass);
+                } catch (IllegalArgumentException iae) {
+                    throw new IllegalArgumentException("Failed to narrow key type "+type+" with key-type annotation ("+keyClass.getName()+"): "+iae.getMessage());
+                }
+            }
+            
+            // and finally content class; only applicable to structured types
+            Class<?> cc = intr.findSerializationContentType(a, type.getContentType());
+            if (cc != null) {
+                try {
+                    type = (T) type.widenContentsBy(cc);
+                } catch (IllegalArgumentException iae) {
+                    throw new IllegalArgumentException("Failed to narrow content type "+type+" with content-type annotation ("+cc.getName()+"): "+iae.getMessage());
+                }
+            }
+        }
+        return type;
+    }
+
+    /**
+     * Helper method called to try to find whether there is an annotation in the
+     * class that indicates key serializer to use.
+     * If so, will try to instantiate key serializer and return it; otherwise returns null.
+     */
+    protected JsonSerializer<Object> _findKeySerializer(SerializerProvider prov,
+            Annotated a)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector intr = prov.getAnnotationIntrospector();
+        Object serDef = intr.findKeySerializer(a);
+        if (serDef != null) {
+            return prov.serializerInstance(a, serDef);
+        }
+        return null;
+    }
+
+    /**
+     * Helper method called to try to find whether there is an annotation in the
+     * class that indicates content ("value") serializer to use.
+     * If so, will try to instantiate key serializer and return it; otherwise returns null.
+     */
+    protected JsonSerializer<Object> _findContentSerializer(SerializerProvider prov,
+            Annotated a)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector intr = prov.getAnnotationIntrospector();
+        Object serDef = intr.findContentSerializer(a);
+        if (serDef != null) {
+            return prov.serializerInstance(a, serDef);
+        }
+        return null;
+    }
+
+    /**
+     * @deprecated Since 2.1: use method without 'property'
+     */
+    @Deprecated
+    protected final  boolean usesStaticTyping(SerializationConfig config,
+            BeanDescription beanDesc, TypeSerializer typeSer, BeanProperty property)
+    {
+        return usesStaticTyping(config, beanDesc, typeSer);
+    }
+    
+    /**
+     * Helper method to check whether global settings and/or class
+     * annotations for the bean class indicate that static typing
+     * (declared types)  should be used for properties.
+     * (instead of dynamic runtime types).
+     * 
+     * @since 2.1 (earlier had variant with additional 'property' parameter)
+     */
+    protected boolean usesStaticTyping(SerializationConfig config,
+            BeanDescription beanDesc, TypeSerializer typeSer)
+    {
+        /* 16-Aug-2010, tatu: If there is a (value) type serializer, we can not force
+         *    static typing; that would make it impossible to handle expected subtypes
+         */
+        if (typeSer != null) {
+            return false;
+        }
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        JsonSerialize.Typing t = intr.findSerializationTyping(beanDesc.getClassInfo());
+        if (t != null) {
+            return (t == JsonSerialize.Typing.STATIC);
+        }
+        return config.isEnabled(MapperFeature.USE_STATIC_TYPING);
+    }
+
+    protected Class<?> _verifyAsClass(Object src, String methodName, Class<?> noneClass)
+    {
+        if (src == null) {
+            return null;
+        }
+        if (!(src instanceof Class)) {
+            throw new IllegalStateException("AnnotationIntrospector."+methodName+"() returned value of type "+src.getClass().getName()+": expected type JsonSerializer or Class<JsonSerializer> instead");
+        }
+        Class<?> cls = (Class<?>) src;
+        if (cls == noneClass || cls == NoClass.class) {
+            return null;
+        }
+        return cls;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java
new file mode 100644
index 0000000..4b8979c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyFilter.java
@@ -0,0 +1,83 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Interface that defines API for filter objects use (as configured
+ * using {@link com.fasterxml.jackson.annotation.JsonFilter})
+ * for filtering bean properties to serialize.
+ *<p>
+ * Note that Jackson 2.1 added two new methods -- as a result, it is
+ * strongly recommended that custom implementations extend
+ * {@link com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter},
+ * to avoid backwards compatibility issues in future.
+ */
+public interface BeanPropertyFilter
+{
+    /**
+     * Method called by {@link BeanSerializer} to let filter decide what to do with
+     * given bean property value: the usual choices are to either filter out (i.e.
+     * do nothing) or write using given {@link BeanPropertyWriter}, although filters
+     * can choose other to do something different altogether.
+     * 
+     * @param bean Bean of which property value to serialize
+     * @param jgen Generator use for serializing value
+     * @param prov Provider that can be used for accessing dynamic aspects of serialization
+     *    processing
+     * @param writer Default bean property serializer to use
+     */
+    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov,
+            BeanPropertyWriter writer)
+        throws Exception;
+    
+    /**
+     * Method called by {@link BeanSerializer} to let the filter determine whether, and in what
+     * form the given property exist within the parent, or root, schema. Filters can omit
+     * adding the property to the node, or choose the form of the schema value for the property.
+     *<p>
+     * Typical implementation is something like:
+     *<pre>
+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(propertiesNode, provider);
+     * }
+     *</pre>
+     * 
+     * @param writer Bean property serializer to use to create schema value
+     * @param propertiesNode Node which the given property would exist within
+     * @param provider Provider that can be used for accessing dynamic aspects of serialization
+     * 	processing
+     * 
+     * @since 2.1
+     */
+    public void depositSchemaProperty(BeanPropertyWriter writer, ObjectNode propertiesNode,
+            SerializerProvider provider)
+        throws JsonMappingException;
+    
+    /**
+     * Method called by {@link BeanSerializer} to let the filter determine whether, and in what
+     * form the given property exist within the parent, or root, schema. Filters can omit
+     * adding the property to the node, or choose the form of the schema value for the property
+     *<p>
+     * Typical implementation is something like:
+     *<pre>
+     * if (include(writer)) {
+     *      writer.depositSchemaProperty(objectVisitor, provider);
+     * }
+     *</pre>
+     * 
+     * @param writer Bean property serializer to use to create schema value
+     * @param objectVisitor JsonObjectFormatVisitor which should be aware of 
+     * the property's existence
+     * @param provider Provider that can be used for accessing dynamic aspects of serialization
+     * 	processing
+     * 
+     * @since 2.1
+     */
+    public void depositSchemaProperty(BeanPropertyWriter writer, JsonObjectFormatVisitor objectVisitor,
+            SerializerProvider provider)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
new file mode 100644
index 0000000..32ce099
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.java
@@ -0,0 +1,714 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+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.impl.PropertySerializerMap;
+import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanPropertyWriter;
+import com.fasterxml.jackson.databind.util.Annotations;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Base bean property handler class, which implements common parts of
+ * reflection-based functionality for accessing a property value
+ * and serializing it.
+ *<p> 
+ * Note that current design tries to keep instances immutable (semi-functional
+ * style); mostly because these instances are exposed to application
+ * code and this is to reduce likelihood of data corruption and
+ * synchronization issues.
+ */
+public class BeanPropertyWriter
+    implements BeanProperty
+{
+    /**
+     * Marker object used to indicate "do not serialize if empty"
+     */
+    public final static Object MARKER_FOR_EMPTY = new Object();
+    
+    /*
+    /**********************************************************
+    /* Settings for accessing property value to serialize
+    /**********************************************************
+     */
+
+    /**
+     * Member (field, method) that represents property and allows access
+     * to associated annotations.
+     */
+    protected final AnnotatedMember _member;
+
+    /**
+     * Annotations from context (most often, class that declares property,
+     * or in case of sub-class serializer, from that sub-class)
+     */
+    protected final Annotations _contextAnnotations;
+    
+    /**
+     * Type property is declared to have, either in class definition 
+     * or associated annotations.
+     */
+    protected final JavaType _declaredType;
+    
+    /**
+     * Accessor method used to get property value, for
+     * method-accessible properties.
+     * Null if and only if {@link #_field} is null.
+     */
+    protected final Method _accessorMethod;
+    
+    /**
+     * Field that contains the property value for field-accessible
+     * properties.
+     * Null if and only if {@link #_accessorMethod} is null.
+     */
+    protected final Field _field;
+    
+    /*
+    /**********************************************************
+    /* Opaque internal data that bean serializer factory and
+    /* bean serializers can add.
+    /**********************************************************
+     */
+
+    protected HashMap<Object,Object> _internalSettings;
+    
+    /*
+    /**********************************************************
+    /* Serialization settings
+    /**********************************************************
+     */
+    
+    /**
+     * Logical name of the property; will be used as the field name
+     * under which value for the property is written.
+     */
+    protected final SerializedString _name;
+
+    /**
+     * Wrapper name to use for this element, if any
+     * 
+     * @since 2.2
+     */
+    protected final PropertyName _wrapperName;
+    
+    /**
+     * Type to use for locating serializer; normally same as return
+     * type of the accessor method, but may be overridden by annotations.
+     */
+    protected final JavaType _cfgSerializationType;
+
+    /**
+     * Serializer to use for writing out the value: null if it can not
+     * be known statically; non-null if it can.
+     */
+    protected JsonSerializer<Object> _serializer;
+
+    /**
+     * Serializer used for writing out null values, if any: if null,
+     * null values are to be suppressed.
+     */
+    protected JsonSerializer<Object> _nullSerializer;
+    
+    /**
+     * In case serializer is not known statically (i.e. <code>_serializer</code>
+     * is null), we will use a lookup structure for storing dynamically
+     * resolved mapping from type(s) to serializer(s).
+     */
+    protected PropertySerializerMap _dynamicSerializers;
+
+    /**
+     * Whether null values are to be suppressed (nothing written out if
+     * value is null) or not.
+     */
+    protected final boolean _suppressNulls;
+    
+    /**
+     * Value that is considered default value of the property; used for
+     * default-value-suppression if enabled.
+     */
+    protected final Object _suppressableValue;
+
+    /**
+     * Alternate set of property writers used when view-based filtering
+     * is available for the Bean.
+     */
+    protected final Class<?>[] _includeInViews;
+
+    /**
+     * If property being serialized needs type information to be
+     * included this is the type serializer to use.
+     * Declared type (possibly augmented with annotations) of property
+     * is used for determining exact mechanism to use (compared to
+     * actual runtime type used for serializing actual state).
+     */
+    protected TypeSerializer _typeSerializer;
+    
+    /**
+     * Base type of the property, if the declared type is "non-trivial";
+     * meaning it is either a structured type (collection, map, array),
+     * or parameterized. Used to retain type information about contained
+     * type, which is mostly necessary if type meta-data is to be
+     * included.
+     */
+    protected JavaType _nonTrivialBaseType;
+
+    /**
+     * Whether value of this property has been marked as required.
+     * Retained since it will be needed when traversing type hierarchy
+     * for producing schemas (and other similar tasks); currently not
+     * used for serialization.
+     * 
+     * @since 2.2
+     */
+    protected final boolean _isRequired;
+    
+    /*
+    /**********************************************************
+    /* Construction, configuration
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    public BeanPropertyWriter(BeanPropertyDefinition propDef,
+            AnnotatedMember member, Annotations contextAnnotations,
+            JavaType declaredType,
+            JsonSerializer<?> ser, TypeSerializer typeSer, JavaType serType,
+            boolean suppressNulls, Object suppressableValue)
+    {
+        
+        _member = member;
+        _contextAnnotations = contextAnnotations;
+        _name = new SerializedString(propDef.getName());
+        _wrapperName = propDef.getWrapperName();
+        _declaredType = declaredType;
+        _serializer = (JsonSerializer<Object>) ser;
+        _dynamicSerializers = (ser == null) ? PropertySerializerMap.emptyMap() : null;
+        _typeSerializer = typeSer;
+        _cfgSerializationType = serType;
+        _isRequired = propDef.isRequired();
+
+        if (member instanceof AnnotatedField) {
+            _accessorMethod = null;
+            _field = (Field) member.getMember();
+        } else if (member instanceof AnnotatedMethod) {
+            _accessorMethod = (Method) member.getMember();
+            _field = null;
+        } else {
+            throw new IllegalArgumentException("Can not pass member of type "+member.getClass().getName());
+        }
+        _suppressNulls = suppressNulls;
+        _suppressableValue = suppressableValue;
+        _includeInViews = propDef.findViews();
+
+        // this will be resolved later on, unless nulls are to be suppressed
+        _nullSerializer = null;
+    }
+
+    /**
+     * "Copy constructor" to be used by filtering sub-classes
+     */
+    protected BeanPropertyWriter(BeanPropertyWriter base) {
+        this(base, base._name);
+    }
+
+    protected BeanPropertyWriter(BeanPropertyWriter base, SerializedString name)
+    {
+        _name = name;
+        _wrapperName = base._wrapperName;
+
+        _member = base._member;
+        _contextAnnotations = base._contextAnnotations;
+        _declaredType = base._declaredType;
+        _accessorMethod = base._accessorMethod;
+        _field = base._field;
+        _serializer = base._serializer;
+        _nullSerializer = base._nullSerializer;
+        // one more thing: copy internal settings, if any (since 1.7)
+        if (base._internalSettings != null) {
+            _internalSettings = new HashMap<Object,Object>(base._internalSettings);
+        }
+        _cfgSerializationType = base._cfgSerializationType;
+        _dynamicSerializers = base._dynamicSerializers;
+        _suppressNulls = base._suppressNulls;
+        _suppressableValue = base._suppressableValue;
+        _includeInViews = base._includeInViews;
+        _typeSerializer = base._typeSerializer;
+        _nonTrivialBaseType = base._nonTrivialBaseType;
+        _isRequired = base._isRequired;
+    }
+
+    public BeanPropertyWriter rename(NameTransformer transformer) {
+        String newName = transformer.transform(_name.getValue());
+        if (newName.equals(_name.toString())) {
+            return this;
+        }
+        return new BeanPropertyWriter(this, new SerializedString(newName));
+    }
+    
+    /**
+     * Method called to assign value serializer for property
+     * 
+     * @since 2.0
+     */
+    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");
+        }
+        _serializer = ser;
+    }
+
+    /**
+     * Method called to assign null value serializer for property
+     * 
+     * @since 2.0
+     */
+    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");
+        }
+        _nullSerializer = nullSer;
+    }
+    
+    /**
+     * Method called create an instance that handles details of unwrapping
+     * contained value.
+     */
+    public BeanPropertyWriter unwrappingWriter(NameTransformer unwrapper) {
+        return new UnwrappingBeanPropertyWriter(this, unwrapper);
+    }
+
+    /**
+     * Method called to define type to consider as "non-trivial" basetype,
+     * needed for dynamic serialization resolution for complex (usually container)
+     * types
+     */
+    public void setNonTrivialBaseType(JavaType t) {
+        _nonTrivialBaseType = t;
+    }
+
+    /*
+    /**********************************************************
+    /* BeanProperty impl
+    /**********************************************************
+     */
+    
+    @Override
+    public String getName() {
+        return _name.getValue();
+    }
+
+    @Override
+    public JavaType getType() {
+        return _declaredType;
+    }
+
+    @Override
+    public PropertyName getWrapperName() {
+        return _wrapperName;
+    }
+
+    @Override
+    public boolean isRequired() {
+        return _isRequired;
+    }
+    
+    @Override
+    public <A extends Annotation> A getAnnotation(Class<A> acls) {
+        return _member.getAnnotation(acls);
+    }
+
+    @Override
+    public <A extends Annotation> A getContextAnnotation(Class<A> acls) {
+        return _contextAnnotations.get(acls);
+    }
+
+    @Override
+    public AnnotatedMember getMember() {
+        return _member;
+    }
+
+
+    @Override
+    public void depositSchemaProperty(JsonObjectFormatVisitor objectVisitor)
+        throws JsonMappingException
+    {
+        if (objectVisitor != null) {
+            if (isRequired()) {
+                objectVisitor.property(this); 
+            } else {
+                objectVisitor.optionalProperty(this);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Managing and accessing of opaque internal settings
+    /* (used by extensions)
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing value of specified internal setting.
+     * 
+     * @return Value of the setting, if any; null if none.
+     */
+    public Object getInternalSetting(Object key)
+    {
+        if (_internalSettings == null) {
+            return null;
+        }
+        return _internalSettings.get(key);
+    }
+    
+    /**
+     * Method for setting specific internal setting to given value
+     * 
+     * @return Old value of the setting, if any (null if none)
+     */
+    public Object setInternalSetting(Object key, Object value)
+    {
+        if (_internalSettings == null) {
+            _internalSettings = new HashMap<Object,Object>();
+        }
+        return _internalSettings.put(key, value);
+    }
+
+    /**
+     * Method for removing entry for specified internal setting.
+     * 
+     * @return Existing value of the setting, if any (null if none)
+     */
+    public Object removeInternalSetting(Object key)
+    {
+        Object removed = null;
+        if (_internalSettings != null) {
+            removed = _internalSettings.remove(key);
+            // to reduce memory usage, let's also drop the Map itself, if empty
+            if (_internalSettings.size() == 0) {
+                _internalSettings = null;
+            }
+        }
+        return removed;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    public SerializedString getSerializedName() { return _name; }
+    
+    public boolean hasSerializer() { return _serializer != null; }
+    public boolean hasNullSerializer() { return _nullSerializer != null; }
+
+    public boolean willSuppressNulls() { return _suppressNulls; }
+    
+    // Needed by BeanSerializer#getSchema
+    public JsonSerializer<Object> getSerializer() {
+        return _serializer;
+    }
+
+    public JavaType getSerializationType() {
+        return _cfgSerializationType;
+    }
+
+    public Class<?> getRawSerializationType() {
+        return (_cfgSerializationType == null) ? null : _cfgSerializationType.getRawClass();
+    }
+    
+    public Class<?> getPropertyType() 
+    {
+        if (_accessorMethod != null) {
+            return _accessorMethod.getReturnType();
+        }
+        return _field.getType();
+    }
+
+    /**
+     * Get the generic property type of this property writer.
+     *
+     * @return The property type, or null if not found.
+     */
+    public Type getGenericPropertyType()
+    {
+        if (_accessorMethod != null) {
+            return _accessorMethod.getGenericReturnType();
+        }
+        return _field.getGenericType();
+    }
+
+    public Class<?>[] getViews() { return _includeInViews; }
+
+    /**
+     *<p>
+     * NOTE: due to introspection, this is a <b>slow</b> method to call
+     * and should never be called during actual serialization or filtering
+     * of the property. Rather it is needed for traversal needed for things
+     * like constructing JSON Schema instances.
+     * 
+     * @since 2.1
+     * 
+     * @deprecated since 2.2, use {@link #isRequired()} instead.
+     */
+    @Deprecated
+    protected boolean isRequired(AnnotationIntrospector intr) {
+        return _isRequired;
+    }
+
+    /*
+    /**********************************************************
+    /* Legacy support for JsonFormatVisitable
+    /**********************************************************
+     */
+
+    /**
+     * Attempt to add the output of the given {@link BeanPropertyWriter} in the given {@link ObjectNode}.
+     * Otherwise, add the default schema {@link JsonNode} in place of the writer's output
+     * 
+     * @param propertiesNode Node which the given property would exist within
+     * @param provider Provider that can be used for accessing dynamic aspects of serialization
+     *  processing
+     *  
+     *  {@link BeanPropertyFilter#depositSchemaProperty(BeanPropertyWriter, ObjectNode, SerializerProvider)}
+     * 
+     * @since 2.1
+     */
+    @SuppressWarnings("deprecation")
+    public void depositSchemaProperty(ObjectNode propertiesNode, SerializerProvider provider)
+        throws JsonMappingException
+    {
+        JavaType propType = getSerializationType();
+        // 03-Dec-2010, tatu: SchemaAware REALLY should use JavaType, but alas it doesn't...
+        Type hint = (propType == null) ? getGenericPropertyType() : propType.getRawClass();
+        JsonNode schemaNode;
+        // Maybe it already has annotated/statically configured serializer?
+        JsonSerializer<Object> ser = getSerializer();
+        if (ser == null) { // nope
+            Class<?> serType = getRawSerializationType();
+            if (serType == null) {
+                serType = getPropertyType();
+            }
+            ser = provider.findValueSerializer(serType, this);
+        }
+        boolean isOptional = !isRequired();
+        if (ser instanceof SchemaAware) {
+            schemaNode =  ((SchemaAware) ser).getSchema(provider, hint, isOptional) ;
+        } else {  
+            schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode(); 
+        }
+        propertiesNode.put(getName(), schemaNode);
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization functionality
+    /**********************************************************
+     */
+
+    /**
+     * Method called to access property that this bean stands for, from
+     * within given bean, and to serialize it as a JSON Object field
+     * using appropriate serializer.
+     */
+    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        throws Exception
+    {
+        Object value = get(bean);
+        // Null handling is bit different, check that first
+        if (value == null) {
+            if (_nullSerializer != null) {
+                jgen.writeFieldName(_name);
+                _nullSerializer.serialize(null, jgen, prov);
+            }
+            return;
+        }
+        // then find serializer to use
+        JsonSerializer<Object> ser = _serializer;
+        if (ser == null) {
+            Class<?> cls = value.getClass();
+            PropertySerializerMap map = _dynamicSerializers;
+            ser = map.serializerFor(cls);
+            if (ser == null) {
+                ser = _findAndAddDynamic(map, cls, prov);
+            }
+        }
+        // and then see if we must suppress certain values (default, empty)
+        if (_suppressableValue != null) {
+            if (MARKER_FOR_EMPTY == _suppressableValue) {
+                if (ser.isEmpty(value)) {
+                    return;
+                }
+            } else if (_suppressableValue.equals(value)) {
+                return;
+            }
+        }
+        // For non-nulls: simple check for direct cycles
+        if (value == bean) {
+            _handleSelfReference(bean, ser);
+        }
+        jgen.writeFieldName(_name);
+        if (_typeSerializer == null) {
+            ser.serialize(value, jgen, prov);
+        } else {
+            ser.serializeWithType(value, jgen, prov, _typeSerializer);
+        }
+    }
+
+    /**
+     * Alternative to {@link #serializeAsField} that is used when a POJO
+     * is serialized as JSON Array; the difference is that no field names
+     * are written.
+     * 
+     * @since 2.1
+     */
+    public void serializeAsColumn(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        throws Exception
+    {
+        Object value = get(bean);
+        if (value == null) { // nulls need specialized handling
+            if (_nullSerializer != null) {
+                _nullSerializer.serialize(null, jgen, prov);
+            } else { // can NOT suppress entries in tabular output
+                jgen.writeNull();
+            }
+            return;
+        }
+        // otherwise find serializer to use
+        JsonSerializer<Object> ser = _serializer;
+        if (ser == null) {
+            Class<?> cls = value.getClass();
+            PropertySerializerMap map = _dynamicSerializers;
+            ser = map.serializerFor(cls);
+            if (ser == null) {
+                ser = _findAndAddDynamic(map, cls, prov);
+            }
+        }
+        // and then see if we must suppress certain values (default, empty)
+        if (_suppressableValue != null) {
+            if (MARKER_FOR_EMPTY == _suppressableValue) {
+                if (ser.isEmpty(value)) { // can NOT suppress entries in tabular output
+                    serializeAsPlaceholder(bean, jgen, prov);
+                    return;
+                }
+            } else if (_suppressableValue.equals(value)) { // can NOT suppress entries in tabular output
+                serializeAsPlaceholder(bean, jgen, prov);
+                return;
+            }
+        }
+        // For non-nulls: simple check for direct cycles
+        if (value == bean) {
+            _handleSelfReference(bean, ser);
+        }
+        if (_typeSerializer == null) {
+            ser.serialize(value, jgen, prov);
+        } else {
+            ser.serializeWithType(value, jgen, prov, _typeSerializer);
+        }
+    }
+
+    /**
+     * Method called to serialize a placeholder used in tabular output when
+     * real value is not to be included (is filtered out), but when we need
+     * an entry so that field indexes will not be off. Typically this should
+     * output null or empty String, depending on datatype.
+     * 
+     * @since 2.1
+     */
+    public void serializeAsPlaceholder(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        throws Exception
+    {
+        if (_nullSerializer != null) {
+            _nullSerializer.serialize(null, jgen, prov);
+        } else {
+            jgen.writeNull();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            Class<?> type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result;
+        if (_nonTrivialBaseType != null) {
+            JavaType t = provider.constructSpecializedType(_nonTrivialBaseType, type);
+            result = map.findAndAddSerializer(t, provider, this);
+        } else {
+            result = map.findAndAddSerializer(type, provider, this);
+        }
+        // did we get a new map of serializers? If so, start using it
+        if (map != result.map) {
+            _dynamicSerializers = result.map;
+        }
+        return result.serializer;
+    }
+    
+    /**
+     * Method that can be used to access value of the property this
+     * Object describes, from given bean instance.
+     *<p>
+     * Note: method is final as it should not need to be overridden -- rather,
+     * calling method(s) ({@link #serializeAsField}) should be overridden
+     * to change the behavior
+     */
+    public final Object get(Object bean) throws Exception
+    {
+        if (_accessorMethod != null) {
+            return _accessorMethod.invoke(bean);
+        }
+        return _field.get(bean);
+    }
+
+    protected void _handleSelfReference(Object bean, JsonSerializer<?> ser)
+        throws JsonMappingException
+    {
+        /* 05-Feb-2012, tatu: Usually a problem, but NOT if we are handling
+         *    object id; this may be the case for BeanSerializers at least.
+         */
+        if (ser.usesObjectId()) {
+            return;
+        }
+        throw new JsonMappingException("Direct self-reference leading to cycle");
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append("property '").append(getName()).append("' (");
+        if (_accessorMethod != null) {
+            sb.append("via method ").append(_accessorMethod.getDeclaringClass().getName()).append("#").append(_accessorMethod.getName());
+        } else {
+            sb.append("field \"").append(_field.getDeclaringClass().getName()).append("#").append(_field.getName());
+        }
+        if (_serializer == null) {
+            sb.append(", no static serializer");
+        } else {
+            sb.append(", static serializer of type "+_serializer.getClass().getName());
+        }
+        sb.append(')');
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
new file mode 100644
index 0000000..7098f1f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializer.java
@@ -0,0 +1,156 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.impl.BeanAsArraySerializer;
+import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
+import com.fasterxml.jackson.databind.ser.impl.UnwrappingBeanSerializer;
+import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Serializer class that can serialize Java objects that map
+ * to JSON Object output. Internally handling is mostly dealt with
+ * by a sequence of {@link BeanPropertyWriter}s that will handle
+ * access value to serialize and call appropriate serializers to
+ * write out JSON.
+ *<p>
+ * Implementation note: we will post-process resulting serializer,
+ * to figure out actual serializers for final types. This must be
+ * done from {@link #resolve} method, and NOT from constructor;
+ * otherwise we could end up with an infinite loop.
+ */
+public class BeanSerializer
+    extends BeanSerializerBase
+{
+    /*
+    /**********************************************************
+    /* Life-cycle: constructors
+    /**********************************************************
+     */
+
+    /**
+     * @param builder Builder object that contains collected information
+     *   that may be needed for serializer
+     * @param properties Property writers used for actual serialization
+     */
+    public BeanSerializer(JavaType type, BeanSerializerBuilder builder,
+            BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
+    {
+        super(type, builder, properties, filteredProperties);
+    }
+    
+    /**
+     * Alternate copy constructor that can be used to construct
+     * standard {@link BeanSerializer} passing an instance of
+     * "compatible enough" source serializer.
+     */
+    protected BeanSerializer(BeanSerializerBase src) {
+        super(src);
+    }
+
+    protected BeanSerializer(BeanSerializerBase src, ObjectIdWriter objectIdWriter) {
+        super(src, objectIdWriter);
+    }
+
+    protected BeanSerializer(BeanSerializerBase src, String[] toIgnore) {
+        super(src, toIgnore);
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: factory methods, fluent factories
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing dummy bean serializer; one that
+     * never outputs any properties
+     */
+    public static BeanSerializer createDummy(JavaType forType)
+    {
+        return new BeanSerializer(forType, null, NO_PROPS, null);
+    }
+
+    @Override
+    public JsonSerializer<Object> unwrappingSerializer(NameTransformer unwrapper) {
+        return new UnwrappingBeanSerializer(this, unwrapper);
+    }
+
+    @Override
+    public BeanSerializer withObjectIdWriter(ObjectIdWriter objectIdWriter) {
+        return new BeanSerializer(this, objectIdWriter);
+    }
+
+    @Override
+    protected BeanSerializer withIgnorals(String[] toIgnore) {
+        return new BeanSerializer(this, toIgnore);
+    }
+
+    /**
+     * Implementation has to check whether as-array serialization
+     * is possible reliably; if (and only if) so, will construct
+     * a {@link BeanAsArraySerializer}, otherwise will return this
+     * serializer as is.
+     */
+    @Override
+    protected BeanSerializerBase asArraySerializer()
+    {
+        /* Can not:
+         * 
+         * - have Object Id (may be allowed in future)
+         * - have any getter
+         * 
+         */
+        if ((_objectIdWriter == null)
+                && (_anyGetterWriter == null)
+                && (_propertyFilterId == null)
+                ) {
+            return new BeanAsArraySerializer(this);
+        }
+        // already is one, so:
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializer implementation that differs between impls
+    /**********************************************************
+     */
+
+    /**
+     * Main serialization method that will delegate actual output to
+     * configured
+     * {@link BeanPropertyWriter} instances.
+     */
+    @Override
+    public final void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_objectIdWriter != null) {
+            _serializeWithObjectId(bean, jgen, provider, true);
+            return;
+        }
+        jgen.writeStartObject();
+        if (_propertyFilterId != null) {
+            serializeFieldsFiltered(bean, jgen, provider);
+        } else {
+            serializeFields(bean, jgen, provider);
+        }
+        jgen.writeEndObject();
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override public String toString() {
+        return "BeanSerializer for "+handledType().getName();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
new file mode 100644
index 0000000..06b59cd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerBuilder.java
@@ -0,0 +1,196 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
+
+/**
+ * Builder class used for aggregating deserialization information about
+ * a POJO, in order to build a {@link JsonSerializer} for serializing
+ * intances.
+ * Main reason for using separate builder class is that this makes it easier
+ * to make actual serializer class fully immutable.
+ */
+public class BeanSerializerBuilder
+{
+    private final static BeanPropertyWriter[] NO_PROPERTIES = new BeanPropertyWriter[0];
+
+    /*
+    /**********************************************************
+    /* Basic configuration we start with
+    /**********************************************************
+     */
+
+    final protected BeanDescription _beanDesc;
+
+    protected SerializationConfig _config;
+    
+    /*
+    /**********************************************************
+    /* Accumulated information about properties
+    /**********************************************************
+     */
+
+    /**
+     * Bean properties, in order of serialization
+     */
+    protected List<BeanPropertyWriter> _properties;
+
+    /**
+     * Optional array of filtered property writers; if null, no
+     * view-based filtering is performed.
+     */
+    protected BeanPropertyWriter[] _filteredProperties;
+    
+    /**
+     * Writer used for "any getter" properties, if any.
+     */
+    protected AnyGetterWriter _anyGetter;
+
+    /**
+     * Id of the property filter to use for POJO, if any.
+     */
+    protected Object _filterId;
+
+    /**
+     * Property that is used for type id (and not serialized as regular
+     * property)
+     */
+    protected AnnotatedMember _typeId;
+
+    /**
+     * Object responsible for serializing Object Ids for the handled
+     * type, if any.
+     */
+    protected ObjectIdWriter _objectIdWriter;
+    
+    /*
+    /**********************************************************
+    /* Construction and setter methods
+    /**********************************************************
+     */
+    
+    public BeanSerializerBuilder(BeanDescription beanDesc) {
+        _beanDesc = beanDesc;
+    }
+
+    /**
+     * Copy-constructor that may be used for sub-classing
+     */
+    protected BeanSerializerBuilder(BeanSerializerBuilder src) {
+        _beanDesc = src._beanDesc;
+        _properties = src._properties;
+        _filteredProperties = src._filteredProperties;
+        _anyGetter = src._anyGetter;
+        _filterId = src._filterId;
+    }
+
+    /**
+     * Initialization method called right after construction, to specify
+     * configuration to use.
+     *<p>
+     * Note: ideally should be passed in constructor, but for backwards
+     * compatibility, needed to add a setter instead
+     * 
+     * @since 2.1
+     */
+    protected void setConfig(SerializationConfig config) {
+        _config = config;
+    }
+    
+    public void setProperties(List<BeanPropertyWriter> properties) {
+        _properties = properties;
+    }
+
+    public void setFilteredProperties(BeanPropertyWriter[] properties) {
+        _filteredProperties = properties;
+    }
+    
+    public void setAnyGetter(AnyGetterWriter anyGetter) {
+        _anyGetter = anyGetter;
+    }
+
+    public void setFilterId(Object filterId) {
+        _filterId = filterId;
+    }
+    
+    public void setTypeId(AnnotatedMember idProp) {
+        // Not legal to use multiple ones...
+        if (_typeId != null) {
+            throw new IllegalArgumentException("Multiple type ids specified with "+_typeId+" and "+idProp);
+        }
+        _typeId = idProp;
+    }
+
+    public void setObjectIdWriter(ObjectIdWriter w) {
+        _objectIdWriter = w;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors for things BeanSerializer cares about:
+    /* note -- likely to change between minor revisions
+    /* by new methods getting added.
+    /**********************************************************
+     */
+
+    public AnnotatedClass getClassInfo() { return _beanDesc.getClassInfo(); }
+    
+    public BeanDescription getBeanDescription() { return _beanDesc; }
+
+    public List<BeanPropertyWriter> getProperties() { return _properties; }
+    public boolean hasProperties() {
+        return (_properties != null) && (_properties.size() > 0);
+    }
+
+    public BeanPropertyWriter[] getFilteredProperties() { return _filteredProperties; }
+    
+    public AnyGetterWriter getAnyGetter() { return _anyGetter; }
+    
+    public Object getFilterId() { return _filterId; }
+
+    public AnnotatedMember getTypeId() { return _typeId; }
+
+    public ObjectIdWriter getObjectIdWriter() { return _objectIdWriter; }
+    
+    /*
+    /**********************************************************
+    /* Build methods for actually creating serializer instance
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to create {@link BeanSerializer} instance with
+     * all accumulated information. Will construct a serializer if we
+     * have enough information, or return null if not.
+     */
+    public JsonSerializer<?> build()
+    {
+        BeanPropertyWriter[] properties;
+        // No properties or any getter? No real serializer; caller gets to handle
+        if (_properties == null || _properties.isEmpty()) {
+            if (_anyGetter == null) {
+                return null;
+            }
+            properties = NO_PROPERTIES;
+        } else {
+            properties = _properties.toArray(new BeanPropertyWriter[_properties.size()]);
+            
+        }
+        return new BeanSerializer(_beanDesc.getType(), this,
+                properties, _filteredProperties);
+    }
+    
+    /**
+     * Factory method for constructing an "empty" serializer; one that
+     * outputs no properties (but handles JSON objects properly, including
+     * type information)
+     */
+    public BeanSerializer createDummy() {
+        return BeanSerializer.createDummy(_beanDesc.getType());
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
new file mode 100644
index 0000000..a0eccf6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerFactory.java
@@ -0,0 +1,766 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.SerializerFactoryConfig;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+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.MapSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
+import com.fasterxml.jackson.databind.type.*;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Factory class that can provide serializers for any regular Java beans
+ * (as defined by "having at least one get method recognizable as bean
+ * accessor" -- where {@link Object#getClass} does not count);
+ * as well as for "standard" JDK types. Latter is achieved
+ * by delegating calls to {@link BasicSerializerFactory} 
+ * to find serializers both for "standard" JDK types (and in some cases,
+ * sub-classes as is the case for collection classes like
+ * {@link java.util.List}s and {@link java.util.Map}s) and bean (value)
+ * classes.
+ *<p>
+ * Note about delegating calls to {@link BasicSerializerFactory}:
+ * although it would be nicer to use linear delegation
+ * for construction (to essentially dispatch all calls first to the
+ * underlying {@link BasicSerializerFactory}; or alternatively after
+ * failing to provide bean-based serializer}, there is a problem:
+ * priority levels for detecting standard types are mixed. That is,
+ * we want to check if a type is a bean after some of "standard" JDK
+ * types, but before the rest.
+ * As a result, "mixed" delegation used, and calls are NOT done using
+ * regular {@link SerializerFactory} interface but rather via
+ * direct calls to {@link BasicSerializerFactory}.
+ *<p>
+ * Finally, since all caching is handled by the serializer provider
+ * (not factory) and there is no configurability, this
+ * factory is stateless.
+ * This means that a global singleton instance can be used.
+ */
+public class BeanSerializerFactory
+    extends BasicSerializerFactory
+    implements java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 1;
+
+    /**
+     * Like {@link BasicSerializerFactory}, this factory is stateless, and
+     * thus a single shared global (== singleton) instance can be used
+     * without thread-safety issues.
+     */
+    public final static BeanSerializerFactory instance = new BeanSerializerFactory(null);
+
+    /*
+    /**********************************************************
+    /* Life-cycle: creation, configuration
+    /**********************************************************
+     */
+
+    /**
+     * Constructor for creating instances with specified configuration.
+     */
+    protected BeanSerializerFactory(SerializerFactoryConfig config)
+    {
+        super(config);
+    }
+    
+    /**
+     * Method used by module registration functionality, to attach additional
+     * serializer providers into this serializer factory. This is typically
+     * handled by constructing a new instance with additional serializers,
+     * to ensure thread-safe access.
+     */
+    @Override
+    public SerializerFactory withConfig(SerializerFactoryConfig config)
+    {
+        if (_factoryConfig == config) {
+            return this;
+        }
+        /* 22-Nov-2010, tatu: Handling of subtypes is tricky if we do immutable-with-copy-ctor;
+         *    and we pretty much have to here either choose between losing subtype instance
+         *    when registering additional serializers, or losing serializers.
+         *    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() != BeanSerializerFactory.class) {
+            throw new IllegalStateException("Subtype of BeanSerializerFactory ("+getClass().getName()
+                    +") has not properly overridden method 'withAdditionalSerializers': can not instantiate subtype with "
+                    +"additional serializer definitions");
+        }
+        return new BeanSerializerFactory(config);
+    }
+
+    @Override
+    protected Iterable<Serializers> customSerializers() {
+        return _factoryConfig.serializers();
+    }
+    
+    /*
+    /**********************************************************
+    /* SerializerFactory impl
+    /**********************************************************
+     */
+
+    /**
+     * Main serializer constructor method. We will have to be careful
+     * with respect to ordering of various method calls: essentially
+     * we want to reliably figure out which classes are standard types,
+     * and which are beans. The problem is that some bean Classes may
+     * implement standard interfaces (say, {@link java.lang.Iterable}.
+     *<p>
+     * Note: sub-classes may choose to complete replace implementation,
+     * if they want to alter priority of serializer lookups.
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public JsonSerializer<Object> createSerializer(SerializerProvider prov,
+            JavaType origType)
+        throws JsonMappingException
+    {
+        // Very first thing, let's check if there is explicit serializer annotation:
+        final SerializationConfig config = prov.getConfig();
+        BeanDescription beanDesc = config.introspect(origType);
+        JsonSerializer<?> ser = findSerializerFromAnnotation(prov, beanDesc.getClassInfo());
+        if (ser != null) {
+            return (JsonSerializer<Object>) ser;
+        }
+        boolean staticTyping;
+        // Next: we may have annotations that further define types to use...
+        JavaType type = modifyTypeByAnnotation(config, beanDesc.getClassInfo(), origType);
+        if (type == origType) { // no changes, won't force static typing
+            staticTyping = false;
+        } else { // changes; assume static typing; plus, need to re-introspect if class differs
+            staticTyping = true;
+            if (type.getRawClass() != origType.getRawClass()) {
+                beanDesc = config.introspect(type);
+            }
+        }
+        // Slight detour: do we have a Converter to consider?
+        Converter<Object,Object> conv = beanDesc.findSerializationConverter();
+        if (conv == null) { // no, simple:
+            return (JsonSerializer<Object>) _createSerializer2(prov, type, beanDesc, staticTyping);
+        }
+        JavaType delegateType = conv.getOutputType(prov.getTypeFactory());
+        return new StdDelegatingSerializer(conv, delegateType,
+                _createSerializer2(prov, delegateType, beanDesc, true));
+    }
+
+    protected JsonSerializer<?> _createSerializer2(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc, boolean staticTyping)
+        throws JsonMappingException
+    {
+        // Then JsonSerializable, @JsonValue etc:
+        JsonSerializer<?> ser = findSerializerByAnnotations(prov, type, beanDesc);
+        if (ser != null) {
+            return ser;
+        }
+        final SerializationConfig config = prov.getConfig();
+        
+        // Container types differ from non-container types
+        // (note: called method checks for module-provided serializers)
+        if (type.isContainerType()) {
+            if (!staticTyping) {
+                staticTyping = usesStaticTyping(config, beanDesc, null);
+                // [Issue#23]: Need to figure out how to force passed parameterization
+                //  to stick...
+                /*
+                if (property == null) {
+                    JavaType t = origType.getContentType();
+                    if (t != null && !t.hasRawClass(Object.class)) {
+                        staticTyping = true;
+                    }
+                }
+                */
+            }
+            // 03-Aug-2012, tatu: As per [Issue#40], may require POJO serializer...
+            ser =  buildContainerSerializer(prov, type, beanDesc, staticTyping);
+            // Will return right away, since called method does post-processing:
+            if (ser != null) {
+                return ser;
+            }
+        } else {
+            // Modules may provide serializers of POJO types:
+            for (Serializers serializers : customSerializers()) {
+                ser = serializers.findSerializer(config, type, beanDesc);
+                if (ser != null) {
+                    break;
+                }
+            }
+        }
+        
+        // Otherwise, we will check "primary types"; both marker types that
+        // indicate specific handling (JsonSerializable), or main types that have
+        // precedence over container types
+        if (ser == null) {
+            ser = findSerializerByLookup(type, config, beanDesc, staticTyping);
+            if (ser == null) {
+                ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping);
+                if (ser == null) {
+                    // And this is where this class comes in: if type is not a
+                    // known "primary JDK type", perhaps it's a bean? We can still
+                    // get a null, if we can't find a single suitable bean property.
+                    ser = findBeanSerializer(prov, type, beanDesc);
+                    // Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
+                    if (ser == null) {
+                        ser = findSerializerByAddonType(config, type, beanDesc, staticTyping);
+                    }
+                }
+            }
+        }
+        if (ser != null) {
+            // [Issue#120]: Allow post-processing
+            if (_factoryConfig.hasSerializerModifiers()) {
+                for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                    ser = mod.modifySerializer(config, beanDesc, ser);
+                }
+            }
+        }
+        return ser;
+    }
+    
+    /*
+    /**********************************************************
+    /* Other public methods that are not part of
+    /* JsonSerializerFactory API
+    /**********************************************************
+     */
+
+    /**
+     * Deprecated method; final to help identify problems with sub-classes,
+     * as this method will NOT be called any more in 2.1
+     * 
+     * @deprecated Since 2.1 (use variant without 'property' argument).
+     */
+    @Deprecated
+    public final JsonSerializer<Object> findBeanSerializer(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc, BeanProperty property)
+        throws JsonMappingException {
+        return findBeanSerializer(prov, type, beanDesc);
+    }
+    
+    /**
+     * Method that will try to construct a {@link BeanSerializer} for
+     * given class. Returns null if no properties are found.
+     */
+    public JsonSerializer<Object> findBeanSerializer(SerializerProvider prov,
+            JavaType type, BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        // First things first: we know some types are not beans...
+        if (!isPotentialBeanType(type.getRawClass())) {
+            // 03-Aug-2012, tatu: Except we do need to allow serializers for Enums,
+            //   as per [Issue#24]
+            if (!type.isEnumType()) {
+                return null;
+            }
+        }
+        return constructBeanSerializer(prov, beanDesc);
+    }
+
+    /**
+     * @deprecated Since 2.1
+     */
+    @Deprecated
+    public final TypeSerializer findPropertyTypeSerializer(JavaType baseType,
+            SerializationConfig config, AnnotatedMember accessor, BeanProperty property)
+        throws JsonMappingException {
+        return findPropertyTypeSerializer(baseType, config, accessor);
+    }
+    
+    /**
+     * 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
+     * return null.
+     *
+     * @param baseType Declared type to use as the base type for type information serializer
+     * 
+     * @return Type serializer to use for property values, if one is needed; null if not.
+     */
+    public TypeSerializer findPropertyTypeSerializer(JavaType baseType,
+            SerializationConfig config, AnnotatedMember accessor)
+        throws JsonMappingException
+    {
+        AnnotationIntrospector ai = config.getAnnotationIntrospector();
+        TypeResolverBuilder<?> b = ai.findPropertyTypeResolver(config, accessor, baseType);        
+        // Defaulting: if no annotations on member, check value class
+        if (b == null) {
+            return createTypeSerializer(config, baseType);
+        }
+        Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(
+                accessor, config, ai, baseType);
+        return b.buildTypeSerializer(config, baseType, subtypes);
+    }
+
+    /**
+     * Method called to create a type information serializer for values of given
+     * container property
+     * if one is needed. If not needed (no polymorphic handling configured), should
+     * return null.
+     *
+     * @param containerType Declared type of the container to use as the base type for type information serializer
+     * 
+     * @return Type serializer to use for property value contents, if one is needed; null if not.
+     */    
+    public TypeSerializer findPropertyContentTypeSerializer(JavaType containerType,
+            SerializationConfig config, AnnotatedMember accessor)
+        throws JsonMappingException
+    {
+        JavaType contentType = containerType.getContentType();
+        AnnotationIntrospector ai = config.getAnnotationIntrospector();
+        TypeResolverBuilder<?> b = ai.findPropertyContentTypeResolver(config, accessor, containerType);        
+        // Defaulting: if no annotations on member, check value class
+        if (b == null) {
+            return createTypeSerializer(config, contentType);
+        }
+        Collection<NamedType> subtypes = config.getSubtypeResolver().collectAndResolveSubtypes(accessor,
+                config, ai, contentType);
+        return b.buildTypeSerializer(config, contentType, subtypes);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable non-public factory methods
+    /**********************************************************
+     */
+
+    /**
+     * Deprecated method; final to help identify problems with sub-classes,
+     * as this method will NOT be called any more in 2.1
+     * 
+     * @deprecated Since 2.1, do not pass 'property' argument
+     */
+    @Deprecated
+    protected final JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
+            BeanDescription beanDesc, BeanProperty property)
+        throws JsonMappingException
+    {
+        return constructBeanSerializer(prov, beanDesc);
+    }
+    
+    /**
+     * Method called to construct serializer for serializing specified bean type.
+     * 
+     * @since 2.1
+     */
+    @SuppressWarnings("unchecked")
+    protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
+            BeanDescription beanDesc)
+        throws JsonMappingException
+    {
+        // 13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object
+        // 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");
+        }
+        final SerializationConfig config = prov.getConfig();
+        BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
+        builder.setConfig(config);
+        
+        // First: any detectable (auto-detect, annotations) properties to serialize?
+        List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
+        if (props == null) {
+            props = new ArrayList<BeanPropertyWriter>();
+        }
+        // [JACKSON-440] Need to allow modification bean properties to serialize:
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                props = mod.changeProperties(config, beanDesc, props);
+            }
+        }
+        
+        // Any properties to suppress?
+        props = filterBeanProperties(config, beanDesc, props);
+        
+        // [JACKSON-440] Need to allow reordering of properties to serialize
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                props = mod.orderProperties(config, beanDesc, props);
+            }
+        }
+
+        /* 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);
+        builder.setFilterId(findFilterId(config, beanDesc));
+        
+        AnnotatedMember anyGetter = beanDesc.findAnyGetter();
+        if (anyGetter != null) { // since 1.6
+            if (config.canOverrideAccessModifiers()) {
+                anyGetter.fixAccess();
+            }
+            JavaType type = anyGetter.getType(beanDesc.bindingsForBeanType());
+            // copied from BasicSerializerFactory.buildMapSerializer():
+            boolean staticTyping = config.isEnabled(MapperFeature.USE_STATIC_TYPING);
+            JavaType valueType = type.getContentType();
+            TypeSerializer typeSer = createTypeSerializer(config, valueType);
+            // last 2 nulls; don't know key, value serializers (yet)
+            MapSerializer mapSer = MapSerializer.construct(/* ignored props*/ null, type, staticTyping,
+                    typeSer, null, null);
+            BeanProperty.Std anyProp = new BeanProperty.Std(anyGetter.getName(), valueType, null,
+                    beanDesc.getClassAnnotations(), anyGetter, false);
+            builder.setAnyGetter(new AnyGetterWriter(anyProp, anyGetter, mapSer));
+        }
+        // Next: need to gather view information, if any:
+        processViews(config, builder);
+        
+        // Finally: let interested parties mess with the result bit more...
+        if (_factoryConfig.hasSerializerModifiers()) {
+            for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
+                builder = mod.updateBuilder(config, beanDesc, builder);
+            }
+        }
+        
+        JsonSerializer<Object> ser = (JsonSerializer<Object>) builder.build();
+
+        /* However, after all modifications: no properties, no serializer
+         * (note; as per [JACKSON-670], check was moved later on from an earlier location)
+         */
+        if (ser == null) {
+            /* 27-Nov-2009, tatu: Except that as per [JACKSON-201], we are
+             *   ok with that as long as it has a recognized class annotation
+             *  (which may come from a mix-in too)
+             */
+            if (beanDesc.hasKnownClassAnnotations()) {
+                return builder.createDummy();
+            }
+        }
+        return ser;
+    }
+
+    protected ObjectIdWriter constructObjectIdHandler(SerializerProvider prov,
+            BeanDescription beanDesc, List<BeanPropertyWriter> props)
+        throws JsonMappingException
+    {
+        ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo();
+        if (objectIdInfo == null) {
+            return null;
+        }
+        ObjectIdGenerator<?> gen;
+        Class<?> implClass = objectIdInfo.getGeneratorType();
+
+        // Just one special case: Property-based generator is trickier
+        if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
+            String propName = objectIdInfo.getPropertyName();
+            BeanPropertyWriter idProp = null;
+
+            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+"'");
+                }
+                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)
+                     */
+                    if (i > 0) {
+                        props.remove(i);
+                        props.add(0, idProp);
+                    }
+                    break;
+                }
+            }
+            JavaType idType = idProp.getType();
+            gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp);
+            // one more thing: must ensure that ObjectIdWriter does not actually write the value:
+            return ObjectIdWriter.construct(idType, null, gen, objectIdInfo.getAlwaysAsId());
+            
+        } 
+        // other types are simpler
+        JavaType type = prov.constructType(implClass);
+        // Could require type to be passed explicitly, but we should be able to find it too:
+        JavaType idType = prov.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
+        gen = prov.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo);
+        return ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen,
+                objectIdInfo.getAlwaysAsId());
+    }
+
+    /**
+     * Method called to construct a filtered writer, for given view
+     * definitions. Default implementation constructs filter that checks
+     * active view type to views property is to be included in.
+     */
+    protected BeanPropertyWriter constructFilteredBeanWriter(BeanPropertyWriter writer,
+            Class<?>[] inViews)
+    {
+        return FilteredBeanPropertyWriter.constructViewBased(writer, inViews);
+    }
+    
+    protected PropertyBuilder constructPropertyBuilder(SerializationConfig config,
+            BeanDescription beanDesc)
+    {
+        return new PropertyBuilder(config, beanDesc);
+    }
+
+    protected BeanSerializerBuilder constructBeanSerializerBuilder(BeanDescription beanDesc) {
+        return new BeanSerializerBuilder(beanDesc);
+    }
+
+    /**
+     * Method called to find filter that is configured to be used with bean
+     * serializer being built, if any.
+     */
+    protected Object findFilterId(SerializationConfig config, BeanDescription beanDesc)
+    {
+        return config.getAnnotationIntrospector().findFilterId(beanDesc.getClassInfo());
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridable non-public introspection methods
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method used to skip processing for types that we know
+     * can not 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
+     * types anyway; but better safe than sorry.
+     */
+    protected boolean isPotentialBeanType(Class<?> type)
+    {
+        return (ClassUtil.canBeABeanType(type) == null) && !ClassUtil.isProxyType(type);
+    }
+
+    /**
+     * Method used to collect all actual serializable properties.
+     * Can be overridden to implement custom detection schemes.
+     */
+    protected List<BeanPropertyWriter> findBeanProperties(SerializerProvider prov,
+            BeanDescription beanDesc, BeanSerializerBuilder builder)
+        throws JsonMappingException
+    {
+        List<BeanPropertyDefinition> properties = beanDesc.findProperties();
+        final SerializationConfig config = prov.getConfig();
+
+        // [JACKSON-429]: ignore specified types
+        removeIgnorableTypes(config, beanDesc, properties);
+        
+        // and possibly remove ones without matching mutator...
+        if (config.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS)) {
+            removeSetterlessGetters(config, beanDesc, properties);
+        }
+        
+        // nothing? can't proceed (caller may or may not throw an exception)
+        if (properties.isEmpty()) {
+            return null;
+        }
+        // null is for value type serializer, which we don't have access to from here (ditto for bean prop)
+        boolean staticTyping = usesStaticTyping(config, beanDesc, null);
+        PropertyBuilder pb = constructPropertyBuilder(config, beanDesc);
+        
+        ArrayList<BeanPropertyWriter> result = new ArrayList<BeanPropertyWriter>(properties.size());
+        TypeBindings typeBind = beanDesc.bindingsForBeanType();
+        for (BeanPropertyDefinition property : properties) {
+            final AnnotatedMember accessor = property.getAccessor();
+            // [JACKSON-762]: type id? Requires special handling:
+            if (property.isTypeId()) {
+                if (accessor != null) { // only add if we can access... but otherwise?
+                    if (config.canOverrideAccessModifiers()) {
+                        accessor.fixAccess();
+                    }
+                    builder.setTypeId(accessor);
+                }
+                continue;
+            }
+            // [JACKSON-235]: suppress writing of back references
+            AnnotationIntrospector.ReferenceProperty refType = property.findReferenceType();
+            if (refType != null && refType.isBackReference()) {
+                continue;
+            }
+            if (accessor instanceof AnnotatedMethod) {
+                result.add(_constructWriter(prov, property, typeBind, pb, staticTyping, (AnnotatedMethod) accessor));
+            } else {
+                result.add(_constructWriter(prov, property, typeBind, pb, staticTyping, (AnnotatedField) accessor));
+            }
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable non-public methods for manipulating bean properties
+    /**********************************************************
+     */
+    
+    /**
+     * Overridable method that can filter out properties. Default implementation
+     * checks annotations class may have.
+     */
+    protected List<BeanPropertyWriter> filterBeanProperties(SerializationConfig config,
+            BeanDescription beanDesc, List<BeanPropertyWriter> props)
+    {
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        AnnotatedClass ac = beanDesc.getClassInfo();
+        String[] ignored = intr.findPropertiesToIgnore(ac);
+        if (ignored != null && ignored.length > 0) {
+            HashSet<String> ignoredSet = ArrayBuilders.arrayToSet(ignored);
+            Iterator<BeanPropertyWriter> it = props.iterator();
+            while (it.hasNext()) {
+                if (ignoredSet.contains(it.next().getName())) {
+                    it.remove();
+                }
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Method called to handle view information for constructed serializer,
+     * based on bean property writers.
+     *<p>
+     * Note that this method is designed to be overridden by sub-classes
+     * if they want to provide custom view handling. As such it is not
+     * considered an internal implementation detail, and will be supported
+     * as part of API going forward.
+     */
+    protected void processViews(SerializationConfig config, BeanSerializerBuilder builder)
+    {
+        // [JACKSON-232]: whether non-annotated fields are included by default or not is configurable
+        List<BeanPropertyWriter> props = builder.getProperties();
+        boolean includeByDefault = config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION);
+        final int propCount = props.size();
+        int viewsFound = 0;
+        BeanPropertyWriter[] filtered = new BeanPropertyWriter[propCount];
+        // Simple: view information is stored within individual writers, need to combine:
+        for (int i = 0; i < propCount; ++i) {
+            BeanPropertyWriter bpw = props.get(i);
+            Class<?>[] views = bpw.getViews();
+            if (views == null) { // no view info? include or exclude by default?
+                if (includeByDefault) {
+                    filtered[i] = bpw;
+                }
+            } else {
+                ++viewsFound;
+                filtered[i] = constructFilteredBeanWriter(bpw, views);
+            }
+        }
+        // minor optimization: if no view info, include-by-default, can leave out filtering info altogether:
+        if (includeByDefault && viewsFound == 0) {
+            return;
+        }
+        builder.setFilteredProperties(filtered);
+    }
+
+    /**
+     * 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.
+     */
+    protected void removeIgnorableTypes(SerializationConfig config, BeanDescription beanDesc,
+            List<BeanPropertyDefinition> properties)
+    {
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        HashMap<Class<?>,Boolean> ignores = new HashMap<Class<?>,Boolean>();
+        Iterator<BeanPropertyDefinition> it = properties.iterator();
+        while (it.hasNext()) {
+            BeanPropertyDefinition property = it.next();
+            AnnotatedMember accessor = property.getAccessor();
+            if (accessor == null) {
+                it.remove();
+                continue;
+            }
+            Class<?> type = accessor.getRawType();
+            Boolean result = ignores.get(type);
+            if (result == null) {
+                BeanDescription desc = config.introspectClassAnnotations(type);
+                AnnotatedClass ac = desc.getClassInfo();
+                result = intr.isIgnorableType(ac);
+                // default to false, non-ignorable
+                if (result == null) {
+                    result = Boolean.FALSE;
+                }
+                ignores.put(type, result);
+            }
+            // lotsa work, and yes, it is ignorable type, so:
+            if (result.booleanValue()) {
+                it.remove();
+            }
+        }
+    }
+
+    /**
+     * Helper method that will remove all properties that do not have a mutator.
+     */
+    protected void removeSetterlessGetters(SerializationConfig config, BeanDescription beanDesc,
+            List<BeanPropertyDefinition> properties)
+    {
+        Iterator<BeanPropertyDefinition> it = properties.iterator();
+        while (it.hasNext()) {
+            BeanPropertyDefinition property = it.next();
+            // one caveat: as per [JACKSON-806], only remove implicit properties;
+            // explicitly annotated ones should remain
+            if (!property.couldDeserialize() && !property.isExplicitlyIncluded()) {
+                it.remove();
+            }
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Secondary helper method for constructing {@link BeanPropertyWriter} for
+     * given member (field or method).
+     */
+    protected BeanPropertyWriter _constructWriter(SerializerProvider prov,
+            BeanPropertyDefinition propDef, TypeBindings typeContext,
+            PropertyBuilder pb, boolean staticTyping, AnnotatedMember accessor)
+        throws JsonMappingException
+    {
+        final String name = propDef.getName();
+        if (prov.canOverrideAccessModifiers()) {
+            accessor.fixAccess();
+        }
+        JavaType type = accessor.getType(typeContext);
+        BeanProperty.Std property = new BeanProperty.Std(name, type, propDef.getWrapperName(),
+                pb.getClassAnnotations(), accessor, propDef.isRequired());
+
+        // Does member specify a serializer? If so, let's use it.
+        JsonSerializer<?> annotatedSerializer = findSerializerFromAnnotation(prov,
+                accessor);
+        /* 02-Feb-2012, tatu: Unlike most other codepaths, Serializer produced
+         *  here will NOT be resolved or contextualized, unless done here, so:
+         */
+        if (annotatedSerializer instanceof ResolvableSerializer) {
+            ((ResolvableSerializer) annotatedSerializer).resolve(prov);
+        }
+        if (annotatedSerializer instanceof ContextualSerializer) {
+            annotatedSerializer = ((ContextualSerializer) annotatedSerializer).createContextual(prov, property);
+        }
+        // And how about polymorphic typing? First special to cover JAXB per-field settings:
+        TypeSerializer contentTypeSer = null;
+        if (ClassUtil.isCollectionMapOrArray(type.getRawClass())) {
+            contentTypeSer = findPropertyContentTypeSerializer(type, prov.getConfig(), accessor);
+        }
+
+        // and if not JAXB collection/array with annotations, maybe regular type info?
+        TypeSerializer typeSer = findPropertyTypeSerializer(type, prov.getConfig(), accessor);
+        BeanPropertyWriter pbw = pb.buildWriter(propDef, type, annotatedSerializer,
+                        typeSer, contentTypeSer, accessor, staticTyping);
+        return pbw;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java
new file mode 100644
index 0000000..c0e9495
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/BeanSerializerModifier.java
@@ -0,0 +1,187 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.List;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.DeserializerFactory;
+import com.fasterxml.jackson.databind.type.*;
+
+/**
+ * Abstract class that defines API for objects that can be registered (for {@link BeanSerializerFactory}
+ * to participate in constructing {@link BeanSerializer} instances.
+ * This is typically done by modules that want alter some aspects of serialization
+ * process; and is preferable to sub-classing of {@link BeanSerializerFactory}.
+ *<p>
+ * Sequence in which callback methods are called is as follows:
+ * <ol>
+ *  <li>After factory has collected tentative set of properties (instances of
+ *     <code>BeanPropertyWriter</code>) is sent for modification via
+ *     {@link #changeProperties}. Changes can include removal, addition and
+ *     replacement of suggested properties.
+ *  <li>Resulting set of properties are ordered (sorted) by factory, as per
+ *     configuration, and then {@link #orderProperties} is called to allow
+ *     modifiers to alter ordering.
+ *  <li>After all bean properties and related information is accumulated,
+ *     {@link #updateBuilder} is called with builder, to allow builder state
+ *     to be modified (including possibly replacing builder itself if necessary)
+ *  <li>Once all bean information has been determined,
+ *     factory creates default {@link BeanSerializer} instance and passes
+ *     it to modifiers using {@link #modifySerializer}, for possible
+ *     modification or replacement (by any {@link com.fasterxml.jackson.databind.JsonSerializer} instance)
+ * </ol>
+ *<p>
+ * Default method implementations are "no-op"s, meaning that methods are implemented
+ * but have no effect.
+ */
+public abstract class BeanSerializerModifier
+{
+    /**
+     * Method called by {@link BeanSerializerFactory} with tentative set
+     * of discovered properties.
+     * Implementations can add, remove or replace any of passed properties.
+     *
+     * Properties <code>List</code> passed as argument is modifiable, and returned List must
+     * likewise be modifiable as it may be passed to multiple registered
+     * modifiers.
+     */
+    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
+            BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
+        return beanProperties;
+    }
+
+    /**
+     * Method called by {@link BeanSerializerFactory} with set of properties
+     * to serialize, in default ordering (based on defaults as well as 
+     * possible type annotations).
+     * Implementations can change ordering any way they like.
+     *
+     * Properties <code>List</code> passed as argument is modifiable, and returned List must
+     * likewise be modifiable as it may be passed to multiple registered
+     * modifiers.
+     */
+    public List<BeanPropertyWriter> orderProperties(SerializationConfig config,
+            BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
+        return beanProperties;
+    }
+
+    /**
+     * Method called by {@link BeanSerializerFactory} after collecting all information
+     * regarding POJO to serialize and updating builder with it, but before constructing
+     * serializer.
+     * Implementations may choose to modify state of builder (to affect serializer being
+     * built), or even completely replace it (if they want to build different kind of
+     * serializer). Typically, however, passed-in builder is returned, possibly with
+     * some modifications.
+     */
+    public BeanSerializerBuilder updateBuilder(SerializationConfig config,
+            BeanDescription beanDesc, BeanSerializerBuilder builder) {
+        return builder;
+    }
+    
+    /**
+     * Method called by {@link BeanSerializerFactory} after constructing default
+     * bean serializer instance with properties collected and ordered earlier.
+     * Implementations can modify or replace given serializer and return serializer
+     * to use. Note that although initial serializer being passed is of type
+     * {@link BeanSerializer}, modifiers may return serializers of other types;
+     * and this is why implementations must check for type before casting.
+     *<p>
+     * NOTE: since 2.2, gets called for serializer of those non-POJO types that
+     * do not go through any of more specific <code>modifyXxxSerializer</code>
+     * methods; mostly for JDK types like {@link java.util.Iterator} and such.
+     */
+    public JsonSerializer<?> modifySerializer(SerializationConfig config,
+            BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Callback methods for other types (since 2.2)
+    /**********************************************************
+     */
+
+    /**
+     * Method called by {@link DeserializerFactory} after it has constructed the
+     * standard serializer for given
+     * {@link ArrayType}
+     * to make it possible to either replace or augment this serializer with
+     * additional functionality.
+     * 
+     * @param config Configuration in use
+     * @param valueType Type of the value serializer is used for.
+     * @param beanDesc Details of the type in question, to allow checking class annotations
+     * @param serializer Default serializer that would be used.
+     * 
+     * @return Serializer to use; either <code>serializer</code> that was passed
+     *   in, or an instance method constructed.
+     * 
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyArraySerializer(SerializationConfig config,
+            ArrayType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyCollectionSerializer(SerializationConfig config,
+            CollectionType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyCollectionLikeSerializer(SerializationConfig config,
+            CollectionLikeType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+    
+    /**
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyMapSerializer(SerializationConfig config,
+            MapType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyMapLikeSerializer(SerializationConfig config,
+            MapLikeType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyEnumSerializer(SerializationConfig config,
+            JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+
+    /**
+     * Method called by {@link DeserializerFactory} after it has constructed the
+     * default key serializer to use for serializing {@link java.util.Map} keys of
+     * given type.
+     * This makes it possible to either replace or augment default serializer with
+     * additional functionality.
+     * 
+     * @param config Configuration in use
+     * @param valueType Type of keys the serializer is used for.
+     * @param beanDesc Details of the type in question, to allow checking class annotations
+     * @param serializer Default serializer that would be used.
+     * 
+     * @return Serializer to use; either <code>serializer</code> that was passed
+     *   in, or an instance method constructed.
+     * 
+     * @since 2.2
+     */
+    public JsonSerializer<?> modifyKeySerializer(SerializationConfig config,
+            JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+        return serializer;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
new file mode 100644
index 0000000..df5f5a0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContainerSerializer.java
@@ -0,0 +1,135 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+/**
+ * Intermediate base class for serializers used for serializing
+ * types that contain element(s) of other types, such as arrays,
+ * {@link java.util.Collection}s (<code>Lists</code>, <code>Sets</code>
+ * etc) and {@link java.util.Map}s and iterable things
+ * ({@link java.util.Iterator}s).
+ */
+public abstract class ContainerSerializer<T>
+    extends StdSerializer<T>
+{
+    /*
+    /**********************************************************
+    /* Construction, initialization
+    /**********************************************************
+     */
+
+    protected ContainerSerializer(Class<T> t) {
+        super(t);
+    }
+    
+    /**
+     * Alternate constructor that is (alas!) needed to work
+     * around kinks of generic type handling
+     * 
+     * @param t
+     */
+    protected ContainerSerializer(Class<?> t, boolean dummy) {
+        super(t, dummy);
+    }
+
+    protected ContainerSerializer(ContainerSerializer<?> src) {
+        super(src._handledType, false);
+    }
+    
+    /**
+     * Factory(-like) method that can be used to construct a new container
+     * serializer that uses specified {@link TypeSerializer} for decorating
+     * contained values with additional type information.
+     * 
+     * @param vts Type serializer to use for contained values; can be null,
+     *    in which case 'this' serializer is returned as is
+     * @return Serializer instance that uses given type serializer for values if
+     *    that is possible (or if not, just 'this' serializer)
+     */
+    public ContainerSerializer<?> withValueTypeSerializer(TypeSerializer vts) {
+        if (vts == null) return this;
+        return _withValueTypeSerializer(vts);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for finding declared (static) element type for
+     * type this serializer is used for.
+     */
+    public abstract JavaType getContentType();
+
+    /**
+     * Accessor for serializer used for serializing contents
+     * (List and array elements, Map values etc) of the
+     * container for which this serializer is used, if it is
+     * known statically.
+     * Note that for dynamic types this may return null; if so,
+     * caller has to instead use {@link #getContentType()} and
+     * {@link com.fasterxml.jackson.databind.SerializerProvider#findValueSerializer}.
+     */
+    public abstract JsonSerializer<?> getContentSerializer();
+    
+    /*
+    /**********************************************************
+    /* Abstract methods for sub-classes to implement
+    /**********************************************************
+     */
+    
+    /* Overridden as abstract, to force re-implementation; necessary for all
+     * collection types.
+     */
+    @Override
+    public abstract boolean isEmpty(T value);
+
+    /**
+     * Method called to determine if the given value (of type handled by
+     * this serializer) contains exactly one element.
+     *<p>
+     * Note: although it might seem sensible to instead define something
+     * like "getElementCount()" method, this would not work well for
+     * containers that do not keep track of size (like linked lists may
+     * not).
+     */
+    public abstract boolean hasSingleElement(T value);
+    
+    /**
+     * Method that needs to be implemented to allow construction of a new
+     * serializer object with given {@link TypeSerializer}, used when
+     * addition type information is to be embedded.
+     */
+    protected abstract ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts);
+
+    /*
+    /**********************************************************
+    /* Helper methods for sub-types
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used to encapsulate logic for determining whether there is
+     * a property annotation that overrides element type; if so, we can
+     * and need to statically find the serializer.
+     * 
+     * @since 2.1
+     */
+    protected boolean hasContentTypeAnnotation(SerializerProvider provider,
+            BeanProperty property)
+    {
+        if (property != null) {
+            AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+            if (intr != null) {
+                if (intr.findSerializationContentType(property.getMember(), property.getType()) != null) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java
new file mode 100644
index 0000000..3bb3161
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ContextualSerializer.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Add-on interface that {@link JsonSerializer}s can implement to get a callback
+ * that can be used to create contextual instances of serializer to use for
+ * handling properties of supported type. This can be useful
+ * for serializers that can be configured by annotations, or should otherwise
+ * have differing behavior depending on what kind of property is being serialized.
+ *<p>
+ * Note that in cases where serializer needs both contextualization and
+ * resolution -- that is, implements both this interface and {@link ResolvableSerializer}
+ * -- resolution via {@link ResolvableSerializer} occurs first, and contextual
+ * resolution (via this interface) later on.
+ */
+public interface ContextualSerializer
+{
+    /**
+     * Method called to see if a different (or differently configured) serializer
+     * is needed to serialize values of specified property.
+     * Note that instance that this method is called on is typically shared one and
+     * as a result method should <b>NOT</b> modify this instance but rather construct
+     * and return a new instance. This instance should only be returned as-is, in case
+     * it is already suitable for use.
+     * 
+     * @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
+     *   null is passed instead (in which case impls usually pass 'this' serializer as is)
+     * 
+     * @return Serializer to use for serializing values of specified property;
+     *   may be this instance or a new instance.
+     * 
+     * @throws JsonMappingException
+     */
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
new file mode 100644
index 0000000..e94002c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/DefaultSerializerProvider.java
@@ -0,0 +1,446 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.NoClass;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+/**
+ * Standard implementation used by {@link ObjectMapper}:
+ * adds methods only exposed to {@link ObjectMapper},
+ * as well as constructors.
+ *<p>
+ * Note that class is abstract just because it does not
+ * define {@link #createInstance} method.
+ *<p>
+ * Also note that all custom {@link SerializerProvider}
+ * implementations must sub-class this class: {@link ObjectMapper}
+ * requires this type, not basic provider type.
+ */
+public abstract class DefaultSerializerProvider
+    extends SerializerProvider
+    implements java.io.Serializable // since 2.1; only because ObjectWriter needs it
+{
+    private static final long serialVersionUID = 1L;
+
+    /*
+    /**********************************************************
+    /* State, for non-blueprint instances: Object Id handling
+    /**********************************************************
+     */
+
+    /**
+     * Per-serialization map Object Ids that have seen so far, iff
+     * Object Id handling is enabled.
+     */
+    protected transient IdentityHashMap<Object, WritableObjectId> _seenObjectIds;
+    
+    protected transient ArrayList<ObjectIdGenerator<?>> _objectIdGenerators;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected DefaultSerializerProvider() { super(); }
+
+    protected DefaultSerializerProvider(SerializerProvider src,
+            SerializationConfig config,SerializerFactory f) {
+        super(src, config, f);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API: methods that ObjectMapper will call
+    /**********************************************************
+     */
+
+    /**
+     * Overridable method, used to create a non-blueprint instances from the blueprint.
+     * This is needed to retain state during serialization.
+     */
+    public abstract DefaultSerializerProvider createInstance(SerializationConfig config,
+            SerializerFactory jsf);
+    
+    /**
+     * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+     * for serializing given value, using serializers that
+     * this provider has access to (via caching and/or creating new serializers
+     * as need be).
+     */
+    public void serializeValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonGenerationException
+    {
+        JsonSerializer<Object> ser;
+        final boolean wrap;
+
+        if (value == null) { // no type provided; must just use the default null serializer
+            ser = getDefaultNullValueSerializer();
+            wrap = false; // no name to use for wrapping; can't do!
+        } else {
+            Class<?> cls = value.getClass();
+            // true, since we do want to cache root-level typed serializers (ditto for null property)
+            ser = findTypedValueSerializer(cls, true, null);
+
+            // Ok: should we wrap result in an additional property ("root name")?
+            String rootName = _config.getRootName();
+            if (rootName == null) { // not explicitly specified
+                // [JACKSON-163]
+                wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+                if (wrap) {
+                    jgen.writeStartObject();
+                    jgen.writeFieldName(_rootNames.findRootName(value.getClass(), _config));
+                }
+            } else if (rootName.length() == 0) {
+                wrap = false;
+            } else { // [JACKSON-764]
+                // empty String means explicitly disabled; non-empty that it is enabled
+                wrap = true;
+                jgen.writeStartObject();
+                jgen.writeFieldName(rootName);
+            }
+        }
+        try {
+            ser.serialize(value, jgen, this);
+            if (wrap) {
+                jgen.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(msg, e);
+        }
+    }
+
+    /**
+     * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+     * for serializing given value (assumed to be of specified root type,
+     * instead of runtime type of value),
+     * using serializers that
+     * this provider has access to (via caching and/or creating new serializers
+     * as need be),
+     * 
+     * @param rootType Type to use for locating serializer to use, instead of actual
+     *    runtime type. Must be actual type, or one of its super types
+     */
+    public void serializeValue(JsonGenerator jgen, Object value, JavaType rootType)
+        throws IOException, JsonGenerationException
+    {
+        final boolean wrap;
+
+        JsonSerializer<Object> ser;
+        if (value == null) {
+            ser = getDefaultNullValueSerializer();
+            wrap = false;
+        } else {
+            // Let's ensure types are compatible at this point
+            if (!rootType.getRawClass().isAssignableFrom(value.getClass())) {
+                _reportIncompatibleRootType(value, rootType);
+            }
+            // root value, not reached via property:
+            ser = findTypedValueSerializer(rootType, true, null);
+            // [JACKSON-163]
+            wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+            if (wrap) {
+                jgen.writeStartObject();
+                jgen.writeFieldName(_rootNames.findRootName(rootType, _config));
+            }
+        }
+        try {
+            ser.serialize(value, jgen, this);
+            if (wrap) {
+                jgen.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()+"]";
+            }
+            throw new JsonMappingException(msg, e);
+        }
+    }
+
+    /**
+     * The method to be called by {@link ObjectWriter}
+     * for serializing given value (assumed to be of specified root type,
+     * instead of runtime type of value), when it may know specific
+     * {@link JsonSerializer} to use.
+     * 
+     * @param rootType Type to use for locating serializer to use, instead of actual
+     *    runtime type, if no serializer is passed
+     * @param ser Root Serializer to use, if not null
+     * 
+     * @since 2.1
+     */
+    public void serializeValue(JsonGenerator jgen, Object value, JavaType rootType,
+            JsonSerializer<Object> ser)
+        throws IOException, JsonGenerationException
+    {
+        final boolean wrap;
+
+        if (value == null) {
+            ser = getDefaultNullValueSerializer();
+            wrap = false;
+        } else {
+            // Let's ensure types are compatible at this point
+            if (rootType != null) {
+                if (!rootType.getRawClass().isAssignableFrom(value.getClass())) {
+                    _reportIncompatibleRootType(value, rootType);
+                }
+            }
+            // root value, not reached via property:
+            if (ser == null) {
+                ser = findTypedValueSerializer(rootType, true, null);
+            }
+            wrap = _config.isEnabled(SerializationFeature.WRAP_ROOT_VALUE);
+            if (wrap) {
+                jgen.writeStartObject();
+                jgen.writeFieldName(_rootNames.findRootName(rootType, _config));
+            }
+        }
+        try {
+            ser.serialize(value, jgen, this);
+            if (wrap) {
+                jgen.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()+"]";
+            }
+            throw new JsonMappingException(msg, e);
+        }
+    }
+    
+    /**
+     * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+     * to generate <a href="http://json-schema.org/">JSON schema</a> for
+     * given type.
+     *
+     * @param type The type for which to generate schema
+     */
+    @SuppressWarnings("deprecation")
+    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)
+         */
+        JsonSerializer<Object> ser = findValueSerializer(type, null);
+        JsonNode schemaNode = (ser instanceof SchemaAware) ?
+                ((SchemaAware) ser).getSchema(this, null) : com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+        if (!(schemaNode instanceof ObjectNode)) {
+            throw new IllegalArgumentException("Class " + type.getName()
+                    +" would not be serialized as a JSON object and therefore has no schema");
+        }
+        return new com.fasterxml.jackson.databind.jsonschema.JsonSchema((ObjectNode) schemaNode);
+    }
+    
+    /**
+     * The method to be called by {@link ObjectMapper} and {@link ObjectWriter}
+     * to to expose the format of the given to to the given visitor
+     *
+     * @param javaType The type for which to generate format
+     * @param visitor the visitor to accept the format
+     */
+    public void acceptJsonFormatVisitor(JavaType javaType, JsonFormatVisitorWrapper visitor)
+        throws JsonMappingException
+    {
+        if (javaType == 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)
+         */
+        visitor.setProvider(this);
+        findValueSerializer(javaType, null).acceptJsonFormatVisitor(visitor, javaType);
+    }
+    
+    /**
+     * Method that can be called to see if this serializer provider
+     * can find a serializer for an instance of given class.
+     *<p>
+     * Note that no Exceptions are thrown, including unchecked ones:
+     * implementations are to swallow exceptions if necessary.
+     */
+    public boolean hasSerializerFor(Class<?> cls) {
+    	try {
+    		return _findExplicitUntypedSerializer(cls) != null;
+    	} catch (JsonMappingException e) {
+    		// usually bad practice, but here caller only asked if a serializer
+    		// could be found; for which exception is useless
+    		return false;
+    	}
+    }
+
+    /*
+    /********************************************************
+    /* Access to caching details
+    /********************************************************
+     */
+
+    /**
+     * Method that can be used to determine how many serializers this
+     * provider is caching currently
+     * (if it does caching: default implementation does)
+     * Exact count depends on what kind of serializers get cached;
+     * default implementation caches all serializers, including ones that
+     * are eagerly constructed (for optimal access speed)
+     *<p> 
+     * The main use case for this method is to allow conditional flushing of
+     * serializer cache, if certain number of entries is reached.
+     */
+    public int cachedSerializersCount() {
+        return _serializerCache.size();
+    }
+
+    /**
+     * Method that will drop all serializers currently cached by this provider.
+     * This can be used to remove memory usage (in case some serializers are
+     * only used once or so), or to force re-construction of serializers after
+     * configuration changes for mapper than owns the provider.
+     */
+    public void flushCachedSerializers() {
+        _serializerCache.flush();
+    }
+
+    /*
+    /**********************************************************
+    /* Object Id handling
+    /**********************************************************
+     */
+    
+    @Override
+    public WritableObjectId findObjectId(Object forPojo,
+            ObjectIdGenerator<?> generatorType)
+    {
+        if (_seenObjectIds == null) {
+            _seenObjectIds = new IdentityHashMap<Object,WritableObjectId>();
+        } else {
+            WritableObjectId oid = _seenObjectIds.get(forPojo);
+            if (oid != null) {
+                return oid;
+            }
+        }
+        // Not seen yet; must add an entry, return it. For that, we need generator
+        ObjectIdGenerator<?> generator = null;
+        
+        if (_objectIdGenerators == null) {
+            _objectIdGenerators = new ArrayList<ObjectIdGenerator<?>>(8);
+        } else {
+            for (int i = 0, len = _objectIdGenerators.size(); i < len; ++i) {
+                ObjectIdGenerator<?> gen = _objectIdGenerators.get(i);
+                if (gen.canUseFor(generatorType)) {
+                    generator = gen;
+                    break;
+                }
+            }
+        }
+        if (generator == null) {
+            generator = generatorType.newForSerialization(this);
+            _objectIdGenerators.add(generator);
+        }
+        WritableObjectId oid = new WritableObjectId(generator);
+        _seenObjectIds.put(forPojo, oid);
+        return oid;
+    }
+
+    /*
+    /**********************************************************
+    /* Factory method impls
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonSerializer<Object> serializerInstance(Annotated annotated,
+            Object serDef)
+        throws JsonMappingException
+    
+    {
+        if (serDef == null) {
+            return null;
+        }
+        JsonSerializer<?> ser;
+        
+        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
+             */
+            if (!(serDef instanceof Class)) {
+                throw new IllegalStateException("AnnotationIntrospector returned serializer definition of type "
+                        +serDef.getClass().getName()+"; expected type JsonSerializer or Class<JsonSerializer> instead");
+            }
+            Class<?> serClass = (Class<?>)serDef;
+            // there are some known "no class" markers to consider too:
+            if (serClass == JsonSerializer.None.class || serClass == NoClass.class) {
+                return null;
+            }
+            if (!JsonSerializer.class.isAssignableFrom(serClass)) {
+                throw new IllegalStateException("AnnotationIntrospector returned Class "
+                        +serClass.getName()+"; expected Class<JsonSerializer>");
+            }
+            HandlerInstantiator hi = _config.getHandlerInstantiator();
+            ser = (hi == null) ? null : hi.serializerInstance(_config, annotated, serClass);
+            if (ser == null) {
+                ser = (JsonSerializer<?>) ClassUtil.createInstance(serClass,
+                        _config.canOverrideAccessModifiers());
+            }
+        }
+        return (JsonSerializer<Object>) _handleResolvable(ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Concrete implementation that defines factory method(s),
+     * defined as final.
+     */
+    public final static class Impl extends DefaultSerializerProvider
+    {
+        private static final long serialVersionUID = 1L;
+
+        public Impl() { super(); }
+
+        protected Impl(SerializerProvider src,
+                SerializationConfig config,SerializerFactory f) {
+            super(src, config, f);
+        }
+
+        @Override
+        public Impl createInstance(SerializationConfig config,
+                SerializerFactory jsf) {
+            return new Impl(this, config, jsf);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java
new file mode 100644
index 0000000..115c05b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/FilterProvider.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.databind.ser;
+
+/**
+ * Interface for objects that providers instances of {@link BeanPropertyFilter}
+ * that match given ids. A provider is configured to be used during serialization,
+ * to find filter to used based on id specified by {@link com.fasterxml.jackson.annotation.JsonFilter}
+ * annotation on bean class.
+ */
+public abstract class FilterProvider
+{
+    /**
+     * Lookup method used to find {@link BeanPropertyFilter} that has specified id.
+     * Note that id is typically a {@link java.lang.String}, but is not necessarily
+     * limited to that; that is, while standard components use String, custom
+     * implementation can choose other kinds of keys.
+     * 
+     * @return Filter registered with specified id, if one defined; null if
+     *   none found.
+     */
+    public abstract BeanPropertyFilter findFilter(Object filterId);
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
new file mode 100644
index 0000000..97e8da3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/PropertyBuilder.java
@@ -0,0 +1,239 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize.Inclusion;
+import com.fasterxml.jackson.databind.introspect.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.util.*;
+
+/**
+ * Helper class for {@link BeanSerializerFactory} that is used to
+ * construct {@link BeanPropertyWriter} instances. Can be sub-classed
+ * to change behavior.
+ */
+public class PropertyBuilder
+{
+    final protected SerializationConfig _config;
+    final protected BeanDescription _beanDesc;
+    final protected JsonInclude.Include _outputProps;
+
+    final protected AnnotationIntrospector _annotationIntrospector;
+
+    /**
+     * If a property has serialization inclusion value of
+     * {@link Inclusion#ALWAYS}, we need to know the default
+     * value of the bean, to know if property value equals default
+     * one.
+     */
+    protected Object _defaultBean;
+
+    public PropertyBuilder(SerializationConfig config, BeanDescription beanDesc)
+    {
+        _config = config;
+        _beanDesc = beanDesc;
+        _outputProps = beanDesc.findSerializationInclusion(config.getSerializationInclusion());
+        _annotationIntrospector = _config.getAnnotationIntrospector();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    public Annotations getClassAnnotations() {
+        return _beanDesc.getClassAnnotations();
+    }
+    
+    /**
+     * @param contentTypeSer Optional explicit type information serializer
+     *    to use for contained values (only used for properties that are
+     *    of container type)
+     */
+    protected BeanPropertyWriter buildWriter(BeanPropertyDefinition propDef,
+            JavaType declaredType, JsonSerializer<?> ser,
+            TypeSerializer typeSer, TypeSerializer contentTypeSer,
+            AnnotatedMember am, boolean defaultUseStaticTyping)
+    {
+        // do we have annotation that forces type to use (to declared type or its super type)?
+        JavaType serializationType = findSerializationType(am, defaultUseStaticTyping, declaredType);
+
+        // 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.
+             */
+            if (serializationType == null) {
+//                serializationType = TypeFactory.type(am.getGenericType(), _beanDesc.getType());
+                serializationType = declaredType;
+            }
+            JavaType ct = serializationType.getContentType();
+            /* 03-Sep-2010, tatu: This is somehow related to [JACKSON-356], but I don't completely
+             *   yet understand how pieces fit together. Still, better be explicit than rely on
+             *   NPE to indicate an issue...
+             */
+            if (ct == null) {
+                throw new IllegalStateException("Problem trying to create BeanPropertyWriter for property '"
+                        +propDef.getName()+"' (of type "+_beanDesc.getType()+"); serialization type "+serializationType+" has no content");
+            }
+            serializationType = serializationType.withContentTypeHandler(contentTypeSer);
+            ct = serializationType.getContentType();
+        }
+        
+        Object valueToSuppress = null;
+        boolean suppressNulls = false;
+
+        JsonInclude.Include methodProps = _annotationIntrospector.findSerializationInclusion(am, _outputProps);
+        if (methodProps != null) {
+            switch (methodProps) {
+            case NON_DEFAULT:
+                valueToSuppress = getDefaultValue(propDef.getName(), am);
+                if (valueToSuppress == null) {
+                    suppressNulls = true;
+                } else {
+                    // [JACKSON-531]: Allow comparison of arrays too...
+                    if (valueToSuppress.getClass().isArray()) {
+                        valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
+                    }
+                }
+                break;
+            case NON_EMPTY:
+                // always suppress nulls
+                suppressNulls = true;
+                // but possibly also 'empty' values:
+                valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
+                break;
+            case NON_NULL:
+                suppressNulls = true;
+                // fall through
+            case ALWAYS: // default
+                // we may still want to suppress empty collections, as per [JACKSON-254]:
+                if (declaredType.isContainerType()
+                        && !_config.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS)) {
+                    valueToSuppress = BeanPropertyWriter.MARKER_FOR_EMPTY;
+                }
+                break;
+            }
+        }
+
+        BeanPropertyWriter bpw = new BeanPropertyWriter(propDef,
+                am, _beanDesc.getClassAnnotations(), declaredType,
+                ser, typeSer, serializationType, suppressNulls, valueToSuppress);
+        
+        // [JACKSON-132]: Unwrapping
+        NameTransformer unwrapper = _annotationIntrospector.findUnwrappingNameTransformer(am);
+        if (unwrapper != null) {
+            bpw = bpw.unwrappingWriter(unwrapper);
+        }
+        return bpw;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods; annotation access
+    /**********************************************************
+     */
+
+    /**
+     * Method that will try to determine statically defined type of property
+     * being serialized, based on annotations (for overrides), and alternatively
+     * declared type (if static typing for serialization is enabled).
+     * If neither can be used (no annotations, dynamic typing), returns null.
+     */
+    protected JavaType findSerializationType(Annotated a, boolean useStaticTyping, JavaType declaredType)
+    {
+        // [JACKSON-120]: Check to see if serialization type is fixed
+        Class<?> serClass = _annotationIntrospector.findSerializationType(a);
+        if (serClass != null) {
+            // Must be a super type to be usable
+            Class<?> rawDeclared = declaredType.getRawClass();
+            if (serClass.isAssignableFrom(rawDeclared)) {
+                declaredType = declaredType.widenBy(serClass);
+            } else {
+                /* 18-Nov-2010, tatu: Related to fixing [JACKSON-416], an issue with such
+                 *   check is that for deserialization more specific type makes sense;
+                 *   and for serialization more generic. But alas JAXB uses but a single
+                 *   annotation to do both... Hence, we must just discard type, as long as
+                 *   types are related
+                 */
+                if (!rawDeclared.isAssignableFrom(serClass)) {
+                    throw new IllegalArgumentException("Illegal concrete-type annotation for method '"+a.getName()+"': class "+serClass.getName()+" not a super-type of (declared) class "+rawDeclared.getName());
+                }
+                /* 03-Dec-2010, tatu: Actually, ugh, to resolve [JACKSON-415] may further relax this
+                 *   and actually accept subtypes too for serialization. Bit dangerous in theory
+                 *   but need to trust user here...
+                 */
+                declaredType = _config.constructSpecializedType(declaredType, serClass);
+            }
+            useStaticTyping = true;
+        }
+
+        JavaType secondary = BeanSerializerFactory.modifySecondaryTypesByAnnotation(_config, a, declaredType);
+        if (secondary != declaredType) {
+            useStaticTyping = true;
+            declaredType = secondary;
+        }
+        
+        /* [JACKSON-114]: if using static typing, declared type is known
+         * to be the type...
+         */
+        if (!useStaticTyping) {
+            JsonSerialize.Typing typing = _annotationIntrospector.findSerializationTyping(a);
+            if (typing != null) {
+                useStaticTyping = (typing == JsonSerialize.Typing.STATIC);
+            }
+        }
+        return useStaticTyping ? declaredType : null;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for default value handling
+    /**********************************************************
+     */
+    
+    protected Object getDefaultBean()
+    {
+        if (_defaultBean == null) {
+            /* If we can fix access rights, we should; otherwise non-public
+             * classes or default constructor will prevent instantiation
+             */
+            _defaultBean = _beanDesc.instantiateBean(_config.canOverrideAccessModifiers());
+            if (_defaultBean == null) {
+                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");
+            }
+        }
+        return _defaultBean;
+    }
+
+    protected Object getDefaultValue(String name, AnnotatedMember member)
+    {
+        Object defaultBean = getDefaultBean();
+        try {
+            return member.getValue(defaultBean);
+        } catch (Exception e) {
+            return _throwWrapped(e, name, defaultBean);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for exception handling
+    /**********************************************************
+     */
+    
+    protected Object _throwWrapped(Exception e, String propName, Object defaultBean)
+    {
+        Throwable t = e;
+        while (t.getCause() != null) {
+            t = t.getCause();
+        }
+        if (t instanceof Error) throw (Error) t;
+        if (t instanceof RuntimeException) throw (RuntimeException) 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/ResolvableSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java
new file mode 100644
index 0000000..c86d0c1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/ResolvableSerializer.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Interface used to indicate serializers that want to do post-processing
+ * after construction and being added to {@link SerializerProvider},
+ * but before being used. This is typically used to resolve references
+ * to other contained types; for example, bean serializers use this
+ * to eagerly find serializers for contained field types.
+ *<p>
+ * Note that in cases where serializer needs both contextualization and
+ * resolution -- that is, implements both this interface and {@link ContextualSerializer}
+ * -- resolution via this interface occurs first, and contextual
+ * resolution (using {@link ContextualSerializer}) later on.
+ */
+public interface ResolvableSerializer
+{
+    /**
+     * Method called after {@link SerializerProvider} has registered
+     * the serializer, but before it has returned it to the caller.
+     * Called object can then resolve its dependencies to other types,
+     * including self-references (direct or indirect).
+     *<p>
+     * Note that this method does NOT return serializer, since resolution
+     * is not allowed to change actual serializer to use.
+     *
+     * @param provider Provider that has constructed serializer this method
+     *   is called on.
+     */
+    public abstract void resolve(SerializerProvider provider)
+        throws JsonMappingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java
new file mode 100644
index 0000000..0d4b1f2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerCache.java
@@ -0,0 +1,292 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.impl.ReadOnlyClassToSerializerMap;
+
+/**
+ * Simple cache object that allows for doing 2-level lookups: first level is
+ * by "local" read-only lookup Map (used without locking)
+ * and second backup level is by a shared modifiable HashMap.
+ * The idea is that after a while, most serializers are found from the
+ * local Map (to optimize performance, reduce lock contention),
+ * but that during buildup we can use a shared map to reduce both
+ * number of distinct read-only maps constructed, and number of
+ * serializers constructed.
+ *<p>
+ * Since version 1.5 cache will actually contain three kinds of entries,
+ * based on combination of class pair key. First class in key is for the
+ * type to serialize, and second one is type used for determining how
+ * to resolve value type. One (but not both) of entries can be null.
+ */
+public final class SerializerCache
+{
+    /**
+     * Shared, modifiable map; all access needs to be through synchronized blocks.
+     *<p>
+     * NOTE: keys are of various types (see below for key types), in addition to
+     * basic {@link JavaType} used for "untyped" serializers.
+     */
+    private HashMap<TypeKey, JsonSerializer<Object>> _sharedMap = new HashMap<TypeKey, JsonSerializer<Object>>(64);
+
+    /**
+     * Most recent read-only instance, created from _sharedMap, if any.
+     */
+    private ReadOnlyClassToSerializerMap _readOnlyMap = null;
+
+    public SerializerCache() { }
+
+    /**
+     * Method that can be called to get a read-only instance populated from the
+     * most recent version of the shared lookup Map.
+     */
+    public ReadOnlyClassToSerializerMap getReadOnlyLookupMap()
+    {
+        ReadOnlyClassToSerializerMap m;
+        synchronized (this) {
+            m = _readOnlyMap;
+            if (m == null) {
+                _readOnlyMap = m = ReadOnlyClassToSerializerMap.from(_sharedMap);
+            }
+        }
+        return m.instance();
+    }
+
+    /*
+    /**********************************************************
+    /* Lookup methods for accessing shared (slow) cache
+    /**********************************************************
+     */
+
+    public synchronized int size() {
+        return _sharedMap.size();
+    }
+    
+    /**
+     * Method that checks if the shared (and hence, synchronized) lookup Map might have
+     * untyped serializer for given type.
+     */
+    public JsonSerializer<Object> untypedValueSerializer(Class<?> type)
+    {
+        synchronized (this) {
+            return _sharedMap.get(new TypeKey(type, false));
+        }
+    }
+
+    public JsonSerializer<Object> untypedValueSerializer(JavaType type)
+    {
+        synchronized (this) {
+            return _sharedMap.get(new TypeKey(type, false));
+        }
+    }
+
+    public JsonSerializer<Object> typedValueSerializer(JavaType type)
+    {
+        synchronized (this) {
+            return _sharedMap.get(new TypeKey(type, true));
+        }
+    }
+
+    public JsonSerializer<Object> typedValueSerializer(Class<?> cls)
+    {
+        synchronized (this) {
+            return _sharedMap.get(new TypeKey(cls, true));
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Methods for adding shared serializer instances
+    /**********************************************************
+     */
+    
+    /**
+     * Method called if none of lookups succeeded, and caller had to construct
+     * a serializer. If so, we will update the shared lookup map so that it
+     * can be resolved via it next time.
+     */
+    public void addTypedSerializer(JavaType type, JsonSerializer<Object> ser)
+    {
+        synchronized (this) {
+            if (_sharedMap.put(new TypeKey(type, true), ser) == null) {
+                // let's invalidate the read-only copy, too, to get it updated
+                _readOnlyMap = null;
+            }
+        }
+    }
+
+    public void addTypedSerializer(Class<?> cls, JsonSerializer<Object> ser)
+    {
+        synchronized (this) {
+            if (_sharedMap.put(new TypeKey(cls, true), ser) == null) {
+                // let's invalidate the read-only copy, too, to get it updated
+                _readOnlyMap = null;
+            }
+        }
+    }
+    
+    public void addAndResolveNonTypedSerializer(Class<?> type, JsonSerializer<Object> ser,
+            SerializerProvider provider)
+        throws JsonMappingException
+    {
+        synchronized (this) {
+            if (_sharedMap.put(new TypeKey(type, false), ser) == null) {
+                // let's invalidate the read-only copy, too, to get it updated
+                _readOnlyMap = null;
+            }
+            /* Finally: some serializers want to do post-processing, after
+             * getting registered (to handle cyclic deps).
+             */
+            /* 14-May-2011, tatu: As per [JACKSON-570], resolving needs to be done
+             *   in synchronized manner; this because while we do need to register
+             *   instance first, we also must keep lock until resolution is complete
+             */
+            if (ser instanceof ResolvableSerializer) {
+                ((ResolvableSerializer) ser).resolve(provider);
+            }
+        }
+    }
+
+    public void addAndResolveNonTypedSerializer(JavaType type, JsonSerializer<Object> ser,
+            SerializerProvider provider)
+        throws JsonMappingException
+    {
+        synchronized (this) {
+            if (_sharedMap.put(new TypeKey(type, false), ser) == null) {
+                // let's invalidate the read-only copy, too, to get it updated
+                _readOnlyMap = null;
+            }
+            /* Finally: some serializers want to do post-processing, after
+             * getting registered (to handle cyclic deps).
+             */
+            /* 14-May-2011, tatu: As per [JACKSON-570], resolving needs to be done
+             *   in synchronized manner; this because while we do need to register
+             *   instance first, we also must keep lock until resolution is complete
+             */
+            if (ser instanceof ResolvableSerializer) {
+                ((ResolvableSerializer) ser).resolve(provider);
+            }
+        }
+    }
+
+    /**
+     * Method called by StdSerializerProvider#flushCachedSerializers() to
+     * clear all cached serializers
+     */
+    public synchronized void flush() {
+        _sharedMap.clear();
+    }
+
+    /*
+    /**************************************************************
+    /* Helper class(es)
+    /**************************************************************
+     */
+
+    /**
+     * Key that offers two "modes"; one with raw class, as used for
+     * cases were raw class type is available (for example, when using
+     * runtime type); and one with full generics-including.
+     */
+    public final static class TypeKey
+    {
+        protected int _hashCode;
+
+        protected Class<?> _class;
+
+        protected JavaType _type;
+
+        /**
+         * Indicator of whether serializer stored has a type serializer
+         * wrapper around it or not; if not, it is "untyped" serializer;
+         * if it has, it is "typed"
+         */
+        protected boolean _isTyped;
+        
+        public TypeKey(Class<?> key, boolean typed) {
+            _class = key;
+            _type = null;
+            _isTyped = typed;
+            _hashCode = hash(key, typed);
+        }
+
+        public TypeKey(JavaType key, boolean typed) {
+            _type = key;
+            _class = null;
+            _isTyped = typed;
+            _hashCode = hash(key, typed);
+        }
+
+        private final static int hash(Class<?> cls, boolean typed) {
+            int hash = cls.getName().hashCode();
+            if (typed) {
+                ++hash;
+            }
+            return hash;
+        }
+
+        private final static int hash(JavaType type, boolean typed) {
+            int hash = type.hashCode() - 1;
+            if (typed) {
+                --hash;
+            }
+            return hash;
+        }
+        
+        public void resetTyped(Class<?> cls) {
+            _type = null;
+            _class = cls;
+            _isTyped = true;
+            _hashCode = hash(cls, true);
+        }
+
+        public void resetUntyped(Class<?> cls) {
+            _type = null;
+            _class = cls;
+            _isTyped = false;
+            _hashCode = hash(cls, false);
+        }
+        
+        public void resetTyped(JavaType type) {
+            _type = type;
+            _class = null;
+            _isTyped = true;
+            _hashCode = hash(type, true);
+        }
+
+        public void resetUntyped(JavaType type) {
+            _type = type;
+            _class = null;
+            _isTyped = false;
+            _hashCode = hash(type, false);
+        }
+        
+        @Override public final int hashCode() { return _hashCode; }
+
+        @Override public final String toString() {
+            if (_class != null) {
+                return "{class: "+_class.getName()+", typed? "+_isTyped+"}";
+            }
+            return "{type: "+_type+", typed? "+_isTyped+"}";
+        }
+        
+        // note: we assume key is never used for anything other than as map key, so:
+        @Override public final boolean equals(Object o)
+        {
+            if (o == null) return false;
+            if (o == this) return true;
+            if (o.getClass() != getClass()) {
+                return false;
+            }
+            TypeKey other = (TypeKey) o;
+            if (other._isTyped == _isTyped) {
+                if (_class != null) {
+                    return other._class == _class;
+                }
+                return _type.equals(other._type);
+            }
+            return false;
+        } 
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java
new file mode 100644
index 0000000..fd8671f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/SerializerFactory.java
@@ -0,0 +1,106 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Abstract class that defines API used by {@link SerializerProvider}
+ * to obtain actual
+ * {@link JsonSerializer} instances from multiple distinct factories.
+ */
+public abstract class SerializerFactory
+{
+    /*
+    /**********************************************************
+    /* Additional configuration methods
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for creating a new factory instance with additional serializer
+     * provider; equivalent to calling
+     *<pre>
+     *   withConfig(getConfig().withAdditionalSerializers(additional));
+     *<pre>
+     */
+    public abstract SerializerFactory withAdditionalSerializers(Serializers additional);
+
+    public abstract SerializerFactory withAdditionalKeySerializers(Serializers additional);
+    
+    /**
+     * Convenience method for creating a new factory instance with additional bean
+     * serializer modifier; equivalent to calling
+     *<pre>
+     *   withConfig(getConfig().withSerializerModifier(modifier));
+     *<pre>
+     */
+    public abstract SerializerFactory withSerializerModifier(BeanSerializerModifier modifier);
+    
+    /*
+    /**********************************************************
+    /* Basic SerializerFactory API:
+    /**********************************************************
+     */
+
+    /**
+     * @deprecated Since 2.1: need to use the new variant without 'property'
+     *    argument (since one won't be passed)
+     */
+    @Deprecated
+    public JsonSerializer<Object> createSerializer(SerializerProvider prov,
+            JavaType baseType, BeanProperty property)
+        throws JsonMappingException {
+        return createSerializer(prov, baseType);
+    }
+    
+    /**
+      * Method called to create (or, for immutable serializers, reuse) a serializer for given type. 
+      * 
+      * @param prov Provider that needs to be used to resolve annotation-provided
+      *    serializers (but NOT for others)
+      *    
+      * @since 2.1 (earlier versions had method with different signature)
+      */
+    public abstract JsonSerializer<Object> createSerializer(SerializerProvider prov,
+            JavaType baseType)
+        throws JsonMappingException;
+    
+    /**
+     * Method called to create a type information serializer for given base type,
+     * if one is needed. If not needed (no polymorphic handling configured), should
+     * return null.
+     *
+     * @param baseType Declared type to use as the base type for type information serializer
+     * 
+     * @return Type serializer to use for the base type, if one is needed; null if not.
+     */
+    public abstract TypeSerializer createTypeSerializer(SerializationConfig config,
+            JavaType baseType)
+        throws JsonMappingException;
+
+    /**
+     * Method called to create serializer to use for serializing JSON property names (which must
+     * be output as <code>JsonToken.FIELD_NAME</code>) for Map that has specified declared
+     * key type, and is for specified property (or, if property is null, as root value)
+     * 
+     * @param baseType Declared type for Map keys
+     * @param defaultImpl Default key serializer implementation to use, if no custom ones
+     *    are found (may be null)
+     * 
+     * @return Serializer to use, if factory knows it; null if not (in which case default
+     *   serializer is to be used)
+     */
+    public abstract JsonSerializer<Object> createKeySerializer(SerializationConfig config,
+            JavaType baseType, JsonSerializer<Object> defaultImpl)
+        throws JsonMappingException;
+
+    /**
+     * @deprecated Since 2.2, use one that takes additional <code>defaultImpl</code> parameter
+     */
+    @Deprecated
+    public JsonSerializer<Object> createKeySerializer(SerializationConfig config,
+            JavaType baseType)
+        throws JsonMappingException {
+        return createKeySerializer(config, baseType, null);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java
new file mode 100644
index 0000000..a15a8cf
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/Serializers.java
@@ -0,0 +1,142 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.type.*;
+
+/**
+ * Interface that defines API for simple extensions that can provide additional serializers
+ * for various types. Access is by a single callback method; instance is to either return
+ * a configured {@link JsonSerializer} for specified type, or null to indicate that it
+ * does not support handling of the type. In latter case, further calls can be made
+ * for other providers; in former case returned serializer is used for handling of
+ * instances of specified type.
+ */
+public interface Serializers
+{
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified type, which is not of a container type (for which other methods are
+     * called).
+     * 
+     * @param type Fully resolved type of instances to serialize
+     * @param config Serialization configuration in use
+     * @param beanDesc Additional information about type
+     *    
+     * @return Configured serializer to use for the type; or null if implementation
+     *    does not recognize or support type
+     */
+    public JsonSerializer<?> findSerializer(SerializationConfig config,
+            JavaType type, BeanDescription beanDesc);
+
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified array type.
+     * Implementation should return a serializer instance if it supports
+     * specified type; or null if it does not.
+     */
+    public JsonSerializer<?> findArraySerializer(SerializationConfig config,
+            ArrayType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
+
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified {@link java.util.Collection} type.
+     * Implementation should return a serializer instance if it supports
+     * specified type; or null if it does not.
+     */
+    public JsonSerializer<?> findCollectionSerializer(SerializationConfig config,
+            CollectionType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
+
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified "Collection-like" type (type that acts like {@link java.util.Collection},
+     * but does not implement it).
+     * Implementation should return a serializer instance if it supports
+     * specified type; or null if it does not.
+     */
+    public JsonSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
+            CollectionLikeType type, BeanDescription beanDesc,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
+    
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified {@link java.util.Map} type.
+     * Implementation should return a serializer instance if it supports
+     * specified type; or null if it does not.
+     */
+    public JsonSerializer<?> findMapSerializer(SerializationConfig config,
+            MapType type, BeanDescription beanDesc,
+            JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
+
+    /**
+     * Method called by serialization framework first time a serializer is needed for
+     * specified "Map-like" type (type that acts like {@link java.util.Map},
+     * but does not implement it).
+     * Implementation should return a serializer instance if it supports
+     * specified type; or null if it does not.
+     */
+    public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
+            MapLikeType type, BeanDescription beanDesc,
+            JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer);
+
+    /**
+     * Basic {@link Serializers} implementation that implements all methods but provides
+     * no serializers. Its main purpose is to serve as a base class so that
+     * sub-classes only need to override methods they need.
+     */
+    public static class Base implements Serializers
+    {
+        @Override
+        public JsonSerializer<?> findSerializer(SerializationConfig config,
+                JavaType type, BeanDescription beanDesc)
+        {
+            return null;
+        }
+        
+        @Override
+        public JsonSerializer<?> findArraySerializer(SerializationConfig config,
+                ArrayType type, BeanDescription beanDesc,
+                TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        {
+            return null;
+        }
+
+        @Override
+        public JsonSerializer<?> findCollectionSerializer(SerializationConfig config,
+                CollectionType type, BeanDescription beanDesc,
+                TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        {
+            return null;
+        }
+
+        @Override
+        public JsonSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
+                CollectionLikeType type, BeanDescription beanDesc,
+                TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        {
+            return null;
+        }
+            
+        @Override
+        public JsonSerializer<?> findMapSerializer(SerializationConfig config,
+                MapType type, BeanDescription beanDesc,
+                JsonSerializer<Object> keySerializer,
+                TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        {
+            return null;
+        }
+
+        @Override
+        public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
+                MapLikeType type, BeanDescription beanDesc,
+                JsonSerializer<Object> keySerializer,
+                TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+        {
+            return null;
+        }
+    }
+}
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
new file mode 100644
index 0000000..0f9546c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/BeanAsArraySerializer.java
@@ -0,0 +1,210 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Specialized POJO serializer that differs from
+ * {@link com.fasterxml.jackson.databind.ser.BeanSerializer}
+ * in that instead of producing a JSON Object it will output
+ * a JSON Array, omitting field names, and serializing values in
+ * specified serialization order.
+ * This behavior is usually triggered by using annotation
+ * {@link com.fasterxml.jackson.annotation.JsonFormat} or its
+ * equivalents.
+ *<p>
+ * This serializer can be used for "simple" instances; and will NOT
+ * be used if one of following is true:
+ *<ul>
+ * <li>Unwrapping is used (no way to expand out array in JSON Object)
+ *  </li>
+ * <li>Type information ("type id") is to be used: while this could work
+ *   for some embedding methods, it would likely cause conflicts.
+ *  </li>
+ * <li>Object Identity ("object id") is used: while references would work,
+ *    the problem is inclusion of id itself.
+ *  </li>
+ *</ul>
+ * Note that it is theoretically possible that last 2 issues could be addressed
+ * (by reserving room in array, for example); and if so, support improved.
+ *<p>
+ * In cases where array-based output is not feasible, this serializer
+ * can instead delegate to the original Object-based serializer; this
+ * is why a reference is retained to the original serializer.
+ * 
+ * @since 2.1
+ */
+public class BeanAsArraySerializer
+    extends BeanSerializerBase
+{
+    /**
+     * Serializer that would produce JSON Object version; used in
+     * cases where array output can not be used.
+     */
+    protected final BeanSerializerBase _defaultSerializer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: constructors
+    /**********************************************************
+     */
+
+
+    public BeanAsArraySerializer(BeanSerializerBase src) {    
+        super(src, (ObjectIdWriter) null);
+        _defaultSerializer = src;
+    }
+
+    protected BeanAsArraySerializer(BeanSerializerBase src, String[] toIgnore) {
+        super(src, toIgnore);
+        _defaultSerializer = src;
+    }
+
+    /*
+    /**********************************************************
+    /* Life-cycle: factory methods, fluent factories
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
+        /* If this gets called, we will just need delegate to the default
+         * serializer, to "undo" as-array serialization
+         */
+        return _defaultSerializer.unwrappingSerializer(transformer);
+    }
+
+    @Override
+    public boolean isUnwrappingSerializer() {
+        return false;
+    }
+
+    @Override
+    public BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter) {
+        // can't handle Object Ids, for now, so:
+        return _defaultSerializer.withObjectIdWriter(objectIdWriter);
+    }
+
+    @Override
+    protected BeanAsArraySerializer withIgnorals(String[] toIgnore) {
+        return new BeanAsArraySerializer(this, toIgnore);
+    }
+
+    @Override
+    protected BeanSerializerBase asArraySerializer() {
+        // already is one, so:
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializer implementation that differs between impls
+    /**********************************************************
+     */
+
+    // Re-defined from base class...
+    @Override
+    public void serializeWithType(Object bean, JsonGenerator jgen,
+            SerializerProvider provider, TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        /* Should not even get here; but let's be nice and re-route
+         * if need be.
+         */
+        _defaultSerializer.serializeWithType(bean, jgen, provider, typeSer);
+    }
+    
+    /**
+     * Main serialization method that will delegate actual output to
+     * configured
+     * {@link BeanPropertyWriter} instances.
+     */
+    @Override
+    public final void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // [JACKSON-805]
+        if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                && hasSingleElement(provider)) {
+            serializeAsArray(bean, jgen, provider);
+            return;
+        }
+        /* note: it is assumed here that limitations (type id, object id,
+         * any getter, filtering) have already been checked; so code here
+         * is trivial.
+         */
+        jgen.writeStartArray();
+        serializeAsArray(bean, jgen, provider);
+        jgen.writeEndArray();
+    }
+
+    /*
+    /**********************************************************
+    /* Field serialization methods
+    /**********************************************************
+     */
+    private boolean hasSingleElement(SerializerProvider provider) {
+        final BeanPropertyWriter[] props;
+        if (_filteredProps != null && provider.getActiveView() != null) {
+            props = _filteredProps;
+        } else {
+            props = _props;
+        }
+        return props.length == 1;
+    }
+
+    protected final void serializeAsArray(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final BeanPropertyWriter[] props;
+        if (_filteredProps != null && provider.getActiveView() != null) {
+            props = _filteredProps;
+        } else {
+            props = _props;
+        }
+
+        int i = 0;
+        try {
+            for (final int len = props.length; i < len; ++i) {
+                BeanPropertyWriter prop = props[i];
+                if (prop == null) { // can have nulls in filtered list; but if so, MUST write placeholders
+                    jgen.writeNull();
+                } else {
+                    prop.serializeAsColumn(bean, jgen, provider);
+                }
+            }
+            // NOTE: any getters can not be supported either
+            //if (_anyGetterWriter != null) {
+            //    _anyGetterWriter.getAndSerialize(bean, jgen, provider);
+            //}
+        } catch (Exception e) {
+            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
+            wrapAndThrow(provider, e, bean, name);
+        } catch (StackOverflowError e) {
+            JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
+            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
+            mapE.prependPath(new JsonMappingException.Reference(bean, name));
+            throw mapE;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override public String toString() {
+        return "BeanAsArraySerializer for "+handledType().getName();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java
new file mode 100644
index 0000000..fafa13d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FailingSerializer.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+/**
+ * Special bogus "serializer" that will throw
+ * {@link JsonGenerationException} if its {@link #serialize}
+ * gets invoked. Most commonly registered as handler for unknown types,
+ * as well as for catching unintended usage (like trying to use null
+ * as Map/Object key).
+ */
+public final class FailingSerializer
+    extends StdSerializer<Object>
+{
+    final String _msg;
+    
+    public FailingSerializer(String msg) {
+        super(Object.class);
+        _msg = msg;
+    }
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        throw new JsonGenerationException(_msg);
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return null;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+    {
+        ;
+    }
+}
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
new file mode 100644
index 0000000..06fe638
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/FilteredBeanPropertyWriter.java
@@ -0,0 +1,146 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Decorated {@link BeanPropertyWriter} that will filter out properties
+ * that are not to be included in currently active JsonView.
+ */
+public abstract class FilteredBeanPropertyWriter
+{    
+    public static BeanPropertyWriter constructViewBased(BeanPropertyWriter base, Class<?>[] viewsToIncludeIn)
+    {
+        if (viewsToIncludeIn.length == 1) {
+            return new SingleView(base, viewsToIncludeIn[0]);
+        }
+        return new MultiView(base, viewsToIncludeIn);
+    }
+
+    /*
+    /**********************************************************
+    /* Concrete sub-classes
+    /**********************************************************
+     */
+
+    private final static class SingleView
+        extends BeanPropertyWriter
+    {
+        protected final BeanPropertyWriter _delegate;
+
+        protected final Class<?> _view;
+        
+        protected SingleView(BeanPropertyWriter delegate, Class<?> view)
+        {
+            super(delegate);
+            _delegate = delegate;
+            _view = view;
+        }
+
+        @Override
+        public SingleView rename(NameTransformer transformer) {
+            return new SingleView(_delegate.rename(transformer), _view);
+        }
+        
+        @Override
+        public void assignSerializer(JsonSerializer<Object> ser) {
+            _delegate.assignSerializer(ser);
+        }
+
+        @Override
+        public void assignNullSerializer(JsonSerializer<Object> nullSer) {
+            _delegate.assignNullSerializer(nullSer);
+        }
+        
+        @Override
+        public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov)
+            throws Exception
+        {
+            Class<?> activeView = prov.getActiveView();
+            if (activeView == null || _view.isAssignableFrom(activeView)) {
+                _delegate.serializeAsField(bean, jgen, prov);
+            }
+        }
+
+        @Override
+        public void serializeAsColumn(Object bean, JsonGenerator jgen, SerializerProvider prov)
+            throws Exception
+        {
+            Class<?> activeView = prov.getActiveView();
+            if (activeView == null || _view.isAssignableFrom(activeView)) {
+                _delegate.serializeAsColumn(bean, jgen, prov);
+            } else {
+                _delegate.serializeAsPlaceholder(bean, jgen, prov);
+            }
+        }
+    }
+
+    private final static class MultiView
+        extends BeanPropertyWriter
+    {
+        protected final BeanPropertyWriter _delegate;
+
+        protected final Class<?>[] _views;
+        
+        protected MultiView(BeanPropertyWriter delegate, Class<?>[] views) {
+            super(delegate);
+            _delegate = delegate;
+            _views = views;
+        }
+
+        @Override
+        public MultiView rename(NameTransformer transformer) {
+            return new MultiView(_delegate.rename(transformer), _views);
+        }
+        
+        @Override
+        public void assignSerializer(JsonSerializer<Object> ser) {
+            _delegate.assignSerializer(ser);
+        }
+
+        @Override
+        public void assignNullSerializer(JsonSerializer<Object> nullSer) {
+            _delegate.assignNullSerializer(nullSer);
+        }
+        
+        @Override
+        public void serializeAsField(Object bean, JsonGenerator jgen, 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) {
+                    return;
+                }
+            }
+            _delegate.serializeAsField(bean, jgen, prov);
+        }
+
+        @Override
+        public void serializeAsColumn(Object bean, JsonGenerator jgen, 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;
+                }
+            }
+            _delegate.serializeAsColumn(bean, jgen, prov);
+        }
+    }
+}
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
new file mode 100644
index 0000000..86a9246
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedListSerializer.java
@@ -0,0 +1,171 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+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.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContainerSerializer;
+import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase;
+
+/**
+ * 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}.
+ */
+ at JacksonStdImpl
+public final class IndexedListSerializer
+    extends AsArraySerializerBase<List<?>>
+{
+    public IndexedListSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts,
+            BeanProperty property, JsonSerializer<Object> valueSerializer)
+    {
+        super(List.class, elemType, staticTyping, vts, property, valueSerializer);
+    }
+
+    public IndexedListSerializer(IndexedListSerializer src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSerializer)
+    {
+        super(src, property, vts, valueSerializer);
+    }
+
+    @Override
+    public IndexedListSerializer withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer) {
+        return new IndexedListSerializer(this, property, vts, elementSerializer);
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    @Override
+    public boolean isEmpty(List<?> value) {
+        return (value == null) || value.isEmpty();
+    }
+
+    @Override
+    public boolean hasSingleElement(List<?> value) {
+        return (value.size() == 1);
+    }
+
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+        return new IndexedListSerializer(_elementType, _staticTyping, vts, _property, _elementSerializer);
+    }
+    
+    @Override
+    public void serializeContents(List<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_elementSerializer != null) {
+            serializeContentsUsing(value, jgen, provider, _elementSerializer);
+            return;
+        }
+        if (_valueTypeSerializer != null) {
+            serializeTypedContents(value, jgen, provider);
+            return;
+        }
+        final int len = value.size();
+        if (len == 0) {
+            return;
+        }
+        int i = 0;
+        try {
+            PropertySerializerMap serializers = _dynamicSerializers;
+            for (; i < len; ++i) {
+                Object elem = value.get(i);
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    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);
+                        } else {
+                            serializer = _findAndAddDynamic(serializers, cc, provider);
+                        }
+                        serializers = _dynamicSerializers;
+                    }
+                    serializer.serialize(elem, jgen, provider);
+                }
+            }
+        } catch (Exception e) {
+            // [JACKSON-55] Need to add reference information
+            wrapAndThrow(provider, e, value, i);
+        }
+    }
+    
+    public void serializeContentsUsing(List<?> value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> ser)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.size();
+        if (len == 0) {
+            return;
+        }
+        final TypeSerializer typeSer = _valueTypeSerializer;
+        for (int i = 0; i < len; ++i) {
+            Object elem = value.get(i);
+            try {
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else if (typeSer == null) {
+                    ser.serialize(elem, jgen, provider);
+                } else {
+                    ser.serializeWithType(elem, jgen, provider, typeSer);
+                }
+            } catch (Exception e) {
+                // [JACKSON-55] Need to add reference information
+                wrapAndThrow(provider, e, value, i);
+            }
+        }
+    }
+
+    public void serializeTypedContents(List<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.size();
+        if (len == 0) {
+            return;
+        }
+        int i = 0;
+        try {
+            final TypeSerializer typeSer = _valueTypeSerializer;
+            PropertySerializerMap serializers = _dynamicSerializers;
+            for (; i < len; ++i) {
+                Object elem = value.get(i);
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    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);
+                        } else {
+                            serializer = _findAndAddDynamic(serializers, cc, provider);
+                        }
+                        serializers = _dynamicSerializers;
+                    }
+                    serializer.serializeWithType(elem, jgen, provider, typeSer);
+                }
+            }
+        } catch (Exception e) {
+            // [JACKSON-55] Need to add reference information
+            wrapAndThrow(provider, e, value, i);
+        }
+    }
+}
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
new file mode 100644
index 0000000..3aff54f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IndexedStringListSerializer.java
@@ -0,0 +1,197 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.util.*;
+
+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.JsonFormatTypes;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase;
+
+/**
+ * Efficient implement for serializing {@link List}s that contains Strings and are random-accessible.
+ * The only complexity is due to possibility that serializer for {@link String}
+ * may be overridde; because of this, logic is needed to ensure that the default
+ * serializer is in use to use fastest mode, or if not, to defer to custom
+ * String serializer.
+ */
+ at JacksonStdImpl
+public final class IndexedStringListSerializer
+    extends StaticListSerializerBase<List<String>>
+    implements ContextualSerializer
+{
+    public final static IndexedStringListSerializer instance = new IndexedStringListSerializer();
+    
+    protected final JsonSerializer<String> _serializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected IndexedStringListSerializer() {
+        this(null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public IndexedStringListSerializer(JsonSerializer<?> ser) {
+        super(List.class);
+        _serializer = (JsonSerializer<String>) ser;
+        
+    }
+
+    @Override protected JsonNode contentSchema() {
+        return createSchemaNode("string", true);
+    }
+
+    @Override
+    protected void acceptContentVisitor(JsonArrayFormatVisitor visitor)
+        throws JsonMappingException
+    {
+		visitor.itemsFormat(JsonFormatTypes.STRING);
+    }
+
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _serializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(provider, property, ser);
+        if (ser == null) {
+            ser = provider.findValueSerializer(String.class, property);
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        // Optimization: default serializer just writes String, so we can avoid a call:
+        if (isDefaultSerializer(ser)) {
+            ser = null;
+        }
+        // note: will never have TypeSerializer, because Strings are "natural" type
+        if (ser == _serializer) {
+            return this;
+        }
+        return new IndexedStringListSerializer(ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+
+    @Override
+    public void serialize(List<String> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.size();
+        // [JACKSON-805]
+        if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+            _serializeUnwrapped(value, jgen, provider);
+            return;
+        }
+        
+        jgen.writeStartArray();
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider, len);
+        } else {
+            serializeUsingCustom(value, jgen, provider, len);
+        }
+        jgen.writeEndArray();
+    }
+
+    private final void _serializeUnwrapped(List<String> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider, 1);
+        } else {
+            serializeUsingCustom(value, jgen, provider, 1);
+        }
+    }
+    
+    @Override
+    public void serializeWithType(List<String> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.size();
+        typeSer.writeTypePrefixForArray(value, jgen);
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider, len);
+        } else {
+            serializeUsingCustom(value, jgen, provider, len);
+        }
+        typeSer.writeTypeSuffixForArray(value, jgen);
+    }
+    
+    private final void serializeContents(List<String> value, JsonGenerator jgen, SerializerProvider provider,
+            int len)
+        throws IOException, JsonGenerationException
+    {
+        int i = 0;
+        try {
+            for (; i < len; ++i) {
+                String str = value.get(i);
+                if (str == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    jgen.writeString(str);
+                }
+            }
+        } catch (Exception e) {
+            wrapAndThrow(provider, e, value, i);
+        }
+    }
+
+    private final void serializeUsingCustom(List<String> value, JsonGenerator jgen, SerializerProvider provider,
+            int len)
+        throws IOException, JsonGenerationException
+    {
+        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(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/IteratorSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
new file mode 100644
index 0000000..15f6639
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/IteratorSerializer.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+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.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContainerSerializer;
+import com.fasterxml.jackson.databind.ser.std.AsArraySerializerBase;
+
+ at JacksonStdImpl
+public class IteratorSerializer
+    extends AsArraySerializerBase<Iterator<?>>
+{
+    public IteratorSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts,
+            BeanProperty property)
+    {
+        super(Iterator.class, elemType, staticTyping, vts, property, null);
+    }
+
+    public IteratorSerializer(IteratorSerializer src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSerializer)
+    {
+        super(src, property, vts, valueSerializer);
+    }
+
+    @Override
+    public boolean isEmpty(Iterator<?> value) {
+        return (value == null) || !value.hasNext();
+    }
+
+    @Override
+    public boolean hasSingleElement(Iterator<?> value) {
+        // no really good way to determine (without consuming iterator), so:
+        return false;
+    }
+    
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+        return new IteratorSerializer(_elementType, _staticTyping, vts, _property);
+    }
+
+    @Override
+    public IteratorSerializer withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer) {
+        return new IteratorSerializer(this, property, vts, elementSerializer);
+    }
+
+    @Override
+    public void serializeContents(Iterator<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (value.hasNext()) {
+            final TypeSerializer typeSer = _valueTypeSerializer;
+            JsonSerializer<Object> prevSerializer = null;
+            Class<?> prevClass = null;
+            do {
+                Object elem = value.next();
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    // Minor optimization to avoid most lookups:
+                    Class<?> cc = elem.getClass();
+                    JsonSerializer<Object> currSerializer;
+                    if (cc == prevClass) {
+                        currSerializer = prevSerializer;
+                    } else {
+                        currSerializer = provider.findValueSerializer(cc, _property);
+                        prevSerializer = currSerializer;
+                        prevClass = cc;
+                    }
+                    if (typeSer == null) {
+                        currSerializer.serialize(elem, jgen, provider);
+                    } else {
+                        currSerializer.serializeWithType(elem, jgen, provider, typeSer);
+                    }
+                }
+            } while (value.hasNext());
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/JsonSerializerMap.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/JsonSerializerMap.java
new file mode 100644
index 0000000..90997a3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/JsonSerializerMap.java
@@ -0,0 +1,91 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ser.SerializerCache.TypeKey;
+
+/**
+ * Specialized read-only map used for storing and accessing serializers by type.
+ */
+public class JsonSerializerMap
+{
+    private final Bucket[] _buckets;
+
+    private final int _size;
+    
+    public JsonSerializerMap(Map<TypeKey,JsonSerializer<Object>> serializers)
+    {
+        int size = findSize(serializers.size());
+        _size = size;
+        int hashMask = (size-1);
+        Bucket[] buckets = new Bucket[size];
+        for (Map.Entry<TypeKey,JsonSerializer<Object>> entry : serializers.entrySet()) {
+            TypeKey key = entry.getKey();
+            int index = key.hashCode() & hashMask;
+            buckets[index] = new Bucket(buckets[index], key, entry.getValue());
+        }
+        _buckets = buckets;
+    }
+    
+    private final static int findSize(int size)
+    {
+        // For small enough results (64 or less), we'll require <= 50% fill rate; otherwise 80%
+        int needed = (size <= 64) ? (size + size) : (size + (size >> 2));
+        int result = 8;
+        while (result < needed) {
+            result += result;
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    public int size() { return _size; }
+    
+    public JsonSerializer<Object> find(TypeKey key)
+    {
+        int index = key.hashCode() & (_buckets.length-1);
+        Bucket bucket = _buckets[index];
+        /* Ok let's actually try unrolling loop slightly as this shows up in profiler;
+         * and also because in vast majority of cases first entry is either null
+         * or matches.
+         */
+        if (bucket == null) {
+            return null;
+        }
+        if (key.equals(bucket.key)) {
+            return bucket.value;
+        }
+        while ((bucket = bucket.next) != null) {
+            if (key.equals(bucket.key)) {
+                return bucket.value;
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+    
+    private final static class Bucket
+    {
+        public final TypeKey key;
+        public final JsonSerializer<Object> value;
+        public final Bucket next;
+        
+        public Bucket(Bucket next, TypeKey key, JsonSerializer<Object> value)
+        {
+            this.next = next;
+            this.key = key;
+            this.value = value;
+        }
+    }
+}
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
new file mode 100644
index 0000000..86d88ad
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ObjectIdWriter.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Object that knows how to serialize Object Ids.
+ */
+public final class ObjectIdWriter
+{
+    public final JavaType idType;
+
+    /**
+     * Name of id property to write, if not null: if null, should
+     * only write references, but id property is handled by some
+     * other entity.
+     */
+    public final SerializedString propertyName;
+    
+    /**
+     * Blueprint generator instance: actual instance will be
+     * fetched from {@link SerializerProvider} using this as
+     * the key.
+     */
+    public final ObjectIdGenerator<?> generator;
+    
+    /**
+     * Serializer used for serializing id values.
+     */
+    public final JsonSerializer<Object> serializer;
+
+    /**
+     * Marker that indicates what the first reference is to be
+     * serialized as full POJO, or as Object Id (other references
+     * will always be serialized as Object Id)
+     * 
+     * @since 2.1
+     */
+    public final boolean alwaysAsId;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    protected ObjectIdWriter(JavaType t, SerializedString propName,
+            ObjectIdGenerator<?> gen, JsonSerializer<?> ser, boolean alwaysAsId)
+    {
+        idType = t;
+        propertyName = propName;
+        generator = gen;
+        serializer = (JsonSerializer<Object>) ser;
+        this.alwaysAsId = alwaysAsId;
+    }
+
+    /**
+     * Factory method called by {@link com.fasterxml.jackson.databind.ser.std.BeanSerializerBase}
+     * with the initial information based on standard settings for the type
+     * for which serializer is being built.
+     */
+    public static ObjectIdWriter construct(JavaType idType, String propName,
+            ObjectIdGenerator<?> generator, boolean alwaysAsId)
+    {
+        SerializedString serName = (propName == null) ? null : new SerializedString(propName);
+        return new ObjectIdWriter(idType, serName, generator, null, alwaysAsId);
+    }
+
+    public ObjectIdWriter withSerializer(JsonSerializer<?> ser) {
+        return new ObjectIdWriter(idType, propertyName, generator, ser, alwaysAsId);
+    }
+
+    /**
+     * @since 2.1
+     */
+    public ObjectIdWriter withAlwaysAsId(boolean newState) {
+        if (newState == alwaysAsId) {
+            return this;
+        }
+        return new ObjectIdWriter(idType, propertyName, generator, serializer, newState);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java
new file mode 100644
index 0000000..5758db2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertyBasedObjectIdGenerator.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
+import com.fasterxml.jackson.databind.ser.*;
+
+public class PropertyBasedObjectIdGenerator
+    extends ObjectIdGenerators.PropertyGenerator
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final BeanPropertyWriter _property;
+    
+    public PropertyBasedObjectIdGenerator(ObjectIdInfo oid, BeanPropertyWriter prop)
+    {
+        this(oid.getScope(), prop);
+    }
+
+    protected PropertyBasedObjectIdGenerator(Class<?> scope, BeanPropertyWriter prop)
+    {
+        super(scope);
+        _property = prop;
+    }
+
+    /**
+     * We must override this method, to prevent errors when scopes are the same,
+     * but underlying class (on which to access property) is different.
+     */
+    @Override
+    public boolean canUseFor(ObjectIdGenerator<?> gen) {
+        if (gen.getClass() == getClass()) {
+            PropertyBasedObjectIdGenerator other = (PropertyBasedObjectIdGenerator) gen;
+            if (other.getScope() == _scope) {
+                /* 26-Jul-2012, tatu: This is actually not enough, because the property
+                 *   accessor within BeanPropertyWriter won't work for other property fields
+                 *  (see [https://github.com/FasterXML/jackson-module-jaxb-annotations/issues/9]
+                 *  for details).
+                 *  So we need to verify that underlying property is actually the same.
+                 */
+                return (other._property == _property);
+            }
+        }
+        return false;
+    }
+    
+    @Override
+    public Object generateId(Object forPojo) {
+        try {
+            return _property.get(forPojo);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IllegalStateException("Problem accessing property '"
+                    +_property.getName()+"': "+e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public ObjectIdGenerator<Object> forScope(Class<?> scope) {
+        return (scope == _scope) ? this : new PropertyBasedObjectIdGenerator(scope, _property);
+    }
+
+    @Override
+    public ObjectIdGenerator<Object> newForSerialization(Object context) {
+        // No state, can return this
+        return this;
+    }
+
+    @Override
+    public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) {
+        // should we use general type for all; or type of property itself?
+        return new IdKey(getClass(), _scope, key);
+    }
+
+}
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
new file mode 100644
index 0000000..9978d35
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/PropertySerializerMap.java
@@ -0,0 +1,230 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+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.SerializerProvider;
+
+
+/**
+ * Helper container used for resolving serializers for dynamic (possibly but not
+ * necessarily polymorphic) properties: properties whose type is not forced
+ * to use dynamic (declared) type and that are not final.
+ * If so, serializer to use can only be established once actual value type is known.
+ * Since this happens a lot unless static typing is forced (or types are final)
+ * this implementation is optimized for efficiency.
+ * Instances are immutable; new instances are created with factory methods: this
+ * is important to ensure correct multi-threaded access.
+ */
+public abstract class PropertySerializerMap
+{
+    /**
+     * Main lookup method. Takes a "raw" type since usage is always from
+     * place where parameterization is fixed such that there can not be
+     * type-parametric variations.
+     */
+    public abstract JsonSerializer<Object> serializerFor(Class<?> type);
+
+    /**
+     * Method called if initial lookup fails; will both find serializer
+     * and construct new map instance if warranted, and return both
+     * @throws JsonMappingException 
+     */
+    public final SerializerAndMapResult findAndAddSerializer(Class<?> type,
+            SerializerProvider provider, BeanProperty property)
+        throws JsonMappingException
+    {
+        JsonSerializer<Object> serializer = provider.findValueSerializer(type, property);
+        return new SerializerAndMapResult(serializer, newWith(type, serializer));
+    }
+
+    public final SerializerAndMapResult findAndAddSerializer(JavaType type,
+            SerializerProvider provider, BeanProperty property)
+        throws JsonMappingException
+    {
+        JsonSerializer<Object> serializer = provider.findValueSerializer(type, property);
+        return new SerializerAndMapResult(serializer, newWith(type.getRawClass(), serializer));
+    }
+
+    public abstract PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer);
+    
+    public static PropertySerializerMap emptyMap() {
+        return Empty.instance;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Value class used for returning tuple that has both serializer
+     * that was retrieved and new map instance
+     */
+    public final static class SerializerAndMapResult
+    {
+        public final JsonSerializer<Object> serializer;
+        public final PropertySerializerMap map;
+        
+        public SerializerAndMapResult(JsonSerializer<Object> serializer,
+                PropertySerializerMap map)
+        {
+            this.serializer = serializer;
+            this.map = map;
+        }
+    }
+
+    /**
+     * Trivial container for bundling type + serializer entries.
+     */
+    private final static class TypeAndSerializer
+    {
+        public final Class<?> type;
+        public final JsonSerializer<Object> serializer;
+
+        public TypeAndSerializer(Class<?> type, JsonSerializer<Object> serializer) {
+            this.type = type;
+            this.serializer = serializer;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Implementations
+    /**********************************************************
+     */
+
+    /**
+     * Bogus instance that contains no serializers; used as the default
+     * map with new serializers.
+     */
+    private final static class Empty extends PropertySerializerMap
+    {
+        protected final static Empty instance = new Empty();
+
+        @Override
+        public JsonSerializer<Object> serializerFor(Class<?> type) {
+            return null; // empty, nothing to find
+        }        
+
+        @Override
+        public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
+            return new Single(type, serializer);
+        }
+    }
+
+    /**
+     * Map that contains a single serializer; although seemingly silly
+     * this is probably the most commonly used variant because many
+     * theoretically dynamic or polymorphic types just have single
+     * actual type.
+     */
+    private final static class Single extends PropertySerializerMap
+    {
+        private final Class<?> _type;
+        private final JsonSerializer<Object> _serializer;
+
+        public Single(Class<?> type, JsonSerializer<Object> serializer) {
+            _type = type;
+            _serializer = serializer;
+        }
+
+        @Override
+        public JsonSerializer<Object> serializerFor(Class<?> type)
+        {
+            if (type == _type) {
+                return _serializer;
+            }
+            return null;
+        }
+
+        @Override
+        public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
+            return new Double(_type, _serializer, type, serializer);
+        }
+    }
+
+    private final static class Double extends PropertySerializerMap
+    {
+        private final Class<?> _type1, _type2;
+        private final JsonSerializer<Object> _serializer1, _serializer2;
+
+        public Double(Class<?> type1, JsonSerializer<Object> serializer1,
+                Class<?> type2, JsonSerializer<Object> serializer2)
+        {
+            _type1 = type1;
+            _serializer1 = serializer1;
+            _type2 = type2;
+            _serializer2 = serializer2;
+        }
+
+        @Override
+        public JsonSerializer<Object> serializerFor(Class<?> type)
+        {
+            if (type == _type1) {
+                return _serializer1;
+            }
+            if (type == _type2) {
+                return _serializer2;
+            }
+            return null;
+        }        
+
+        @Override
+        public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer) {
+            // Ok: let's just create generic one
+            TypeAndSerializer[] ts = new TypeAndSerializer[2];
+            ts[0] = new TypeAndSerializer(_type1, _serializer1);
+            ts[1] = new TypeAndSerializer(_type2, _serializer2);
+            return new Multi(ts);
+        }
+    }
+    
+    private final static class Multi extends PropertySerializerMap
+    {
+        /**
+         * Let's limit number of serializers we actually cache; linear
+         * lookup won't scale too well beyond smallish number, and if
+         * we really want to support larger collections should use
+         * a hash map. But it seems unlikely this is a common use
+         * case so for now let's just stop building after hard-coded
+         * limit. 8 sounds like a reasonable stab for now.
+         */
+        private final static int MAX_ENTRIES = 8;
+        
+        private final TypeAndSerializer[] _entries;
+
+        public Multi(TypeAndSerializer[] entries) {
+            _entries = entries;
+        }
+
+        @Override
+        public JsonSerializer<Object> serializerFor(Class<?> type)
+        {
+            for (int i = 0, len = _entries.length; i < len; ++i) {
+                TypeAndSerializer entry = _entries[i];
+                if (entry.type == type) {
+                    return entry.serializer;
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public PropertySerializerMap newWith(Class<?> type, JsonSerializer<Object> serializer)
+        {
+            int len = _entries.length;
+            // Will only grow up to N entries
+            if (len == MAX_ENTRIES) {
+                return this;
+            }
+            // 1.6 has nice resize methods but we are still 1.5
+            TypeAndSerializer[] entries = new TypeAndSerializer[len+1];
+            System.arraycopy(_entries, 0, entries, 0, len);
+            entries[len] = new TypeAndSerializer(type, serializer);
+            return new Multi(entries);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java
new file mode 100644
index 0000000..69eb7a8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/ReadOnlyClassToSerializerMap.java
@@ -0,0 +1,88 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ser.SerializerCache.TypeKey;
+
+/**
+ * Optimized lookup table for accessing two types of serializers; typed
+ * and non-typed. Only accessed from a single thread, so no synchronization
+ * needed for accessors.
+ */
+public final class ReadOnlyClassToSerializerMap
+{
+    /**
+     * Actual mappings from type key to serializers
+     */
+    protected final JsonSerializerMap _map;
+
+    /**
+     * We'll reuse key class to avoid unnecessary instantiations; since
+     * this is not shared between threads, we can just reuse single
+     * instance.
+     */
+    protected TypeKey _cacheKey = null;
+    
+    private ReadOnlyClassToSerializerMap(JsonSerializerMap map)
+    {
+        _map = map;
+    }
+
+    public ReadOnlyClassToSerializerMap instance()
+    {
+        return new ReadOnlyClassToSerializerMap(_map);
+    }
+
+    /**
+     * Factory method for creating the "blueprint" lookup map. Such map
+     * can not be used as is but just shared: to get an actual usable
+     * instance, {@link #instance} has to be called first.
+     */
+    public static ReadOnlyClassToSerializerMap from(HashMap<TypeKey, JsonSerializer<Object>> src)
+    {
+        return new ReadOnlyClassToSerializerMap(new JsonSerializerMap(src));
+    }
+
+    public JsonSerializer<Object> typedValueSerializer(JavaType type)
+    { 
+        if (_cacheKey == null) {
+            _cacheKey = new TypeKey(type, true);
+        } else {
+            _cacheKey.resetTyped(type);
+        }
+        return _map.find(_cacheKey);
+    }
+
+    public JsonSerializer<Object> typedValueSerializer(Class<?> cls)
+    { 
+        if (_cacheKey == null) {
+            _cacheKey = new TypeKey(cls, true);
+        } else {
+            _cacheKey.resetTyped(cls);
+        }
+        return _map.find(_cacheKey);
+    }
+
+    public JsonSerializer<Object> untypedValueSerializer(JavaType type)
+    { 
+        if (_cacheKey == null) {
+            _cacheKey = new TypeKey(type, false);
+        } else {
+            _cacheKey.resetUntyped(type);
+        }
+        return _map.find(_cacheKey);
+    }
+
+    public JsonSerializer<Object> untypedValueSerializer(Class<?> cls)
+    { 
+        if (_cacheKey == null) {
+            _cacheKey = new TypeKey(cls, false);
+        } else {
+            _cacheKey.resetUntyped(cls);
+        }
+        return _map.find(_cacheKey);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java
new file mode 100644
index 0000000..8e76366
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleBeanPropertyFilter.java
@@ -0,0 +1,145 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.ser.BeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+
+/**
+ * Simple {@link BeanPropertyFilter} implementation that only uses property name
+ * to determine whether to serialize property as is, or to filter it out.
+ */
+public abstract class SimpleBeanPropertyFilter
+    implements BeanPropertyFilter // sub-classes must also implement java.io.Serializable
+{
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected SimpleBeanPropertyFilter() { }
+
+    /**
+     * Factory method to construct filter that filters out all properties <b>except</b>
+     * ones includes in set
+     */
+    public static SimpleBeanPropertyFilter filterOutAllExcept(Set<String> properties) {
+        return new FilterExceptFilter(properties);
+    }
+
+    public static SimpleBeanPropertyFilter filterOutAllExcept(String... propertyArray) {
+        HashSet<String> properties = new HashSet<String>(propertyArray.length);
+        Collections.addAll(properties, propertyArray);
+        return new FilterExceptFilter(properties);
+    }
+
+    public static SimpleBeanPropertyFilter serializeAllExcept(Set<String> properties) {
+        return new SerializeExceptFilter(properties);
+    }
+
+    public static SimpleBeanPropertyFilter serializeAllExcept(String... propertyArray) {
+        HashSet<String> properties = new HashSet<String>(propertyArray.length);
+        Collections.addAll(properties, propertyArray);
+        return new SerializeExceptFilter(properties);
+    }
+
+    /*
+    /**********************************************************
+    /* Methods for sub-classes
+    /**********************************************************
+     */
+
+    /**
+     * Method called to determine whether property will be included
+     * (if 'true' returned) or filtered out (if 'false' returned)
+     */
+    protected abstract boolean include(BeanPropertyWriter writer);
+
+    @Override
+    public void serializeAsField(Object bean, JsonGenerator jgen,
+            SerializerProvider provider, BeanPropertyWriter writer) throws Exception
+    {
+        if (include(writer)) {
+            writer.serializeAsField(bean, jgen, provider);
+        }
+    }
+
+    @Override
+    public void depositSchemaProperty(BeanPropertyWriter writer,
+            ObjectNode propertiesNode, SerializerProvider provider)
+        throws JsonMappingException
+    {
+        if (include(writer)) {
+            writer.depositSchemaProperty(propertiesNode, provider);
+        }
+    }
+
+    @Override
+    public void depositSchemaProperty(BeanPropertyWriter writer,
+            JsonObjectFormatVisitor objectVisitor, SerializerProvider provider)
+        throws JsonMappingException
+    {
+        if (include(writer)) {
+            writer.depositSchemaProperty(objectVisitor);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Sub-classes
+    /**********************************************************
+     */
+
+    /**
+     * Filter implementation which defaults to filtering out unknown
+     * properties and only serializes ones explicitly listed.
+     */
+    public static class FilterExceptFilter
+        extends SimpleBeanPropertyFilter
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        /**
+         * Set of property names to serialize.
+         */
+        protected final Set<String> _propertiesToInclude;
+
+        public FilterExceptFilter(Set<String> properties) {
+            _propertiesToInclude = properties;
+        }
+
+        @Override
+        protected boolean include(BeanPropertyWriter writer) {
+            return _propertiesToInclude.contains(writer.getName());
+        }
+    }
+
+    /**
+     * Filter implementation which defaults to serializing all
+     * properties, except for ones explicitly listed to be filtered out.
+     */
+    public static class SerializeExceptFilter
+        extends SimpleBeanPropertyFilter
+    {
+        /**
+         * Set of property names to filter out.
+         */
+        protected final Set<String> _propertiesToExclude;
+
+        public SerializeExceptFilter(Set<String> properties) {
+            _propertiesToExclude = properties;
+        }
+
+        @Override
+        protected boolean include(BeanPropertyWriter writer) {
+            return !_propertiesToExclude.contains(writer.getName());
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java
new file mode 100644
index 0000000..f7febc0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/SimpleFilterProvider.java
@@ -0,0 +1,108 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.ser.*;
+
+/**
+ * Simple {@link FilterProvider} implementation that just stores
+ * direct id-to-filter mapping.
+ */
+public class SimpleFilterProvider
+    extends FilterProvider
+    implements java.io.Serializable // since 2.1
+{
+    // generated for 2.1.0
+    private static final long serialVersionUID = -2825494703774121220L;
+
+    /**
+     * Mappings from ids to filters.
+     */
+    protected final Map<String,BeanPropertyFilter> _filtersById;
+
+    /**
+     * This is the filter we return in case no mapping was found for
+     * given id; default is 'null' (in which case caller typically
+     * reports an error), but can be set to an explicit filter.
+     */
+    protected BeanPropertyFilter _defaultFilter;
+
+    /**
+     * Flag that indicates whether request for an unknown filter id should
+     * result an exception (default) or not.
+     * Note that this is only relevant if no default filter has been
+     * configured.
+     */
+    protected boolean _cfgFailOnUnknownId = true;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: constructing, configuring
+    /**********************************************************
+     */
+    
+    public SimpleFilterProvider() {
+        this(new HashMap<String,BeanPropertyFilter>());
+    }
+
+    /**
+     * @param mapping Mapping from id to filter; used as is, no copy is made.
+     */
+    public SimpleFilterProvider(Map<String,BeanPropertyFilter> mapping) {
+        _filtersById = mapping;
+    }
+    
+    /**
+     * Method for defining filter to return for "unknown" filters; cases
+     * where there is no mapping from given id to an explicit filter.
+     * 
+     * @param f Filter to return when no filter is found for given id
+     */
+    public SimpleFilterProvider setDefaultFilter(BeanPropertyFilter f)
+    {
+        _defaultFilter = f;
+        return this;
+    }
+
+    public BeanPropertyFilter getDefaultFilter() {
+        return _defaultFilter;
+    }
+    
+    public SimpleFilterProvider setFailOnUnknownId(boolean state) {
+        _cfgFailOnUnknownId = state;
+        return this;
+    }
+
+    public boolean willFailOnUnknownId() {
+        return _cfgFailOnUnknownId;
+    }
+    
+    public SimpleFilterProvider addFilter(String id, BeanPropertyFilter filter) {
+        _filtersById.put(id, filter);
+        return this;
+    }
+
+    public BeanPropertyFilter removeFilter(String id) {
+        return _filtersById.remove(id);
+    }
+
+    /*
+    /**********************************************************
+    /* Public lookup API
+    /**********************************************************
+     */
+    
+    @Override
+    public BeanPropertyFilter findFilter(Object filterId)
+    {
+        BeanPropertyFilter f = _filtersById.get(filterId);
+        if (f == null) {
+            f = _defaultFilter;
+            if (f == null && _cfgFailOnUnknownId) {
+                throw new IllegalArgumentException("No filter configured with id '"+filterId+"' (type "
+                        +filterId.getClass().getName()+")");
+            }
+        }
+        return f;
+    }
+}
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
new file mode 100644
index 0000000..88450b0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringArraySerializer.java
@@ -0,0 +1,213 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
+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.std.ArraySerializerBase;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Standard serializer used for <code>String[]</code> values.
+ */
+ at JacksonStdImpl
+public class StringArraySerializer
+    extends ArraySerializerBase<String[]>
+    implements ContextualSerializer
+{
+    /* Note: not clean in general, but we are betting against
+     * anyone re-defining properties of String.class here...
+     */
+    private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(String.class);
+
+    public final static StringArraySerializer instance = new StringArraySerializer();
+    
+    /**
+     * Value serializer to use, if it's not the standard one
+     * (if it is we can optimize serialization a lot)
+     */
+    protected final JsonSerializer<Object> _elementSerializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected StringArraySerializer() {
+        super(String[].class, null);
+        _elementSerializer = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public StringArraySerializer(StringArraySerializer src,
+            BeanProperty prop, JsonSerializer<?> ser) {
+        super(src, prop);
+        _elementSerializer = (JsonSerializer<Object>) ser;
+    }
+    
+    /**
+     * Strings never add type info; hence, even if type serializer is suggested,
+     * we'll ignore it...
+     */
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _elementSerializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(provider, property, ser);
+        if (ser == null) {
+            ser = provider.findValueSerializer(String.class, property);
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        // Optimization: default serializer just writes String, so we can avoid a call:
+        if (isDefaultSerializer(ser)) {
+            ser = null;
+        }
+        // note: will never have TypeSerializer, because Strings are "natural" type
+        if (ser == _elementSerializer) {
+            return this;
+        }
+        return new StringArraySerializer(this, property, ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Simple accessors
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return VALUE_TYPE;
+    }
+
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _elementSerializer;
+    }
+    
+    @Override
+    public boolean isEmpty(String[] value) {
+        return (value == null) || (value.length == 0);
+    }
+
+    @Override
+    public boolean hasSingleElement(String[] value) {
+        return (value.length == 1);
+    }
+    
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serializeContents(String[] value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.length;
+        if (len == 0) {
+            return;
+        }
+        if (_elementSerializer != null) {
+            serializeContentsSlow(value, jgen, provider, _elementSerializer);
+            return;
+        }
+        /* 08-Dec-2008, tatus: If we want this to be fully overridable
+         *  (for example, to support String cleanup during writing
+         *  or something), we should find serializer  by provider.
+         *  But for now, that seems like an overkill: and caller can
+         *  add custom serializer if that is needed as well.
+         * (ditto for null values)
+         */
+        //JsonSerializer<String> ser = (JsonSerializer<String>)provider.findValueSerializer(String.class);
+        for (int i = 0; i < len; ++i) {
+            String str = value[i];
+            if (str == null) {
+                jgen.writeNull();
+            } else {
+                //ser.serialize(value[i], jgen, provider);
+                jgen.writeString(value[i]);
+            }
+        }
+    }
+
+    private void serializeContentsSlow(String[] value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> ser)
+        throws IOException, JsonGenerationException
+    {
+        for (int i = 0, len = value.length; i < len; ++i) {
+            String str = value[i];
+            if (str == null) {
+                provider.defaultSerializeNull(jgen);
+            } else {
+                ser.serialize(value[i], jgen, provider);
+            }
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        ObjectNode o = createSchemaNode("array", true);
+        o.put("items", createSchemaNode("string"));
+        return o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        if (visitor != null) {
+            JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+            if (v2 != null) {
+                v2.itemsFormat(JsonFormatTypes.STRING);
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..5f92c74
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/StringCollectionSerializer.java
@@ -0,0 +1,195 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.util.*;
+
+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.JsonFormatTypes;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.std.StaticListSerializerBase;
+
+/**
+ * Efficient implement for serializing {@link Collection}s that contain Strings.
+ * The only complexity is due to possibility that serializer for {@link String}
+ * may be overridde; because of this, logic is needed to ensure that the default
+ * serializer is in use to use fastest mode, or if not, to defer to custom
+ * String serializer.
+ */
+ at JacksonStdImpl
+public class StringCollectionSerializer
+    extends StaticListSerializerBase<Collection<String>>
+    implements ContextualSerializer
+{
+    public final static StringCollectionSerializer instance = new StringCollectionSerializer();
+    
+    protected final JsonSerializer<String> _serializer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected StringCollectionSerializer() {
+        this(null);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected StringCollectionSerializer(JsonSerializer<?> ser)
+    {
+        super(Collection.class);
+        _serializer = (JsonSerializer<String>) ser;
+    }        
+    
+    @Override protected JsonNode contentSchema() {
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    protected void acceptContentVisitor(JsonArrayFormatVisitor visitor)
+        throws JsonMappingException
+    {
+        visitor.itemsFormat(JsonFormatTypes.STRING);
+    }
+
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _serializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(provider, property, ser);
+        if (ser == null) {
+            ser = provider.findValueSerializer(String.class, property);
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        // Optimization: default serializer just writes String, so we can avoid a call:
+        if (isDefaultSerializer(ser)) {
+            ser = null;
+        }
+        // note: will never have TypeSerializer, because Strings are "natural" type
+        if (ser == _serializer) {
+            return this;
+        }
+        return new StringCollectionSerializer(ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serialize(Collection<String> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // [JACKSON-805]
+        if ((value.size() == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
+            _serializeUnwrapped(value, jgen, provider);
+            return;
+        }      
+        jgen.writeStartArray();
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider);
+        } else {
+            serializeUsingCustom(value, jgen, provider);
+        }
+        jgen.writeEndArray();
+    }
+
+    private final void _serializeUnwrapped(Collection<String> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider);
+        } else {
+            serializeUsingCustom(value, jgen, provider);
+        }
+    }
+
+    @Override
+    public void serializeWithType(Collection<String> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForArray(value, jgen);
+        if (_serializer == null) {
+            serializeContents(value, jgen, provider);
+        } else {
+            serializeUsingCustom(value, jgen, provider);
+        }
+        typeSer.writeTypeSuffixForArray(value, jgen);
+    }
+
+    private final void serializeContents(Collection<String> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_serializer != null) {
+            serializeUsingCustom(value, jgen, provider);
+            return;
+        }
+        int i = 0;
+        for (String str : value) {
+            try {
+                if (str == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    jgen.writeString(str);
+                }
+                ++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
new file mode 100644
index 0000000..d2ac529
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/TypeWrappedSerializer.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Simple serializer that will call configured type serializer, passing
+ * in configured data serializer, and exposing it all as a simple
+ * serializer.
+ */
+public final class TypeWrappedSerializer
+    extends JsonSerializer<Object>
+{
+    final protected TypeSerializer _typeSerializer;
+    final protected JsonSerializer<Object> _serializer;
+
+    @SuppressWarnings("unchecked")
+    public TypeWrappedSerializer(TypeSerializer typeSer, JsonSerializer<?> ser)
+    {
+        super();
+        _typeSerializer = typeSer;
+        _serializer = (JsonSerializer<Object>) ser;
+    }
+
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        _serializer.serializeWithType(value, jgen, provider, _typeSerializer);
+    }
+
+    @Override
+    public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        /* 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);
+    }
+    
+    @Override
+    public Class<Object> handledType() { return Object.class; }
+}
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
new file mode 100644
index 0000000..4b4a163
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnknownSerializer.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+public class UnknownSerializer
+    extends StdSerializer<Object>
+{
+    public UnknownSerializer() {
+        super(Object.class);
+    }
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonMappingException
+    {
+        // 27-Nov-2009, tatu: As per [JACKSON-201] may or may not fail...
+        if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
+            failForEmpty(value);
+        }
+        // But if it's fine, we'll just output empty JSON Object:
+        jgen.writeStartObject();
+        jgen.writeEndObject();
+    }
+
+    @Override
+    public final void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        if (provider.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS)) {
+            failForEmpty(value);
+        }
+        typeSer.writeTypePrefixForObject(value, jgen);
+        typeSer.writeTypeSuffixForObject(value, jgen);
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
+        return null;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    { 
+        visitor.expectAnyFormat(typeHint);
+    }
+
+    protected void failForEmpty(Object value) throws JsonMappingException
+    {
+        throw new JsonMappingException("No serializer found for class "+value.getClass().getName()+" and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )");
+    }
+}
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
new file mode 100644
index 0000000..f9a582d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanPropertyWriter.java
@@ -0,0 +1,141 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Variant of {@link BeanPropertyWriter} which will handle unwrapping
+ * of JSON Object (including of properties of Object within surrounding
+ * JSON object, and not as sub-object).
+ */
+public class UnwrappingBeanPropertyWriter
+    extends BeanPropertyWriter
+{
+    /**
+     * Transformer used to add prefix and/or suffix for properties
+     * of unwrapped POJO.
+     */
+    protected final NameTransformer _nameTransformer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public UnwrappingBeanPropertyWriter(BeanPropertyWriter base, NameTransformer unwrapper) {
+        super(base);
+        _nameTransformer = unwrapper;
+    }
+
+    private UnwrappingBeanPropertyWriter(UnwrappingBeanPropertyWriter base, NameTransformer transformer,
+            SerializedString name) {
+        super(base, name);
+        _nameTransformer = transformer;
+    }
+
+    @Override
+    public UnwrappingBeanPropertyWriter rename(NameTransformer transformer)
+    {
+        String oldName = _name.getValue();
+        String newName = transformer.transform(oldName);
+
+        // important: combine transformers:
+        transformer = NameTransformer.chainedTransformer(transformer, _nameTransformer);
+    
+        return new UnwrappingBeanPropertyWriter(this, transformer, new SerializedString(newName));
+    }
+
+    /*
+    /**********************************************************
+    /* Overrides
+    /**********************************************************
+     */
+    
+    @Override
+    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        throws Exception
+    {
+        Object value = get(bean);
+        if (value == null) {
+            // Hmmh. I assume we MUST pretty much suppress nulls, since we
+            // can't really unwrap them...
+            return;
+        }
+        JsonSerializer<Object> ser = _serializer;
+        if (ser == null) {
+            Class<?> cls = value.getClass();
+            PropertySerializerMap map = _dynamicSerializers;
+            ser = map.serializerFor(cls);
+            if (ser == null) {
+                ser = _findAndAddDynamic(map, cls, prov);
+            }
+        }
+        if (_suppressableValue != null) {
+            if (MARKER_FOR_EMPTY == _suppressableValue) {
+                if (ser.isEmpty(value)) {
+                    return;
+                }
+            } else if (_suppressableValue.equals(value)) {
+                return;
+            }
+        }
+        // For non-nulls, first: simple check for direct cycles
+        if (value == bean) {
+            _handleSelfReference(bean, ser);
+        }
+
+        // note: must verify we are using unwrapping serializer; if not, will write field name
+        if (!ser.isUnwrappingSerializer()) {
+            jgen.writeFieldName(_name);
+        }
+
+        if (_typeSerializer == null) {
+            ser.serialize(value, jgen, prov);
+        } else {
+            ser.serializeWithType(value, jgen, prov, _typeSerializer);
+        }
+    }
+
+    // need to override as we must get unwrapping instance...
+    @Override
+    public void assignSerializer(JsonSerializer<Object> ser)
+    {
+        super.assignSerializer(ser);
+        if (_serializer != null) {
+            NameTransformer t = _nameTransformer;
+            if (_serializer.isUnwrappingSerializer()) {
+                t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) _serializer)._nameTransformer);
+            }
+            _serializer = _serializer.unwrappingSerializer(t);
+        }
+    }
+    
+    // need to override as we must get unwrapping instance...
+    @Override
+    protected JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            Class<?> type, SerializerProvider provider) throws JsonMappingException
+    {
+        JsonSerializer<Object> serializer;
+        if (_nonTrivialBaseType != null) {
+            JavaType subtype = provider.constructSpecializedType(_nonTrivialBaseType, type);
+            serializer = provider.findValueSerializer(subtype, this);
+        } else {
+            serializer = provider.findValueSerializer(type, this);
+        }
+        NameTransformer t = _nameTransformer;
+        if (serializer.isUnwrappingSerializer()) {
+            t = NameTransformer.chainedTransformer(t, ((UnwrappingBeanSerializer) serializer)._nameTransformer);
+        }
+        serializer = serializer.unwrappingSerializer(t);
+        
+        _dynamicSerializers = _dynamicSerializers.newWith(type, serializer);
+        return serializer;
+    }
+}
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
new file mode 100644
index 0000000..69b3aad
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/UnwrappingBeanSerializer.java
@@ -0,0 +1,116 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.*;
+import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+public class UnwrappingBeanSerializer
+    extends BeanSerializerBase
+{
+    /**
+     * Transformer used to add prefix and/or suffix for properties
+     * of unwrapped POJO.
+     */
+    protected final NameTransformer _nameTransformer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: constructors
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used for creating unwrapping instance of a
+     * standard <code>BeanSerializer</code>
+     */
+    public UnwrappingBeanSerializer(BeanSerializerBase src, NameTransformer transformer) {
+        super(src, transformer);
+        _nameTransformer = transformer;
+    }
+
+    public UnwrappingBeanSerializer(UnwrappingBeanSerializer src, ObjectIdWriter objectIdWriter) {    
+        super(src, objectIdWriter);
+        _nameTransformer = src._nameTransformer;
+    }
+
+    protected UnwrappingBeanSerializer(UnwrappingBeanSerializer src, String[] toIgnore) {
+        super(src, toIgnore);
+        _nameTransformer = src._nameTransformer;
+    }
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: factory methods, fluent factories
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
+        // !!! 23-Jan-2012, tatu: Should we chain transformers?
+        return new UnwrappingBeanSerializer(this, transformer);
+    }
+
+    @Override
+    public boolean isUnwrappingSerializer() {
+        return true; // sure is
+    }
+
+    @Override
+    public UnwrappingBeanSerializer withObjectIdWriter(ObjectIdWriter objectIdWriter) {
+        return new UnwrappingBeanSerializer(this, objectIdWriter);
+    }
+
+    @Override
+    protected UnwrappingBeanSerializer withIgnorals(String[] toIgnore) {
+        return new UnwrappingBeanSerializer(this, toIgnore);
+    }
+
+    /**
+     * JSON Array output can not be done if unwrapping operation is
+     * requested; so implementation will simply return 'this'.
+     */
+    @Override
+    protected BeanSerializerBase asArraySerializer() {
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializer implementation that differs between impls
+    /**********************************************************
+     */
+
+    /**
+     * Main serialization method that will delegate actual output to
+     * configured
+     * {@link BeanPropertyWriter} instances.
+     */
+    @Override
+    public final void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_objectIdWriter != null) {
+            _serializeWithObjectId(bean, jgen, provider, false);
+            return;
+        }
+        if (_propertyFilterId != null) {
+            serializeFieldsFiltered(bean, jgen, provider);
+        } else {
+            serializeFields(bean, jgen, provider);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override public String toString() {
+        return "UnwrappingBeanSerializer for "+handledType().getName();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java
new file mode 100644
index 0000000..9e0b99d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/WritableObjectId.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.ser.impl;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Simple value container used to keep track of Object Ids during
+ * serialization.
+ */
+public final class WritableObjectId
+{
+    public final ObjectIdGenerator<?> generator;
+
+    public Object id;
+
+    protected boolean idWritten = false;
+    
+    public WritableObjectId(ObjectIdGenerator<?> generator) {
+        this.generator = generator;
+    }
+    
+    public boolean writeAsId(JsonGenerator jgen, SerializerProvider provider, ObjectIdWriter w)
+        throws IOException, JsonGenerationException
+    {
+        if (id != null && (idWritten || w.alwaysAsId)) {
+            w.serializer.serialize(id, jgen, provider);
+            return true;
+        }
+        return false;
+    }
+    
+    public Object generateId(Object forPojo) {
+        return (id = generator.generateId(forPojo));
+    }
+
+    public void writeAsField(JsonGenerator jgen, SerializerProvider provider,
+            ObjectIdWriter w)
+        throws IOException, JsonGenerationException
+    {
+        SerializedString name = w.propertyName;
+        idWritten = true;
+        if (name != null) {
+            jgen.writeFieldName(name);
+            w.serializer.serialize(id, jgen, provider);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/impl/package-info.java b/src/main/java/com/fasterxml/jackson/databind/ser/impl/package-info.java
new file mode 100644
index 0000000..d1b84bb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Contains implementation classes of serialization part of 
+ * data binding.
+ */
+package com.fasterxml.jackson.databind.ser.impl;
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/package-info.java b/src/main/java/com/fasterxml/jackson/databind/ser/package-info.java
new file mode 100644
index 0000000..e8c31db
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Contains implementation classes of serialization part of 
+ * data binding.
+ */
+package com.fasterxml.jackson.databind.ser;
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
new file mode 100644
index 0000000..1f68d91
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ArraySerializerBase.java
@@ -0,0 +1,74 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.*;
+
+/**
+ * Intermediate base class for serializers used for various
+ * Java arrays.
+ * 
+ * @param <T> Type of arrays serializer handles
+ */
+public abstract class ArraySerializerBase<T>
+    extends ContainerSerializer<T>
+{
+    protected final BeanProperty _property;
+
+    protected ArraySerializerBase(Class<T> cls)
+    {
+        super(cls);
+        _property = null;
+    }
+
+    protected ArraySerializerBase(Class<T> cls, BeanProperty property)
+    {
+        super(cls);
+        _property = property;
+    }
+
+    protected ArraySerializerBase(ArraySerializerBase<?> src)
+    {
+        super(src._handledType, false);
+        _property = src._property;
+    }
+    
+    protected ArraySerializerBase(ArraySerializerBase<?> src, BeanProperty property)
+    {
+        super(src._handledType, false);
+        _property = property;
+    }
+    
+    @Override
+    public final void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // [JACKSON-805]
+        if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                && hasSingleElement(value)) {
+            serializeContents(value, jgen, provider);
+            return;
+        }
+        jgen.writeStartArray();
+        serializeContents(value, jgen, provider);
+        jgen.writeEndArray();
+    }
+    
+    @Override
+    public final void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work
+        typeSer.writeTypePrefixForArray(value, jgen);
+        serializeContents(value, jgen, provider);
+        typeSer.writeTypeSuffixForArray(value, jgen);
+    }
+
+    protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException;
+}
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
new file mode 100644
index 0000000..a363638
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/AsArraySerializerBase.java
@@ -0,0 +1,287 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.*;
+
+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.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.TypeFactory;
+
+/**
+ * Base class for serializers that will output contents as JSON
+ * arrays; typically serializers used for {@link java.util.Collection}
+ * and array types.
+ */
+public abstract class AsArraySerializerBase<T>
+    extends ContainerSerializer<T>
+    implements ContextualSerializer
+{
+    protected final boolean _staticTyping;
+
+    protected final JavaType _elementType;
+
+    /**
+     * Type serializer used for values, if any.
+     */
+    protected final TypeSerializer _valueTypeSerializer;
+
+    /**
+     * Value serializer to use, if it can be statically determined
+     */
+    protected final JsonSerializer<Object> _elementSerializer;
+
+    /**
+     * Collection-valued property being serialized with this instance
+     */
+    protected final BeanProperty _property;
+
+    /**
+     * If element type can not be statically determined, mapping from
+     * runtime type to serializer is handled using this object
+     */
+    protected PropertySerializerMap _dynamicSerializers;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected AsArraySerializerBase(Class<?> cls, JavaType et, boolean staticTyping,
+            TypeSerializer vts, BeanProperty property, JsonSerializer<Object> elementSerializer)
+    {
+        // typing with generics is messy... have to resort to this:
+        super(cls, false);
+        _elementType = et;
+        // static if explicitly requested, or if element type is final
+        _staticTyping = staticTyping || (et != null && et.isFinal());
+        _valueTypeSerializer = vts;
+        _property = property;
+        _elementSerializer = elementSerializer;
+        _dynamicSerializers = PropertySerializerMap.emptyMap();
+    }
+
+    @SuppressWarnings("unchecked")
+    protected AsArraySerializerBase(AsArraySerializerBase<?> src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> elementSerializer)
+    {
+        super(src);
+        _elementType = src._elementType;
+        _staticTyping = src._staticTyping;
+        _valueTypeSerializer = vts;
+        _property = property;
+        _elementSerializer = (JsonSerializer<Object>) elementSerializer;
+        _dynamicSerializers = src._dynamicSerializers;
+    }
+    
+    public abstract AsArraySerializerBase<T> withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer);
+
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+    
+    /**
+     * This method is needed to resolve contextual annotations like
+     * per-property overrides, as well as do recursive call
+     * to <code>createContextual</code> of content serializer, if
+     * known statically.
+     */
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        TypeSerializer typeSer = _valueTypeSerializer;
+        if (typeSer != null) {
+            typeSer = typeSer.forProperty(property);
+        }
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _elementSerializer;
+        }
+        // 18-Feb-2013, tatu: May have a content converter:
+        ser = findConvertingContentSerializer(provider, property, ser);
+        if (ser == null) {
+            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.
+                if (_elementType != null) {
+                    if (_staticTyping || hasContentTypeAnnotation(provider, property)) {
+                        ser = provider.findValueSerializer(_elementType, property);
+                    }
+                }
+            }
+        } else {
+            if (ser instanceof ContextualSerializer) {
+                ser = ((ContextualSerializer) ser).createContextual(provider, property);
+            }
+        }
+        if ((ser != _elementSerializer) || (property != _property) || _valueTypeSerializer != typeSer) {
+            return withResolved(property, typeSer, ser);
+        }
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    @Override
+    public JavaType getContentType() {
+        return _elementType;
+    }
+
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _elementSerializer;
+    }
+
+    /*
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public final void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // [JACKSON-805]
+        if (provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)
+                && hasSingleElement(value)) {
+            serializeContents(value, jgen, provider);
+            return;
+        }
+        jgen.writeStartArray();
+        serializeContents(value, jgen, provider);
+        jgen.writeEndArray();
+    }
+    
+    @Override
+    public final void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        // note: let's NOT consider [JACKSON-805] here; gets too complicated, and probably just won't work
+        typeSer.writeTypePrefixForArray(value, jgen);
+        serializeContents(value, jgen, provider);
+        typeSer.writeTypeSuffixForArray(value, jgen);
+    }
+
+    protected abstract void serializeContents(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException;
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        /* 15-Jan-2010, tatu: This should probably be rewritten, given that
+         *    more information about content type is actually being explicitly
+         *    passed. So there should be less need to try to re-process that
+         *    information.
+         */
+        ObjectNode o = createSchemaNode("array", true);
+        JavaType contentType = null;
+        if (typeHint != null) {
+            JavaType javaType = provider.constructType(typeHint);
+            contentType = javaType.getContentType();
+            if (contentType == null) { // could still be parametrized (Iterators)
+                if (typeHint instanceof ParameterizedType) {
+                    Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments();
+                    if (typeArgs.length == 1) {
+                        contentType = provider.constructType(typeArgs[0]);
+                    }
+                }
+            }
+        }
+        if (contentType == null && _elementType != null) {
+            contentType = _elementType;
+        }
+        if (contentType != 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 (schemaNode == null) {
+                schemaNode = com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+            }
+            o.put("items", schemaNode);
+        }
+        return o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        JsonArrayFormatVisitor arrayVisitor = (visitor == null) ? null : visitor.expectArrayFormat(typeHint);
+        if (arrayVisitor != null) {
+            TypeFactory tf = visitor.getProvider().getTypeFactory();
+            JavaType contentType = tf.moreSpecificType(_elementType, typeHint.getContentType());
+            if (contentType == null) {
+                throw new JsonMappingException("Could not resolve type");
+            }
+            JsonSerializer<?> valueSer = _elementSerializer;
+            if (valueSer == null) {
+                valueSer = visitor.getProvider().findValueSerializer(contentType, _property);
+            }
+            arrayVisitor.itemsFormat(valueSer, contentType);
+        }
+    }
+
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            Class<?> type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        // did we get a new map of serializers? If so, start using it
+        if (map != result.map) {
+            _dynamicSerializers = result.map;
+        }
+        return result.serializer;
+    }
+
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            JavaType type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        if (map != result.map) {
+            _dynamicSerializers = result.map;
+        }
+        return result.serializer;
+    }
+}
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
new file mode 100644
index 0000000..feac112
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BeanSerializerBase.java
@@ -0,0 +1,740 @@
+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.annotation.ObjectIdGenerator;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.jsonschema.JsonSerializableSchema;
+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.*;
+import com.fasterxml.jackson.databind.ser.impl.ObjectIdWriter;
+import com.fasterxml.jackson.databind.ser.impl.PropertyBasedObjectIdGenerator;
+import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.Converter;
+import com.fasterxml.jackson.databind.util.NameTransformer;
+
+/**
+ * Base class both for the standard bean serializer, and couple
+ * of variants that only differ in small details.
+ * Can be used for custom bean serializers as well, although that
+ * is not the primary design goal.
+ */
+public abstract class BeanSerializerBase
+    extends StdSerializer<Object>
+    implements ContextualSerializer, ResolvableSerializer,
+        JsonFormatVisitable, SchemaAware
+{
+    final protected static BeanPropertyWriter[] NO_PROPS = new BeanPropertyWriter[0];
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Writers used for outputting actual property values
+     */
+    final protected BeanPropertyWriter[] _props;
+
+    /**
+     * Optional filters used to suppress output of properties that
+     * are only to be included in certain views
+     */
+    final protected BeanPropertyWriter[] _filteredProps;
+
+    /**
+     * Handler for {@link com.fasterxml.jackson.annotation.JsonAnyGetter}
+     * annotated properties
+     */
+    final protected AnyGetterWriter _anyGetterWriter;
+    
+    /**
+     * Id of the bean property filter to use, if any; null if none.
+     */
+    final protected Object _propertyFilterId;
+
+    /**
+     * If using custom type ids (usually via getter, or field), this is the
+     * reference to that member.
+     */
+    final protected AnnotatedMember _typeId;
+    
+    /**
+     * If this POJO can be alternatively serialized using just an object id
+     * to denote a reference to previously serialized object,
+     * this Object will handle details.
+     */
+    final protected ObjectIdWriter _objectIdWriter;
+
+    /**
+     * Requested shape from bean class annotations.
+     */
+    final protected JsonFormat.Shape _serializationShape;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle: constructors
+    /**********************************************************
+     */
+
+    /**
+     * Constructor used by {@link BeanSerializerBuilder} to create an
+     * instance
+     * 
+     * @param type Nominal type of values handled by this serializer
+     * @param builder Builder for accessing other collected information
+     */
+    protected BeanSerializerBase(JavaType type, BeanSerializerBuilder builder,
+            BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
+    {
+        super(type);
+        _props = properties;
+        _filteredProps = filteredProperties;
+        if (builder == null) { // mostly for testing
+            _typeId = null;
+            _anyGetterWriter = null;
+            _propertyFilterId = null;
+            _objectIdWriter = null;
+            _serializationShape = null;
+        } else {
+            _typeId = builder.getTypeId();
+            _anyGetterWriter = builder.getAnyGetter();
+            _propertyFilterId = builder.getFilterId();
+            _objectIdWriter = builder.getObjectIdWriter();
+            JsonFormat.Value format = builder.getBeanDescription().findExpectedFormat(null);
+            _serializationShape = (format == null) ? null : format.getShape();
+        }
+    }
+
+    public BeanSerializerBase(BeanSerializerBase src,
+            BeanPropertyWriter[] properties, BeanPropertyWriter[] filteredProperties)
+    {
+        super(src._handledType);
+        _props = properties;
+        _filteredProps = filteredProperties;
+
+        _typeId = src._typeId;
+        _anyGetterWriter = src._anyGetterWriter;
+        _objectIdWriter = src._objectIdWriter;
+        _propertyFilterId = src._propertyFilterId;
+        _serializationShape = src._serializationShape;
+    }
+
+    protected BeanSerializerBase(BeanSerializerBase src, ObjectIdWriter objectIdWriter)
+    {
+        super(src._handledType);
+        _props = src._props;
+        _filteredProps = src._filteredProps;
+        
+        _typeId = src._typeId;
+        _anyGetterWriter = src._anyGetterWriter;
+        _objectIdWriter = objectIdWriter;
+        _propertyFilterId = src._propertyFilterId;
+        _serializationShape = src._serializationShape;
+    }
+
+    protected BeanSerializerBase(BeanSerializerBase src, String[] toIgnore)
+    {
+        super(src._handledType);
+
+        // Bit clumsy, but has to do:
+        HashSet<String> ignoredSet = ArrayBuilders.arrayToSet(toIgnore);
+        final BeanPropertyWriter[] propsIn = src._props;
+        final BeanPropertyWriter[] fpropsIn = src._filteredProps;
+        final int len = propsIn.length;
+
+        ArrayList<BeanPropertyWriter> propsOut = new ArrayList<BeanPropertyWriter>(len);
+        ArrayList<BeanPropertyWriter> fpropsOut = (fpropsIn == null) ? null : new ArrayList<BeanPropertyWriter>(len);
+
+        for (int i = 0; i < len; ++i) {
+            BeanPropertyWriter bpw = propsIn[i];
+            // should be ignored?
+            if (ignoredSet.contains(bpw.getName())) {
+                continue;
+            }
+            propsOut.add(bpw);
+            if (fpropsIn != null) {
+                fpropsOut.add(fpropsIn[i]);
+            }
+        }
+        _props = propsOut.toArray(new BeanPropertyWriter[propsOut.size()]);
+        _filteredProps = (fpropsOut == null) ? null : fpropsOut.toArray(new BeanPropertyWriter[fpropsOut.size()]);
+        
+        _typeId = src._typeId;
+        _anyGetterWriter = src._anyGetterWriter;
+        _objectIdWriter = src._objectIdWriter;
+        _propertyFilterId = src._propertyFilterId;
+        _serializationShape = src._serializationShape;
+    }
+    
+    /**
+     * Fluent factory used for creating a new instance with different
+     * {@link ObjectIdWriter}.
+     * 
+     * @since 2.0
+     */
+    public abstract BeanSerializerBase withObjectIdWriter(ObjectIdWriter objectIdWriter);
+
+    /**
+     * Fluent factory used for creating a new instance with additional
+     * set of properties to ignore (from properties this instance otherwise has)
+     * 
+     * @since 2.0
+     */
+    protected abstract BeanSerializerBase withIgnorals(String[] toIgnore);
+
+    /**
+     * Fluent factory for creating a variant that output POJO as a
+     * JSON Array. Implementations may ignore this request if output
+     * as array is not possible (either at all, or reliably).
+     * 
+     * @since 2.1
+     */
+    protected abstract BeanSerializerBase asArraySerializer();
+    
+    /**
+     * Copy-constructor that is useful for sub-classes that just want to
+     * copy all super-class properties without modifications.
+     */
+    protected BeanSerializerBase(BeanSerializerBase src) {
+        this(src, src._props, src._filteredProps);
+    }
+
+    /**
+     * Copy-constructor that will also rename properties with given prefix
+     * (if it's non-empty)
+     */
+    protected BeanSerializerBase(BeanSerializerBase src, NameTransformer unwrapper) {
+        this(src, rename(src._props, unwrapper), rename(src._filteredProps, unwrapper));
+    }
+    
+    private final static BeanPropertyWriter[] rename(BeanPropertyWriter[] props,
+            NameTransformer transformer)
+    {
+        if (props == null || props.length == 0 || transformer == null || transformer == NameTransformer.NOP) {
+            return props;
+        }
+        final int len = props.length;
+        BeanPropertyWriter[] result = new BeanPropertyWriter[len];
+        for (int i = 0; i < len; ++i) {
+            BeanPropertyWriter bpw = props[i];
+            if (bpw != null) {
+                result[i] = bpw.rename(transformer);
+            }
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Post-constriction processing: resolvable, contextual
+    /**********************************************************
+     */
+
+    /**
+     * We need to implement {@link ResolvableSerializer} to be able to
+     * properly handle cyclic type references.
+     */
+    @Override
+    public void resolve(SerializerProvider provider)
+        throws JsonMappingException
+    {
+        int filteredCount = (_filteredProps == null) ? 0 : _filteredProps.length;
+        for (int i = 0, len = _props.length; i < len; ++i) {
+            BeanPropertyWriter prop = _props[i];
+            // let's start with null serializer resolution actually
+            if (!prop.willSuppressNulls() && !prop.hasNullSerializer()) {
+                JsonSerializer<Object> nullSer = provider.findNullValueSerializer(prop);
+                if (nullSer != null) {
+                    prop.assignNullSerializer(nullSer);
+                    // also: remember to replace filtered property too? (see [JACKSON-364])
+                    if (i < filteredCount) {
+                        BeanPropertyWriter w2 = _filteredProps[i];
+                        if (w2 != null) {
+                            w2.assignNullSerializer(nullSer);
+                        }
+                    }
+                }
+            }
+            
+            if (prop.hasSerializer()) {
+                continue;
+            }
+            // [Issue#124]: allow use of converters
+            JsonSerializer<Object> ser = findConvertingSerializer(provider, prop);
+            if (ser == null) {
+                // Was the serialization type hard-coded? If so, use it
+                JavaType type = prop.getSerializationType();
+                
+                // 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) {
+                    type = provider.constructType(prop.getGenericPropertyType());
+                    if (!type.isFinal()) {
+                        if (type.isContainerType() || type.containedTypeCount() > 0) {
+                            prop.setNonTrivialBaseType(type);
+                        }
+                        continue;
+                    }
+                }
+                ser = provider.findValueSerializer(type, prop);
+                /* 04-Feb-2010, tatu: We may have stashed type serializer for content types
+                 *   too, earlier; if so, it's time to connect the dots here:
+                 */
+                if (type.isContainerType()) {
+                    TypeSerializer typeSer = type.getContentType().getTypeHandler();
+                    if (typeSer != null) {
+                        // for now, can do this only for standard containers...
+                        if (ser instanceof ContainerSerializer<?>) {
+                            // ugly casts... but necessary
+                            @SuppressWarnings("unchecked")
+                            JsonSerializer<Object> ser2 = (JsonSerializer<Object>)((ContainerSerializer<?>) ser).withValueTypeSerializer(typeSer);
+                            ser = ser2;
+                        }
+                    }
+                }
+            }
+            prop.assignSerializer(ser);
+            // and maybe replace filtered property too? (see [JACKSON-364])
+            if (i < filteredCount) {
+                BeanPropertyWriter w2 = _filteredProps[i];
+                if (w2 != null) {
+                    w2.assignSerializer(ser);
+                }
+            }
+        }
+
+        // also, any-getter may need to be resolved
+        if (_anyGetterWriter != null) {
+            _anyGetterWriter.resolve(provider);
+        }
+    }
+
+    /**
+     * 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,
+     * it is container type itself, not key or content type).
+     * 
+     * @since 2.2
+     */
+    protected JsonSerializer<Object> findConvertingSerializer(SerializerProvider provider,
+            BeanPropertyWriter prop)
+        throws JsonMappingException
+    {
+        final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+        if (intr != null) {
+            Object convDef = intr.findSerializationConverter(prop.getMember());
+            if (convDef != null) {
+                Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
+                JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
+                JsonSerializer<?> ser = provider.findValueSerializer(delegateType, prop);
+                return new StdDelegatingSerializer(conv, delegateType, ser);
+            }
+        }
+        return null;
+    }
+    
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        ObjectIdWriter oiw = _objectIdWriter;
+        String[] ignorals = null;
+        final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+        final AnnotatedMember accessor = (property == null || intr == null)
+                ? null : property.getMember();
+        
+        // First: may have an override for Object Id:
+        if (accessor != null) {
+            ignorals = intr.findPropertiesToIgnore(accessor);
+            ObjectIdInfo objectIdInfo = intr.findObjectIdInfo(accessor);
+            if (objectIdInfo == null) {
+                // no ObjectId override, but maybe ObjectIdRef?
+                if (oiw != null) {
+                    objectIdInfo = intr.findObjectReferenceInfo(accessor, new ObjectIdInfo("", null, null));
+                    oiw = _objectIdWriter.withAlwaysAsId(objectIdInfo.getAlwaysAsId());
+                }
+            } else {
+                /* Ugh: mostly copied from BeanSerializerBase: but can't easily
+                 * change it to be able to move to SerializerProvider (where it
+                 * really belongs)
+                 */
+                
+                // 2.1: allow modifications by "id ref" annotations as well:
+                objectIdInfo = intr.findObjectReferenceInfo(accessor, objectIdInfo);
+                ObjectIdGenerator<?> gen;
+                Class<?> implClass = objectIdInfo.getGeneratorType();
+                JavaType type = provider.constructType(implClass);
+                JavaType idType = provider.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
+                // Property-based generator is trickier
+                if (implClass == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
+                    String propName = objectIdInfo.getPropertyName();
+                    BeanPropertyWriter idProp = null;
+
+                    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+"'");
+                        }
+                        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)
+                             */
+                            if (i > 0) { // note: must shuffle both regular properties and filtered
+                                System.arraycopy(_props, 0, _props, 1, i);
+                                _props[0] = idProp;
+                                if (_filteredProps != null) {
+                                    BeanPropertyWriter fp = _filteredProps[i];
+                                    System.arraycopy(_filteredProps, 0, _filteredProps, 1, i);
+                                    _filteredProps[0] = fp;
+                                }
+                            }
+                            break;
+                        }
+                    }
+                    idType = idProp.getType();
+                    gen = new PropertyBasedObjectIdGenerator(objectIdInfo, idProp);
+                    oiw = ObjectIdWriter.construct(idType, null, gen, objectIdInfo.getAlwaysAsId());
+                } else { // other types need to be simpler
+                    gen = provider.objectIdGeneratorInstance(accessor, objectIdInfo);
+                    oiw = ObjectIdWriter.construct(idType, objectIdInfo.getPropertyName(), gen,
+                            objectIdInfo.getAlwaysAsId());
+                }
+            }
+        }
+        // either way, need to resolve serializer:
+        BeanSerializerBase contextual = this;
+        if (oiw != null) {
+            JsonSerializer<?> ser = provider.findValueSerializer(oiw.idType, property);
+            oiw = oiw.withSerializer(ser);
+            if (oiw != _objectIdWriter) {
+                contextual = contextual.withObjectIdWriter(oiw);
+            }
+        }
+        // And possibly add more properties to ignore
+        if (ignorals != null && ignorals.length != 0) {
+            contextual = contextual.withIgnorals(ignorals);
+        }
+        // One more thing: are we asked to serialize POJO as array?
+        JsonFormat.Shape shape = null;
+        if (accessor != null) {
+            JsonFormat.Value format = intr.findFormat((Annotated) accessor);
+
+            if (format != null) {
+                shape = format.getShape();
+            }
+        }
+        if (shape == null) {
+            shape = _serializationShape;
+        }
+        if (shape == JsonFormat.Shape.ARRAY) {
+            contextual = contextual.asArraySerializer();
+        }
+        return contextual;
+    }
+    
+    /*
+    /**********************************************************
+    /* Partial JsonSerializer implementation
+    /**********************************************************
+     */
+
+    @Override
+    public boolean usesObjectId() {
+        return (_objectIdWriter != null);
+    }
+    
+    // Main serialization method left unimplemented
+    @Override
+    public abstract void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException;
+
+    // Type-info-augmented case implemented as it does not usually differ between impls
+    @Override
+    public void serializeWithType(Object bean, JsonGenerator jgen,
+            SerializerProvider provider, TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        if (_objectIdWriter != null) {
+            _serializeWithObjectId(bean, jgen, provider, typeSer);
+            return;
+        }
+
+        String typeStr = (_typeId == null) ? null :_customTypeId(bean);
+        if (typeStr == null) {
+            typeSer.writeTypePrefixForObject(bean, jgen);
+        } else {
+            typeSer.writeCustomTypePrefixForObject(bean, jgen, typeStr);
+        }
+        if (_propertyFilterId != null) {
+            serializeFieldsFiltered(bean, jgen, provider);
+        } else {
+            serializeFields(bean, jgen, provider);
+        }
+        if (typeStr == null) {
+            typeSer.writeTypeSuffixForObject(bean, jgen);
+        } else {
+            typeSer.writeCustomTypeSuffixForObject(bean, jgen, typeStr);
+        }
+    }
+
+    protected final void _serializeWithObjectId(Object bean,
+            JsonGenerator jgen, SerializerProvider provider,
+            boolean startEndObject)
+        throws IOException, JsonGenerationException
+    {
+        final ObjectIdWriter w = _objectIdWriter;
+        WritableObjectId objectId = provider.findObjectId(bean, w.generator);
+        // If possible, write as id already
+        if (objectId.writeAsId(jgen, provider, w)) {
+            return;
+        }
+        // If not, need to inject the id:
+        Object id = objectId.generateId(bean);
+        if (w.alwaysAsId) {
+            w.serializer.serialize(id, jgen, provider);
+            return;
+        }
+        if (startEndObject) {
+            jgen.writeStartObject();
+        }
+        objectId.writeAsField(jgen, provider, w);
+        if (_propertyFilterId != null) {
+            serializeFieldsFiltered(bean, jgen, provider);
+        } else {
+            serializeFields(bean, jgen, provider);
+        }
+        if (startEndObject) {
+            jgen.writeEndObject();
+        }
+    }
+    
+    protected final void _serializeWithObjectId(Object bean,
+            JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        final ObjectIdWriter w = _objectIdWriter;
+        WritableObjectId objectId = provider.findObjectId(bean, w.generator);
+        // If possible, write as id already
+        if (objectId.writeAsId(jgen, provider, w)) {
+            return;
+        }
+        // If not, need to inject the id:
+        Object id = objectId.generateId(bean);
+        if (w.alwaysAsId) {
+            w.serializer.serialize(id, jgen, provider);
+            return;
+        }
+        String typeStr = (_typeId == null) ? null :_customTypeId(bean);
+        if (typeStr == null) {
+            typeSer.writeTypePrefixForObject(bean, jgen);
+        } else {
+            typeSer.writeCustomTypePrefixForObject(bean, jgen, typeStr);
+        }
+        objectId.writeAsField(jgen, provider, w);
+        if (_propertyFilterId != null) {
+            serializeFieldsFiltered(bean, jgen, provider);
+        } else {
+            serializeFields(bean, jgen, provider);
+        }
+        if (typeStr == null) {
+            typeSer.writeTypeSuffixForObject(bean, jgen);
+        } else {
+            typeSer.writeCustomTypeSuffixForObject(bean, jgen, typeStr);
+        }
+    }
+    
+    private final String _customTypeId(Object bean)
+    {
+        final Object typeId = _typeId.getValue(bean);
+        if (typeId == null) {
+            return "";
+        }
+        return (typeId instanceof String) ? (String) typeId : typeId.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* Field serialization methods
+    /**********************************************************
+     */
+
+    protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final BeanPropertyWriter[] props;
+        if (_filteredProps != null && provider.getActiveView() != null) {
+            props = _filteredProps;
+        } else {
+            props = _props;
+        }
+        int i = 0;
+        try {
+            for (final int len = props.length; i < len; ++i) {
+                BeanPropertyWriter prop = props[i];
+                if (prop != null) { // can have nulls in filtered list
+                    prop.serializeAsField(bean, jgen, provider);
+                }
+            }
+            if (_anyGetterWriter != null) {
+                _anyGetterWriter.getAndSerialize(bean, jgen, provider);
+            }
+        } catch (Exception e) {
+            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.
+             */
+            JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
+            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
+            mapE.prependPath(new JsonMappingException.Reference(bean, name));
+            throw mapE;
+        }
+    }
+
+    /**
+     * Alternative serialization method that gets called when there is a
+     * {@link BeanPropertyFilter} that needs to be called to determine
+     * which properties are to be serialized (and possibly how)
+     */
+    protected void serializeFieldsFiltered(Object bean, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        /* note: almost verbatim copy of "serializeFields"; copied (instead of merged)
+         * so that old method need not add check for existence of filter.
+         */
+        
+        final BeanPropertyWriter[] props;
+        if (_filteredProps != null && provider.getActiveView() != null) {
+            props = _filteredProps;
+        } else {
+            props = _props;
+        }
+        final BeanPropertyFilter filter = findFilter(provider);
+        // better also allow missing filter actually..
+        if (filter == null) {
+            serializeFields(bean, jgen, provider);
+            return;
+        }
+        
+        int i = 0;
+        try {
+            for (final int len = props.length; i < len; ++i) {
+                BeanPropertyWriter prop = props[i];
+                if (prop != null) { // can have nulls in filtered list
+                    filter.serializeAsField(bean, jgen, provider, prop);
+                }
+            }
+            if (_anyGetterWriter != null) {
+                _anyGetterWriter.getAndSerialize(bean, jgen, provider);
+            }
+        } catch (Exception e) {
+            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
+            wrapAndThrow(provider, e, bean, name);
+        } catch (StackOverflowError e) {
+            JsonMappingException mapE = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
+            String name = (i == props.length) ? "[anySetter]" : props[i].getName();
+            mapE.prependPath(new JsonMappingException.Reference(bean, name));
+            throw mapE;
+        }
+    }
+
+    /**
+     * Helper method used to locate filter that is needed, based on filter id
+     * this serializer was constructed with.
+     */
+    protected BeanPropertyFilter findFilter(SerializerProvider provider)
+        throws JsonMappingException
+    {
+        final Object filterId = _propertyFilterId;
+        FilterProvider filters = provider.getFilterProvider();
+        // Not ok to miss the provider, if a filter is declared to be needed.
+        if (filters == null) {
+            throw new JsonMappingException("Can not resolve BeanPropertyFilter with id '"+filterId+"'; no FilterProvider configured");
+        }
+        BeanPropertyFilter filter = filters.findFilter(filterId);
+        // But whether unknown ids are ok just depends on filter provider; if we get null that's fine
+        return filter;
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        ObjectNode o = createSchemaNode("object", true);
+        // [JACKSON-813]: Add optional JSON Schema id attribute, if found
+        // NOTE: not optimal, does NOT go through AnnotationIntrospector etc:
+        JsonSerializableSchema ann = _handledType.getAnnotation(JsonSerializableSchema.class);
+        if (ann != null) {
+            String id = ann.id();
+            if (id != null && id.length() > 0) {
+                o.put("id", id);
+            }
+        }
+ 
+        //todo: should the classname go in the title?
+        //o.put("title", _className);
+        ObjectNode propertiesNode = o.objectNode();
+        final BeanPropertyFilter filter;
+        if (_propertyFilterId != null) {
+            filter = findFilter(provider);
+        } else {
+            filter = null;
+        }
+        		
+        for (int i = 0; i < _props.length; i++) {
+            BeanPropertyWriter prop = _props[i];
+            if (filter == null) {
+                prop.depositSchemaProperty(propertiesNode, provider);
+            } else {
+                filter.depositSchemaProperty(prop, propertiesNode, provider);
+            }
+
+        }
+        o.put("properties", propertiesNode);
+        return o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        //deposit your output format 
+        JsonObjectFormatVisitor objectVisitor = (visitor == null) ? null : visitor.expectObjectFormat(typeHint);
+        if (objectVisitor != null) {
+            if (_propertyFilterId != null) {
+                BeanPropertyFilter filter = findFilter(visitor.getProvider());
+                for (int i = 0; i < _props.length; i++) {
+                    filter.depositSchemaProperty(_props[i], objectVisitor, visitor.getProvider());
+                }
+            } else {
+                for (int i = 0; i < _props.length; i++) {
+                    _props[i].depositSchemaProperty(objectVisitor);
+                }
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..439f922
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/BooleanSerializer.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+
+/**
+ * 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)
+ */
+ at JacksonStdImpl
+public final class BooleanSerializer
+    extends NonTypedScalarSerializerBase<Boolean>
+{
+    /**
+     * Whether type serialized is primitive (boolean) or wrapper
+     * (java.lang.Boolean); if true, former, if false, latter.
+     */
+    final boolean _forPrimitive;
+
+    public BooleanSerializer(boolean forPrimitive)
+    {
+        super(Boolean.class);
+        _forPrimitive = forPrimitive;
+    }
+
+    @Override
+    public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeBoolean(value.booleanValue());
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        return createSchemaNode("boolean", !_forPrimitive);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        if (visitor != null) {
+            visitor.expectBooleanFormat(typeHint);
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..653d7cf
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CalendarSerializer.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.Calendar;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+
+/**
+ * Standard serializer for {@link java.util.Calendar}.
+ * As with other time/date types, is configurable to produce timestamps
+ * (standard Java 64-bit timestamp) or textual formats (usually ISO-8601).
+ */
+ at JacksonStdImpl
+public class CalendarSerializer
+    extends DateTimeSerializerBase<Calendar>
+{
+    public static final CalendarSerializer instance = new CalendarSerializer();
+
+    public CalendarSerializer() { this(false, null); }
+
+    public CalendarSerializer(boolean useTimestamp, DateFormat customFormat) {
+        super(Calendar.class, useTimestamp, customFormat);
+    }
+
+    @Override
+    public CalendarSerializer withFormat(boolean timestamp, DateFormat customFormat)
+    {
+        if (timestamp) {
+            return new CalendarSerializer(true, null);
+        }
+        return new CalendarSerializer(false, customFormat);
+    }
+
+    @Override
+    protected long _timestamp(Calendar value) {
+        return (value == null) ? 0L : value.getTimeInMillis();
+    }
+
+    @Override
+    public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp) {
+            jgen.writeNumber(_timestamp(value));
+        } else if (_customFormat != null) {
+            // 21-Feb-2011, tatu: not optimal, but better than alternatives:
+            synchronized (_customFormat) {
+                jgen.writeString(_customFormat.format(value));
+            }
+        } else {
+            provider.defaultSerializeDateValue(value.getTime(), jgen);
+        }
+    }
+
+}
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
new file mode 100644
index 0000000..e99a9ea
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/CollectionSerializer.java
@@ -0,0 +1,158 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+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.ContainerSerializer;
+import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
+
+/**
+ * Fallback serializer for cases where Collection is not known to be
+ * of type for which more specializer serializer exists (such as
+ * index-accessible List).
+ * If so, we will just construct an {@link java.util.Iterator}
+ * to iterate over elements.
+ */
+public class CollectionSerializer
+    extends AsArraySerializerBase<Collection<?>>
+{
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public CollectionSerializer(JavaType elemType, boolean staticTyping, TypeSerializer vts,
+            BeanProperty property, JsonSerializer<Object> valueSerializer)
+    {
+        super(Collection.class, elemType, staticTyping, vts, property, valueSerializer);
+    }
+
+    public CollectionSerializer(CollectionSerializer src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSerializer)
+    {
+        super(src, property, vts, valueSerializer);
+    }
+    
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+        return new CollectionSerializer(_elementType, _staticTyping, vts, _property, _elementSerializer);
+    }
+
+    @Override
+    public CollectionSerializer withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer) {
+        return new CollectionSerializer(this, property, vts, elementSerializer);
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isEmpty(Collection<?> value) {
+        return (value == null) || value.isEmpty();
+    }
+
+    @Override
+    public boolean hasSingleElement(Collection<?> value) {
+        Iterator<?> it = value.iterator();
+        if (!it.hasNext()) {
+            return false;
+        }
+        it.next();
+        return !it.hasNext();
+    }
+    
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serializeContents(Collection<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_elementSerializer != null) {
+            serializeContentsUsing(value, jgen, provider, _elementSerializer);
+            return;
+        }
+        Iterator<?> it = value.iterator();
+        if (!it.hasNext()) {
+            return;
+        }
+        PropertySerializerMap serializers = _dynamicSerializers;
+        final TypeSerializer typeSer = _valueTypeSerializer;
+
+        int i = 0;
+        try {
+            do {
+                Object elem = it.next();
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    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);
+                        } else {
+                            serializer = _findAndAddDynamic(serializers, cc, provider);
+                        }
+                        serializers = _dynamicSerializers;
+                    }
+                    if (typeSer == null) {
+                        serializer.serialize(elem, jgen, provider);
+                    } else {
+                        serializer.serializeWithType(elem, jgen, provider, typeSer);
+                    }
+                }
+                ++i;
+            } while (it.hasNext());
+        } catch (Exception e) {
+            // [JACKSON-55] Need to add reference information
+            wrapAndThrow(provider, e, value, i);
+        }
+    }
+
+    public void serializeContentsUsing(Collection<?> value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> ser)
+        throws IOException, JsonGenerationException
+    {
+        Iterator<?> it = value.iterator();
+        if (it.hasNext()) {
+            TypeSerializer typeSer = _valueTypeSerializer;
+            int i = 0;
+            do {
+                Object elem = it.next();
+                try {
+                    if (elem == null) {
+                        provider.defaultSerializeNull(jgen);
+                    } else {
+                        if (typeSer == null) {
+                            ser.serialize(elem, jgen, provider);
+                        } else {
+                            ser.serializeWithType(elem, jgen, provider, typeSer);
+                        }
+                    }
+                    ++i;
+                } catch (Exception e) {
+                    // [JACKSON-55] Need to add reference information
+                    wrapAndThrow(provider, e, value, i);
+                }
+            } while (it.hasNext());
+        }
+    }
+}
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
new file mode 100644
index 0000000..deb90d0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateSerializer.java
@@ -0,0 +1,63 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+
+/**
+ * For efficiency, we will serialize Dates as longs, instead of
+ * potentially more readable Strings.
+ */
+ at JacksonStdImpl
+public class DateSerializer
+    extends DateTimeSerializerBase<Date>
+{
+    /**
+     * Default instance that is used when no contextual configuration
+     * is needed.
+     */
+    public static final DateSerializer instance = new DateSerializer();
+    
+    public DateSerializer() {
+        this(false, null);
+    }
+        
+    public DateSerializer(boolean useTimestamp, DateFormat customFormat) {
+        super(Date.class, useTimestamp, customFormat);
+    }
+
+    @Override
+    public DateSerializer withFormat(boolean timestamp, DateFormat customFormat)
+    {
+        if (timestamp) {
+            return new DateSerializer(true, null);
+        }
+        return new DateSerializer(false, customFormat);
+    }
+
+    @Override
+    protected long _timestamp(Date value) {
+        return (value == null) ? 0L : value.getTime();
+    }
+
+    @Override
+    public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp) {
+            jgen.writeNumber(_timestamp(value));
+        } else if (_customFormat != null) {
+            // 21-Feb-2011, tatu: not optimal, but better than alternatives:
+            synchronized (_customFormat) {
+                jgen.writeString(_customFormat.format(value));
+            }
+        } else {
+            provider.defaultSerializeDateValue(value, jgen);
+        }
+    }
+}
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
new file mode 100644
index 0000000..34ddd86
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/DateTimeSerializerBase.java
@@ -0,0 +1,151 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.*;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+
+public abstract class DateTimeSerializerBase<T>
+    extends StdScalarSerializer<T>
+    implements ContextualSerializer
+{
+    /**
+     * Flag that indicates that serialization must be done as the
+     * Java timetamp, regardless of other settings.
+     */
+    protected final boolean _useTimestamp;
+    
+    /**
+     * Specific format to use, if not default format: non null value
+     * also indicates that serialization is to be done as JSON String,
+     * not numeric timestamp, unless {@link #_useTimestamp} is true.
+     */
+    protected final DateFormat _customFormat;
+
+    protected DateTimeSerializerBase(Class<T> type,
+            boolean useTimestamp, DateFormat customFormat)
+    {
+        super(type);
+        _useTimestamp = useTimestamp;
+        _customFormat = customFormat;
+    }
+
+    public abstract DateTimeSerializerBase<T> withFormat(boolean timestamp, DateFormat customFormat);
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property) throws JsonMappingException
+    {
+        if (property != null) {
+            JsonFormat.Value format = prov.getAnnotationIntrospector().findFormat((Annotated)property.getMember());
+            if (format != null) {
+                // Simple case first: serialize as numeric timestamp?
+                if (format.getShape().isNumeric()) {
+                    return withFormat(true, null);
+                }
+                // If not, do we have a pattern?
+                TimeZone tz = format.getTimeZone();
+                String pattern = format.getPattern();
+                if (pattern.length() > 0){
+                    Locale loc = format.getLocale();
+                    if (loc == null) {
+                        loc = prov.getLocale();
+                    }
+                    SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
+                    if (tz == null) {
+                        tz = prov.getTimeZone();
+                    }
+                    df.setTimeZone(tz);
+                    return withFormat(false, df);
+                }
+                // If not, do we at least have a custom timezone?
+                if (tz != null) {
+                    DateFormat df = prov.getConfig().getDateFormat();
+                    // one shortcut: with our custom format, can simplify handling a bit
+                    if (df.getClass() == StdDateFormat.class) {
+                        df = StdDateFormat.getISO8601Format(tz);
+                    } else {
+                        // otherwise need to clone, re-set timezone:
+                        df = (DateFormat) df.clone();
+                        df.setTimeZone(tz);
+                    }
+                    return withFormat(false, df);
+                }
+            }
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isEmpty(T value) {
+        // let's assume "null date" (timestamp 0) qualifies for empty
+        return (value == null) || (_timestamp(value) == 0L);
+    }
+
+    protected abstract long _timestamp(T value);
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        //todo: (ryan) add a format for the date in the schema?
+        boolean asNumber = _useTimestamp;
+        if (!asNumber) {
+            if (_customFormat == null) {
+                asNumber = provider.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+            }
+        }
+        return createSchemaNode(asNumber ? "number" : "string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        boolean asNumber = _useTimestamp;
+        if (!asNumber) {
+            if (_customFormat == null) {
+                asNumber = visitor.getProvider().isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+            }
+        }
+        if (asNumber) {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.LONG);
+                v2.format(JsonValueFormat.UTC_MILLISEC);
+            }
+        } else {
+            JsonStringFormatVisitor v2 = visitor.expectStringFormat(typeHint);
+            if (v2 != null) {
+                v2.format(JsonValueFormat.DATE_TIME);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+
+    @Override
+    public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java
new file mode 100644
index 0000000..bcb62bd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumMapSerializer.java
@@ -0,0 +1,366 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+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.JsonFormatVisitable;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+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.util.EnumValues;
+
+/**
+ * Specialized serializer for {@link EnumMap}s. Somewhat tricky to
+ * implement because actual Enum value type may not be available;
+ * and if not, it can only be gotten from actual instance.
+ */
+ at JacksonStdImpl
+public class EnumMapSerializer
+    extends ContainerSerializer<EnumMap<? extends Enum<?>, ?>>
+    implements ContextualSerializer
+{
+    protected final boolean _staticTyping;
+
+    /**
+     * Property for which this serializer is being used, if any;
+     * null for root values.
+     */
+    protected final BeanProperty _property;
+    
+    /**
+     * If we know enumeration used as key, this will contain
+     * value set to use for serialization
+     */
+    protected final EnumValues _keyEnums;
+
+    protected final JavaType _valueType;
+    
+    /**
+     * Value serializer to use, if it can be statically determined
+     */
+    protected final JsonSerializer<Object> _valueSerializer;
+
+    /**
+     * Type serializer used for values, if any.
+     */
+    protected final TypeSerializer _valueTypeSerializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public EnumMapSerializer(JavaType valueType, boolean staticTyping, EnumValues keyEnums,
+            TypeSerializer vts, JsonSerializer<Object> valueSerializer)
+    {
+        super(EnumMap.class, false);
+        _property = null; // not yet known
+        _staticTyping = staticTyping || (valueType != null && valueType.isFinal());
+        _valueType = valueType;
+        _keyEnums = keyEnums;
+        _valueTypeSerializer = vts;
+        _valueSerializer = valueSerializer;
+    }
+
+    /**
+     * Constructor called when a contextual instance is created.
+     */
+    @SuppressWarnings("unchecked")
+    public EnumMapSerializer(EnumMapSerializer src, BeanProperty property,
+            JsonSerializer<?> ser)
+    {
+        super(src);
+        _property = property;
+        _staticTyping = src._staticTyping;
+        _valueType = src._valueType;
+        _keyEnums = src._keyEnums;
+        _valueTypeSerializer = src._valueTypeSerializer;
+        _valueSerializer = (JsonSerializer<Object>) ser;
+    }
+    
+    @Override
+    public EnumMapSerializer _withValueTypeSerializer(TypeSerializer vts) {
+        return new EnumMapSerializer(_valueType, _staticTyping, _keyEnums, vts,  _valueSerializer);
+    }
+
+    public EnumMapSerializer withValueSerializer(BeanProperty prop, JsonSerializer<?> ser) {
+        if (_property == prop && ser == _valueSerializer) {
+            return this;
+        }
+        return new EnumMapSerializer(this, prop, ser);
+    }
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _valueSerializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(provider, property, ser);
+        if (ser == null) {
+            if (_staticTyping) {
+                return withValueSerializer(property, provider.findValueSerializer(_valueType, property));
+            }
+        } else if (_valueSerializer instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        if (ser != _valueSerializer) {
+            return withValueSerializer(property, ser);
+        }
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    @Override
+    public JavaType getContentType() {
+        return _valueType;
+    }
+
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _valueSerializer;
+    }
+    
+    @Override
+    public boolean isEmpty(EnumMap<? extends Enum<?>,?> value) {
+        return (value == null) || value.isEmpty();
+    }
+
+    @Override
+    public boolean hasSingleElement(EnumMap<? extends Enum<?>, ?> value) {
+        return value.size() == 1;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serialize(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeStartObject();
+        if (!value.isEmpty()) {
+            serializeContents(value, jgen, provider);
+        }        
+        jgen.writeEndObject();
+    }
+
+    @Override
+    public void serializeWithType(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForObject(value, jgen);
+        if (!value.isEmpty()) {
+            serializeContents(value, jgen, provider);
+        }
+        typeSer.writeTypeSuffixForObject(value, jgen);
+    }
+    
+    protected void serializeContents(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_valueSerializer != null) {
+            serializeContentsUsing(value, jgen, provider, _valueSerializer);
+            return;
+        }
+        JsonSerializer<Object> prevSerializer = null;
+        Class<?> prevClass = null;
+        EnumValues keyEnums = _keyEnums;
+        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+        final TypeSerializer vts = _valueTypeSerializer;
+
+        for (Map.Entry<? extends Enum<?>,?> entry : value.entrySet()) {
+            final Object valueElem = entry.getValue();
+            if (skipNulls && valueElem == null) { // [JACKSON-314] skip entries with null values?
+                continue;
+            }
+            // First, serialize key
+            Enum<?> key = entry.getKey();
+            if (keyEnums == null) {
+                /* 15-Oct-2009, tatu: This is clumsy, but still the simplest efficient
+                 * way to do it currently, as Serializers get cached. (it does assume we'll always use
+                 * default serializer tho -- so ideally code should be rewritten)
+                 */
+                // ... and lovely two-step casting process too...
+                StdSerializer<?> ser = (StdSerializer<?>) provider.findValueSerializer(
+                        key.getDeclaringClass(), _property);
+                keyEnums = ((EnumSerializer) ser).getEnumValues();
+            }
+            jgen.writeFieldName(keyEnums.serializedValueFor(key));
+            if (valueElem == null) {
+                provider.defaultSerializeNull(jgen);
+                continue;
+            }
+            Class<?> cc = valueElem.getClass();
+            JsonSerializer<Object> currSerializer;
+            if (cc == prevClass) {
+                currSerializer = prevSerializer;
+            } else {
+                currSerializer = provider.findValueSerializer(cc, _property);
+                prevSerializer = currSerializer;
+                prevClass = cc;
+            }
+            try {
+                if (vts == null) {
+                    currSerializer.serialize(valueElem, jgen, provider);
+                } else {
+                    currSerializer.serializeWithType(valueElem, jgen, provider, vts);
+                }
+            } catch (Exception e) {
+                // [JACKSON-55] Need to add reference information
+                wrapAndThrow(provider, e, value, entry.getKey().name());
+            }
+        }
+    }
+
+    protected void serializeContentsUsing(EnumMap<? extends Enum<?>,?> value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> valueSer)
+        throws IOException, JsonGenerationException
+    {
+        EnumValues keyEnums = _keyEnums;
+        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+        final TypeSerializer vts = _valueTypeSerializer;
+        
+        for (Map.Entry<? extends Enum<?>,?> entry : value.entrySet()) {
+            final Object valueElem = entry.getValue();
+            if (skipNulls && valueElem == null) { // [JACKSON-314] skip entries with null values?
+                continue;
+            }
+            Enum<?> key = entry.getKey();
+            if (keyEnums == null) {
+                // clumsy, but has to do for now:
+                StdSerializer<?> ser = (StdSerializer<?>) provider.findValueSerializer(key.getDeclaringClass(),
+                        _property);
+                keyEnums = ((EnumSerializer) ser).getEnumValues();
+            }
+            jgen.writeFieldName(keyEnums.serializedValueFor(key));
+            if (valueElem == null) {
+                provider.defaultSerializeNull(jgen);
+                continue;
+            }
+            try {
+                if (vts == null) {
+                    valueSer.serialize(valueElem, jgen, provider);
+                } else {
+                    valueSer.serializeWithType(valueElem, jgen, provider, vts);
+                }
+            } catch (Exception e) {
+                wrapAndThrow(provider, e, value, entry.getKey().name());
+            }
+        }
+    }
+    
+    @SuppressWarnings({ "unchecked", "deprecation" })
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        ObjectNode o = createSchemaNode("object", true);
+        if (typeHint instanceof ParameterizedType) {
+            Type[] typeArgs = ((ParameterizedType) typeHint).getActualTypeArguments();
+            if (typeArgs.length == 2) {
+                JavaType enumType = provider.constructType(typeArgs[0]);
+                JavaType valueType = provider.constructType(typeArgs[1]);
+                ObjectNode propsNode = JsonNodeFactory.instance.objectNode();
+                Class<Enum<?>> enumClass = (Class<Enum<?>>) enumType.getRawClass();
+                for (Enum<?> enumValue : enumClass.getEnumConstants()) {
+                    JsonSerializer<Object> ser = provider.findValueSerializer(valueType.getRawClass(), _property);
+                    JsonNode schemaNode = (ser instanceof SchemaAware) ?
+                            ((SchemaAware) ser).getSchema(provider, null) :
+                            	com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+                    propsNode.put(provider.getConfig().getAnnotationIntrospector().findEnumValue((Enum<?>)enumValue), schemaNode);
+                }
+                o.put("properties", propsNode);
+            }
+        }
+        return o;
+    }
+
+    /**
+     * We consider possibility here that an EnumMap might actually just be serialized
+     * as something like a Record, given that number of keys is bound, just like
+     * with Objects/Records (and not unbounded like regular maps)
+     */
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        if (visitor == null) {
+            return;
+        }
+        JsonObjectFormatVisitor objectVisitor = visitor.expectObjectFormat(typeHint);
+        if (objectVisitor == null) {
+            return;
+        }
+        JavaType valueType = typeHint.containedType(1);
+        JsonSerializer<Object> ser = _valueSerializer;
+        if (ser == null && valueType != null) {
+            ser = visitor.getProvider().findValueSerializer(valueType, _property);
+        }
+        if (valueType == null) {
+            valueType = visitor.getProvider().constructType(Object.class);
+        }
+        EnumValues keyEnums = _keyEnums;
+        if (keyEnums == null) {
+            JavaType enumType = typeHint.containedType(0);
+             if (enumType == null) {
+                 throw new IllegalStateException("Can not resolve Enum type of EnumMap: "+typeHint);
+             }
+             JsonSerializer<?> enumSer = (enumType == null) ? null :
+                 visitor.getProvider().findValueSerializer(enumType, _property);
+             if (!(enumSer instanceof EnumSerializer)) {
+                 throw new IllegalStateException("Can not resolve Enum type of EnumMap: "+typeHint);
+             }
+             keyEnums = ((EnumSerializer) enumSer).getEnumValues();
+        }
+        for (Map.Entry<?,SerializedString> entry : keyEnums.internalMap().entrySet()) {
+            String name = entry.getValue().getValue();
+            // should all have the same type, so:
+            if (ser == null) {
+                ser = visitor.getProvider().findValueSerializer(entry.getKey().getClass(), _property);
+            }
+            objectVisitor.property(name, (JsonFormatVisitable) ser, valueType);
+        }
+    }
+}
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
new file mode 100644
index 0000000..33cfd7d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSerializer.java
@@ -0,0 +1,231 @@
+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.annotation.JsonFormat.Shape;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonStringFormatVisitor;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.util.EnumValues;
+
+/**
+ * Standard serializer used for {@link java.lang.Enum} types.
+ *<p>
+ * Based on {@link StdScalarSerializer} since the JSON value is
+ * scalar (String).
+ * 
+ * @author tatu
+ */
+ at JacksonStdImpl
+public class EnumSerializer
+    extends StdScalarSerializer<Enum<?>>
+    implements ContextualSerializer
+{
+    /**
+     * This map contains pre-resolved values (since there are ways
+     * to customize actual String constants to use) to use as
+     * serializations.
+     */
+    protected final EnumValues _values;
+
+    /**
+     * Flag that is set if we statically know serialization choice
+     * between index and textual format (null if it needs to be dynamically
+     * checked).
+     * 
+     * @since 2.1
+     */
+    protected final Boolean _serializeAsIndex;
+
+    /*
+    /**********************************************************
+    /* Construction, initialization
+    /**********************************************************
+     */
+    
+    /**
+     * @deprecated Since 2.1
+     */
+    @Deprecated
+    public EnumSerializer(EnumValues v) {
+        this(v, null);
+    }
+
+    public EnumSerializer(EnumValues v, Boolean serializeAsIndex)
+    {
+        super(Enum.class, false);
+        _values = v;
+        _serializeAsIndex = serializeAsIndex;
+    }
+    
+    /**
+     * Factory method used by {@link com.fasterxml.jackson.databind.ser.BasicSerializerFactory}
+     * for constructing serializer instance of Enum types.
+     * 
+     * @since 2.1
+     */
+    public static EnumSerializer construct(Class<Enum<?>> enumClass, SerializationConfig config,
+            BeanDescription beanDesc, JsonFormat.Value format)
+    {
+        // [JACKSON-212]: If toString() is to be used instead, leave EnumValues null
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        EnumValues v = config.isEnabled(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
+            ? EnumValues.constructFromToString(enumClass, intr) : EnumValues.constructFromName(enumClass, intr);
+        Boolean serializeAsIndex = _isShapeWrittenUsingIndex(enumClass, format, true);
+        return new EnumSerializer(v, serializeAsIndex);
+    }
+    
+    /**
+     * @deprecated Since 2.1 use the variant that takes in <code>format</code> argument.
+     */
+    @Deprecated
+    public static EnumSerializer construct(Class<Enum<?>> enumClass, SerializationConfig config,
+            BeanDescription beanDesc)
+    {
+        return construct(enumClass, config, beanDesc, beanDesc.findExpectedFormat(null));
+    }
+
+    /**
+     * To support some level of per-property configuration, we will need
+     * to make things contextual. We are limited to "textual vs index"
+     * choice here, however.
+     */
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property) throws JsonMappingException
+    {
+        if (property != null) {
+            JsonFormat.Value format = prov.getAnnotationIntrospector().findFormat((Annotated) property.getMember());
+            if (format != null) {
+                Boolean serializeAsIndex = _isShapeWrittenUsingIndex(property.getType().getRawClass(), format, false);
+                if (serializeAsIndex != _serializeAsIndex) {
+                    return new EnumSerializer(_values, serializeAsIndex);
+                }
+            }
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API for Jackson databind core
+    /**********************************************************
+     */
+    
+    public EnumValues getEnumValues() { return _values; }
+
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public final void serialize(Enum<?> en, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // [JACKSON-684]: serialize as index?
+        if (_serializeAsIndex(provider)) {
+            jgen.writeNumber(en.ordinal());
+            return;
+        }
+        jgen.writeString(_values.serializedValueFor(en));
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        // [JACKSON-684]: serialize as index?
+        if (_serializeAsIndex(provider)) {
+            return createSchemaNode("integer", true);
+        }
+        ObjectNode objectNode = createSchemaNode("string", true);
+        if (typeHint != null) {
+            JavaType type = provider.constructType(typeHint);
+            if (type.isEnumType()) {
+                ArrayNode enumNode = objectNode.putArray("enum");
+                for (SerializedString value : _values.values()) {
+                    enumNode.add(value.getValue());
+                }
+            }
+        }
+        return objectNode;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        // [JACKSON-684]: serialize as index?
+        if (visitor.getProvider().isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX)) {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) { // typically serialized as a small number (byte or int)
+                v2.numberType(JsonParser.NumberType.INT);
+            }
+        } else {
+    		JsonStringFormatVisitor stringVisitor = visitor.expectStringFormat(typeHint);
+    		if (typeHint != null && stringVisitor != null) {
+    			if (typeHint.isEnumType()) {
+    				Set<String> enums = new LinkedHashSet<String>();
+    				for (SerializedString value : _values.values()) {
+    					enums.add(value.getValue());
+    				}
+    				stringVisitor.enumTypes(enums);
+    			}
+    		}
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected final boolean _serializeAsIndex(SerializerProvider provider)
+    {
+        if (_serializeAsIndex != null) {
+            return _serializeAsIndex.booleanValue();
+        }
+        return provider.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX);
+        
+    }
+
+    /**
+     * Helper method called to check whether 
+     */
+    protected static Boolean _isShapeWrittenUsingIndex(Class<?> enumClass,
+            JsonFormat.Value format, boolean fromClass)
+    {
+        JsonFormat.Shape shape = (format == null) ? null : format.getShape();
+        if (shape == null) {
+            return null;
+        }
+        if (shape == Shape.ANY || shape == Shape.SCALAR) { // i.e. "default", check dynamically
+            return null;
+        }
+        if (shape == Shape.STRING) {
+            return Boolean.FALSE;
+        }
+        if (shape.isNumeric()) {
+            return Boolean.TRUE;
+        }
+        throw new IllegalArgumentException("Unsupported serialization shape ("+shape+") for Enum "+enumClass.getName()
+                    +", not supported as "
+                    + (fromClass? "class" : "property")
+                    +" annotation");
+    }
+}
+
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
new file mode 100644
index 0000000..e1edbf6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/EnumSetSerializer.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.util.EnumSet;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+public class EnumSetSerializer
+    extends AsArraySerializerBase<EnumSet<? extends Enum<?>>>
+{
+    public EnumSetSerializer(JavaType elemType, BeanProperty property)
+    {
+        super(EnumSet.class, elemType, true, null, property, null);
+    }
+
+    public EnumSetSerializer(EnumSetSerializer src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> valueSerializer)
+    {
+        super(src, property, vts, valueSerializer);
+    }
+    
+    @Override
+    public EnumSetSerializer _withValueTypeSerializer(TypeSerializer vts) {
+        // no typing for enums (always "hard" type)
+        return this;
+    }
+
+    @Override
+    public EnumSetSerializer withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer) {
+        return new EnumSetSerializer(this, property, vts, elementSerializer);
+    }
+    
+    @Override
+    public boolean isEmpty(EnumSet<? extends Enum<?>> value) {
+        return (value == null) || value.isEmpty();
+    }
+
+    @Override
+    public boolean hasSingleElement(EnumSet<? extends Enum<?>> value) {
+        return value.size() == 1;
+    }
+    
+    @Override
+    public void serializeContents(EnumSet<? extends Enum<?>> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        JsonSerializer<Object> enumSer = _elementSerializer;
+        /* Need to dynamically find instance serializer; unfortunately
+         * that seems to be the only way to figure out type (no accessors
+         * to the enum class that set knows)
+         */
+        for (Enum<?> en : value) {
+            if (enumSer == null) {
+                /* 12-Jan-2010, tatu: Since enums can not be polymorphic, let's
+                 *   not bother with typed serializer variant here
+                 */
+                enumSer = provider.findValueSerializer(en.getDeclaringClass(), _property);
+            }
+            enumSer.serialize(en, jgen, provider);
+        }
+    }
+}
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
new file mode 100644
index 0000000..834a2e7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/InetAddressSerializer.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Simple serializer for {@link java.net.InetAddress}. Main complexity is
+ * with registration, since same serializer is to be used for sub-classes.
+ */
+public class InetAddressSerializer
+    extends StdScalarSerializer<InetAddress>
+{
+    public final static InetAddressSerializer instance = new InetAddressSerializer();
+    
+    public InetAddressSerializer() { super(InetAddress.class); }
+
+    @Override
+    public void serialize(InetAddress value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // 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);
+            }
+        }
+        jgen.writeString(str);
+    }
+
+    @Override
+    public void serializeWithType(InetAddress value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        // Better ensure we don't use specific sub-classes...
+        typeSer.writeTypePrefixForScalar(value, jgen, InetAddress.class);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+}
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
new file mode 100644
index 0000000..aa65e10
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/IterableSerializer.java
@@ -0,0 +1,89 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContainerSerializer;
+
+ at JacksonStdImpl
+public class IterableSerializer
+    extends AsArraySerializerBase<Iterable<?>>
+{
+    public IterableSerializer(JavaType elemType, boolean staticTyping,
+            TypeSerializer vts, BeanProperty property)
+    {
+        super(Iterable.class, elemType, staticTyping, vts, property, null);
+    }
+
+    public IterableSerializer(IterableSerializer src, BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> valueSerializer)
+    {
+        super(src, property, vts, valueSerializer);
+    }
+    
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+        return new IterableSerializer(_elementType, _staticTyping, vts, _property);
+    }
+
+    @Override
+    public IterableSerializer withResolved(BeanProperty property,
+            TypeSerializer vts, JsonSerializer<?> elementSerializer) {
+        return new IterableSerializer(this, property, vts, elementSerializer);
+    }
+    
+    @Override
+    public boolean isEmpty(Iterable<?> value) {
+        // Not really good way to implement this, but has to do for now:
+        return (value == null) || !value.iterator().hasNext();
+    }
+
+    @Override
+    public boolean hasSingleElement(Iterable<?> value) {
+        // no really good way to determine (without consuming iterator), so:
+        return false;
+    }
+    
+    @Override
+    public void serializeContents(Iterable<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        Iterator<?> it = value.iterator();
+        if (it.hasNext()) {
+            final TypeSerializer typeSer = _valueTypeSerializer;
+            JsonSerializer<Object> prevSerializer = null;
+            Class<?> prevClass = null;
+            
+            do {
+                Object elem = it.next();
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                } else {
+                    // Minor optimization to avoid most lookups:
+                    Class<?> cc = elem.getClass();
+                    JsonSerializer<Object> currSerializer;
+                    if (cc == prevClass) {
+                        currSerializer = prevSerializer;
+                    } else {
+                        currSerializer = provider.findValueSerializer(cc, _property);
+                        prevSerializer = currSerializer;
+                        prevClass = cc;
+                    }
+                    if (typeSer == null) {
+                        currSerializer.serialize(elem, jgen, provider);
+                    } else {
+                        currSerializer.serializeWithType(elem, jgen, provider, typeSer);
+                    }
+                }
+            } while (it.hasNext());
+        }
+    }
+}
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
new file mode 100644
index 0000000..10758a6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java
@@ -0,0 +1,286 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+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.ser.BeanSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+
+/**
+ * Serializer class that can serialize Object that have a
+ * {@link com.fasterxml.jackson.annotation.JsonValue} annotation to
+ * indicate that serialization should be done by calling the method
+ * annotated, and serializing result it returns.
+ * <p/>
+ * Implementation note: we will post-process resulting serializer
+ * (much like what is done with {@link BeanSerializer})
+ * to figure out actual serializers for final types.
+ *  This must be done from {@link #createContextual} method, and NOT from constructor;
+ * otherwise we could end up with an infinite loop.
+ */
+ at JacksonStdImpl
+public class JsonValueSerializer
+    extends StdSerializer<Object>
+    implements ContextualSerializer, JsonFormatVisitable, SchemaAware
+{
+    protected final Method _accessorMethod;
+
+    protected final JsonSerializer<Object> _valueSerializer;
+
+    protected final BeanProperty _property;
+    
+    /**
+     * This is a flag that is set in rare (?) cases where this serializer
+     * is used for "natural" types (boolean, int, String, double); and where
+     * we actually must force type information wrapping, even though
+     * one would not normally be added.
+     */
+    protected final boolean _forceTypeInformation;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @param ser Explicit serializer to use, if caller knows it (which
+     *    occurs if and only if the "value method" was annotated with
+     *    {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}), otherwise
+     *    null
+     */
+    public JsonValueSerializer(Method valueMethod, JsonSerializer<Object> ser)
+    {
+        super(Object.class);
+        _accessorMethod = valueMethod;
+        _valueSerializer = ser;
+        _property = null;
+        _forceTypeInformation = true; // gets reconsidered when we are contextualized
+    }
+
+    @SuppressWarnings("unchecked")
+    public JsonValueSerializer(JsonValueSerializer src, BeanProperty property,
+            JsonSerializer<?> ser, boolean forceTypeInfo)
+    {
+        super(_notNullClass(src.handledType()));
+        _accessorMethod = src._accessorMethod;
+        _valueSerializer = (JsonSerializer<Object>) ser;
+        _property = property;
+        _forceTypeInformation = forceTypeInfo;
+    }
+
+    @SuppressWarnings("unchecked")
+    private final static Class<Object> _notNullClass(Class<?> cls) {
+        return (cls == null) ? Object.class : (Class<Object>) cls;
+    }
+    
+    public JsonValueSerializer withResolved(BeanProperty property,
+            JsonSerializer<?> ser, boolean forceTypeInfo)
+    {
+        if (_property == property && _valueSerializer == ser
+                && forceTypeInfo == _forceTypeInformation) {
+            return this;
+        }
+        return new JsonValueSerializer(this, property, ser, forceTypeInfo);
+    }
+    
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+
+    /**
+     * We can try to find the actual serializer for value, if we can
+     * statically figure out what the result type must be.
+     */
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        JsonSerializer<?> ser = _valueSerializer;
+        if (ser == null) {
+            /* Can only assign serializer statically if the declared type is final:
+             * 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
+            if (provider.isEnabled(MapperFeature.USE_STATIC_TYPING)
+                    || Modifier.isFinal(_accessorMethod.getReturnType().getModifiers())) {
+                JavaType t = provider.constructType(_accessorMethod.getGenericReturnType());
+                // false -> no need to cache
+                /* 10-Mar-2010, tatu: Ideally we would actually separate out type
+                 *   serializer from value serializer; but, alas, there's no access
+                 *   to serializer factory at this point... 
+                 */
+                /* 09-Dec-2010, tatu: Turns out we must add special handling for
+                 *   cases where "native" (aka "natural") type is being serialized,
+                 *   using standard serializer
+                 */
+                ser = provider.findTypedValueSerializer(t, false, _property);
+                boolean forceTypeInformation = isNaturalTypeWithStdHandling(t.getRawClass(), ser);
+                return withResolved(property, ser, forceTypeInformation);
+            }
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+            return withResolved(property, ser, _forceTypeInformation);
+        }
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serialize(Object bean, JsonGenerator jgen, SerializerProvider prov)
+        throws IOException, JsonGenerationException
+    {
+        try {
+            Object value = _accessorMethod.invoke(bean);
+            if (value == null) {
+                prov.defaultSerializeNull(jgen);
+                return;
+            }
+            JsonSerializer<Object> ser = _valueSerializer;
+            if (ser == null) {
+                Class<?> c = value.getClass();
+                /* 10-Mar-2010, tatu: Ideally we would actually separate out type
+                 *   serializer from value serializer; but, alas, there's no access
+                 *   to serializer factory at this point... 
+                 */
+                // let's cache it, may be needed soon again
+                ser = prov.findTypedValueSerializer(c, true, _property);
+            }
+            ser.serialize(value, jgen, 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() + "()");
+        }
+    }
+
+    @Override
+    public void serializeWithType(Object bean, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer0)
+        throws IOException, JsonProcessingException
+    {
+        // Regardless of other parts, first need to find value to serialize:
+        Object value = null;
+        try {
+            value = _accessorMethod.invoke(bean);
+            // and if we got null, can also just write it directly
+            if (value == null) {
+                provider.defaultSerializeNull(jgen);
+                return;
+            }
+            JsonSerializer<Object> ser = _valueSerializer;
+            if (ser == null) { // already got a serializer? fabulous, that be easy...
+//                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!)
+                 */
+                if (_forceTypeInformation) {
+                    typeSer0.writeTypePrefixForScalar(bean, jgen);
+                    ser.serialize(value, jgen, provider);
+                    typeSer0.writeTypeSuffixForScalar(bean, jgen);
+                    return;
+                }
+            }
+            /* 13-Feb-2013, tatu: Turns out that work-around should NOT be required
+             *   at all; it would not lead to correct behavior (as per #167).
+             */
+            // and then redirect type id lookups
+//            TypeSerializer typeSer = new TypeSerializerWrapper(typeSer0, bean);
+            ser.serializeWithType(value, jgen, provider, typeSer0);
+        } 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() + "()");
+        }
+    }
+    
+    @SuppressWarnings("deprecation")
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return (_valueSerializer instanceof SchemaAware) ?
+                ((SchemaAware) _valueSerializer).getSchema(provider, null) :
+                    com.fasterxml.jackson.databind.jsonschema.JsonSchema.getDefaultSchemaNode();
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        if (_valueSerializer != null) {
+            _valueSerializer.acceptJsonFormatVisitor(visitor, null); 
+        } else {
+            visitor.expectAnyFormat(typeHint);
+    	}
+    }
+
+    protected boolean isNaturalTypeWithStdHandling(Class<?> rawType, JsonSerializer<?> ser)
+    {
+        // First: do we have a natural type being handled?
+        if (rawType.isPrimitive()) {
+            if (rawType != Integer.TYPE && rawType != Boolean.TYPE && rawType != Double.TYPE) {
+                return false;
+            }
+        } else {
+            if (rawType != String.class &&
+                    rawType != Integer.class && rawType != Boolean.class && rawType != Double.class) {
+                return false;
+            }
+        }
+        return isDefaultSerializer(ser);
+    }
+    
+    /*
+    /**********************************************************
+    /* Other methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "(@JsonValue serializer for method " + _accessorMethod.getDeclaringClass() + "#" + _accessorMethod.getName() + ")";
+    }
+}
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
new file mode 100644
index 0000000..dd6093e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/MapSerializer.java
@@ -0,0 +1,555 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.*;
+
+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.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonMapFormatVisitor;
+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.TypeFactory;
+
+/**
+ * Standard serializer implementation for serializing {link java.util.Map} types.
+ *<p>
+ * Note: about the only configurable setting currently is ability to filter out
+ * entries with specified names.
+ */
+ at JacksonStdImpl
+public class MapSerializer
+    extends ContainerSerializer<Map<?,?>>
+    implements ContextualSerializer
+{
+    protected final static JavaType UNSPECIFIED_TYPE = TypeFactory.unknownType();
+    
+    /**
+     * Map-valued property being serialized with this instance
+     */
+    protected final BeanProperty _property;
+    
+    /**
+     * Set of entries to omit during serialization, if any
+     */
+    protected final HashSet<String> _ignoredEntries;
+
+    /**
+     * Whether static types should be used for serialization of values
+     * or not (if not, dynamic runtime type is used)
+     */
+    protected final boolean _valueTypeIsStatic;
+
+    /**
+     * Declared type of keys
+     */
+    protected final JavaType _keyType;
+
+    /**
+     * Declared type of contained values
+     */
+    protected final JavaType _valueType;
+
+    /**
+     * Key serializer to use, if it can be statically determined
+     */
+    protected JsonSerializer<Object> _keySerializer;
+    
+    /**
+     * Value serializer to use, if it can be statically determined
+     */
+    protected JsonSerializer<Object> _valueSerializer;
+
+    /**
+     * Type identifier serializer used for values, if any.
+     */
+    protected final TypeSerializer _valueTypeSerializer;
+
+    /**
+     * If value type can not be statically determined, mapping from
+     * runtime value types to serializers are stored in this object.
+     */
+    protected PropertySerializerMap _dynamicValueSerializers;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    protected MapSerializer(HashSet<String> ignoredEntries,
+            JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
+            TypeSerializer vts,
+            JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer)
+    {
+        super(Map.class, false);
+        _ignoredEntries = ignoredEntries;
+        _keyType = keyType;
+        _valueType = valueType;
+        _valueTypeIsStatic = valueTypeIsStatic;
+        _valueTypeSerializer = vts;
+        _keySerializer = (JsonSerializer<Object>) keySerializer;
+        _valueSerializer = (JsonSerializer<Object>) valueSerializer;
+        _dynamicValueSerializers = PropertySerializerMap.emptyMap();
+        _property = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected MapSerializer(MapSerializer src, BeanProperty property,
+            JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+            HashSet<String> ignored)
+    {
+        super(Map.class, false);
+        _ignoredEntries = ignored;
+        _keyType = src._keyType;
+        _valueType = src._valueType;
+        _valueTypeIsStatic = src._valueTypeIsStatic;
+        _valueTypeSerializer = src._valueTypeSerializer;
+        _keySerializer = (JsonSerializer<Object>) keySerializer;
+        _valueSerializer = (JsonSerializer<Object>) valueSerializer;
+        _dynamicValueSerializers = src._dynamicValueSerializers;
+        _property = property;
+    }
+
+    protected MapSerializer(MapSerializer src, TypeSerializer vts)
+    {
+        super(Map.class, false);
+        _ignoredEntries = src._ignoredEntries;
+        _keyType = src._keyType;
+        _valueType = src._valueType;
+        _valueTypeIsStatic = src._valueTypeIsStatic;
+        _valueTypeSerializer = vts;
+        _keySerializer = src._keySerializer;
+        _valueSerializer = src._valueSerializer;
+        _dynamicValueSerializers = src._dynamicValueSerializers;
+        _property = src._property;
+    }
+    
+    @Override
+    public MapSerializer _withValueTypeSerializer(TypeSerializer vts)
+    {
+        return new MapSerializer(this, vts);
+    }
+
+    public MapSerializer withResolved(BeanProperty property,
+            JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
+            HashSet<String> ignored)
+    {
+        return new MapSerializer(this, property, keySerializer, valueSerializer, ignored);
+    }
+    
+    public static MapSerializer construct(String[] ignoredList, JavaType mapType,
+            boolean staticValueType, TypeSerializer vts,
+            JsonSerializer<Object> keySerializer, JsonSerializer<Object> valueSerializer)
+    {
+        HashSet<String> ignoredEntries = toSet(ignoredList);
+        JavaType keyType, valueType;
+        
+        if (mapType == null) {
+            keyType = valueType = UNSPECIFIED_TYPE;
+        } else { 
+            keyType = mapType.getKeyType();
+            valueType = mapType.getContentType();
+        }
+        // If value type is final, it's same as forcing static value typing:
+        if (!staticValueType) {
+            staticValueType = (valueType != null && valueType.isFinal());
+        }
+        return new MapSerializer(ignoredEntries, keyType, valueType, staticValueType, vts,
+                keySerializer, valueSerializer);
+    }
+
+    private static HashSet<String> toSet(String[] ignoredEntries) {
+        if (ignoredEntries == null || ignoredEntries.length == 0) {
+            return null;
+        }
+        HashSet<String> result = new HashSet<String>(ignoredEntries.length);
+        for (String prop : ignoredEntries) {
+            result.add(prop);
+        }
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Post-processing (contextualization)
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        JsonSerializer<?> keySer = null;
+
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef;
+                final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+                serDef = intr.findKeySerializer(m);
+                if (serDef != null) {
+                    keySer = provider.serializerInstance(m, serDef);
+                }
+                serDef = intr.findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _valueSerializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(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.
+            if (_valueTypeIsStatic || hasContentTypeAnnotation(provider, property)) {
+                ser = provider.findValueSerializer(_valueType, property);
+            }
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        if (keySer == null) {
+            keySer = _keySerializer;
+        }
+        if (keySer == null) {
+            keySer = provider.findKeySerializer(_keyType, property);
+        } else if (keySer instanceof ContextualSerializer) {
+            keySer = ((ContextualSerializer) keySer).createContextual(provider, property);
+        }
+        HashSet<String> ignored = this._ignoredEntries;
+        AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+        if (intr != null && property != null) {
+            String[] moreToIgnore = intr.findPropertiesToIgnore(property.getMember());
+            if (moreToIgnore != null) {
+                ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
+                for (String str : moreToIgnore) {
+                    ignored.add(str);
+                }
+            }
+        }
+        return withResolved(property, keySer, ser, ignored);
+    }
+    
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    @Override
+    public JavaType getContentType() {
+        return _valueType;
+    }
+
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _valueSerializer;
+    }
+    
+    @Override
+    public boolean isEmpty(Map<?,?> value) {
+        return (value == null) || value.isEmpty();
+    }
+
+    @Override
+    public boolean hasSingleElement(Map<?,?> value) {
+        return (value.size() == 1);
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for currently assigned key serializer. Note that
+     * this may return null during construction of <code>MapSerializer</code>:
+     * depedencies are resolved during {@link #createContextual} method
+     * (which can be overridden by custom implementations), but for some
+     * dynamic types, it is possible that serializer is only resolved
+     * during actual serialization.
+     * 
+     * @since 2.0
+     */
+    public JsonSerializer<?> getKeySerializer() {
+        return _keySerializer;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializer implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public void serialize(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeStartObject();
+        if (!value.isEmpty()) {
+            if (provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
+                value = _orderEntries(value);
+            }
+            if (_valueSerializer != null) {
+                serializeFieldsUsing(value, jgen, provider, _valueSerializer);
+            } else {
+                serializeFields(value, jgen, provider);
+            }
+        }        
+        jgen.writeEndObject();
+    }
+
+    @Override
+    public void serializeWithType(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForObject(value, jgen);
+        if (!value.isEmpty()) {
+            if (provider.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
+                value = _orderEntries(value);
+            }
+            if (_valueSerializer != null) {
+                serializeFieldsUsing(value, jgen, provider, _valueSerializer);
+            } else {
+                serializeFields(value, jgen, provider);
+            }
+        }
+        typeSer.writeTypeSuffixForObject(value, jgen);
+    }
+
+    /*
+    /**********************************************************
+    /* JsonSerializer implementation
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to serialize fields, when the value type is not statically known.
+     */
+    public void serializeFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        // If value type needs polymorphic type handling, some more work needed:
+        if (_valueTypeSerializer != null) {
+            serializeTypedFields(value, jgen, provider);
+            return;
+        }
+        final JsonSerializer<Object> keySerializer = _keySerializer;
+        
+        final HashSet<String> ignored = _ignoredEntries;
+        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+
+        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, jgen, provider);
+            } else {
+                // [JACKSON-314] skip entries with null values?
+                if (skipNulls && valueElem == null) continue;
+                // One twist: is entry ignorable? If so, skip
+                if (ignored != null && ignored.contains(keyElem)) continue;
+                keySerializer.serialize(keyElem, jgen, provider);
+            }
+
+            // And then value
+            if (valueElem == null) {
+                provider.defaultSerializeNull(jgen);
+            } else {
+                Class<?> cc = valueElem.getClass();
+                JsonSerializer<Object> serializer = serializers.serializerFor(cc);
+                if (serializer == null) {
+                    if (_valueType.hasGenericTypes()) {
+                        serializer = _findAndAddDynamic(serializers,
+                                provider.constructSpecializedType(_valueType, cc), provider);
+                    } else {
+                        serializer = _findAndAddDynamic(serializers, cc, provider);
+                    }
+                    serializers = _dynamicValueSerializers;
+                }
+                try {
+                    serializer.serialize(valueElem, jgen, provider);
+                } catch (Exception e) {
+                    // [JACKSON-55] Need to add reference information
+                    String keyDesc = ""+keyElem;
+                    wrapAndThrow(provider, e, value, keyDesc);
+                }
+            }
+        }
+    }
+
+    /**
+     * 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 serializeFieldsUsing(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> ser)
+            throws IOException, JsonGenerationException
+    {
+        final JsonSerializer<Object> keySerializer = _keySerializer;
+        final HashSet<String> ignored = _ignoredEntries;
+        final TypeSerializer typeSer = _valueTypeSerializer;
+        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+
+        for (Map.Entry<?,?> entry : value.entrySet()) {
+            Object valueElem = entry.getValue();
+            Object keyElem = entry.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) continue;
+                if (ignored != null && ignored.contains(keyElem)) continue;
+                keySerializer.serialize(keyElem, jgen, provider);
+            }
+            if (valueElem == null) {
+                provider.defaultSerializeNull(jgen);
+            } else {
+                try {
+                    if (typeSer == null) {
+                        ser.serialize(valueElem, jgen, provider);
+                    } else {
+                        ser.serializeWithType(valueElem, jgen, provider, typeSer);
+                    }
+                } catch (Exception e) {
+                    // [JACKSON-55] Need to add reference information
+                    String keyDesc = ""+keyElem;
+                    wrapAndThrow(provider, e, value, keyDesc);
+                }
+            }
+        }
+    }
+
+    protected void serializeTypedFields(Map<?,?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final JsonSerializer<Object> keySerializer = _keySerializer;
+        JsonSerializer<Object> prevValueSerializer = null;
+        Class<?> prevValueClass = null;
+        final HashSet<String> ignored = _ignoredEntries;
+        final boolean skipNulls = !provider.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
+    
+        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, jgen, provider);
+            } else {
+                // [JACKSON-314] also may need to skip entries with null values
+                if (skipNulls && valueElem == null) continue;
+                // One twist: is entry ignorable? If so, skip
+                if (ignored != null && ignored.contains(keyElem)) continue;
+                keySerializer.serialize(keyElem, jgen, provider);
+            }
+    
+            // And then value
+            if (valueElem == null) {
+                provider.defaultSerializeNull(jgen);
+            } else {
+                Class<?> cc = valueElem.getClass();
+                JsonSerializer<Object> currSerializer;
+                if (cc == prevValueClass) {
+                    currSerializer = prevValueSerializer;
+                } else {
+                    currSerializer = provider.findValueSerializer(cc, _property);
+                    prevValueSerializer = currSerializer;
+                    prevValueClass = cc;
+                }
+                try {
+                    currSerializer.serializeWithType(valueElem, jgen, provider, _valueTypeSerializer);
+                } catch (Exception e) {
+                    // [JACKSON-55] Need to add reference information
+                    String keyDesc = ""+keyElem;
+                    wrapAndThrow(provider, e, value, keyDesc);
+                }
+            }
+        }
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        ObjectNode o = createSchemaNode("object", true);
+        //(ryan) 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 o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        JsonMapFormatVisitor v2 = (visitor == null) ? null : visitor.expectMapFormat(typeHint);        
+        if (v2 != null) {
+            v2.keyFormat(_keySerializer, _keyType);
+            JsonSerializer<?> valueSer = _valueSerializer;
+            if (valueSer == null) {
+                valueSer = _findAndAddDynamic(_dynamicValueSerializers,
+                            _valueType, visitor.getProvider());
+            }
+            v2.valueFormat(valueSer, _valueType);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal helper methods
+    /**********************************************************
+     */
+    
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            Class<?> type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        // did we get a new map of serializers? If so, start using it
+        if (map != result.map) {
+            _dynamicValueSerializers = result.map;
+        }
+        return result.serializer;
+    }
+
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            JavaType type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        if (map != result.map) {
+            _dynamicValueSerializers = result.map;
+        }
+        return result.serializer;
+    }
+
+    protected Map<?,?> _orderEntries(Map<?,?> input)
+    {
+        // minor optimization: may already be sorted?
+        if (input instanceof SortedMap<?,?>) {
+            return input;
+        }
+        return new TreeMap<Object,Object>(input);
+    }
+}
+
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
new file mode 100644
index 0000000..16050e5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NonTypedScalarSerializerBase.java
@@ -0,0 +1,32 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Intermediate base class for limited number of scalar types
+ * that should never include type information. These are "native"
+ * types that are default mappings for corresponding JSON scalar
+ * types: {@link java.lang.String}, {@link java.lang.Integer},
+ * {@link java.lang.Double} and {@link java.lang.Boolean}.
+ */
+public abstract class NonTypedScalarSerializerBase<T>
+    extends StdScalarSerializer<T>
+{
+    protected NonTypedScalarSerializerBase(Class<T> t) {
+        super(t);
+    }
+
+    @Override
+    public final void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        // no type info, just regular serialization
+        serialize(value, jgen, provider);            
+    }
+}
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
new file mode 100644
index 0000000..8dc8081
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NullSerializer.java
@@ -0,0 +1,47 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.lang.reflect.Type;
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+
+/**
+ * This is a simple dummy serializer that will just output literal
+ * JSON null value whenever serialization is requested.
+ * Used as the default "null serializer" (which is used for serializing
+ * null object references unless overridden), as well as for some
+ * more exotic types (java.lang.Void).
+ */
+ at JacksonStdImpl
+public class NullSerializer
+    extends StdSerializer<Object>
+{
+    public final static NullSerializer instance = new NullSerializer();
+    
+    private NullSerializer() { super(Object.class); }
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeNull();
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return createSchemaNode("null");
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        visitor.expectNullFormat(typeHint);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
new file mode 100644
index 0000000..859b53c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/NumberSerializers.java
@@ -0,0 +1,324 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonNumberFormatVisitor;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Container class for serializers used for handling standard JDK-provided types.
+ */
+public class NumberSerializers
+{
+    protected NumberSerializers() { }
+    
+    public static void addAll(Map<String, JsonSerializer<?>> allDeserializers)
+    {
+        final JsonSerializer<?> intS = new IntegerSerializer();
+        allDeserializers.put(Integer.class.getName(), intS);
+        allDeserializers.put(Integer.TYPE.getName(), intS);
+        allDeserializers.put(Long.class.getName(), LongSerializer.instance);
+        allDeserializers.put(Long.TYPE.getName(), LongSerializer.instance);
+        allDeserializers.put(Byte.class.getName(), IntLikeSerializer.instance);
+        allDeserializers.put(Byte.TYPE.getName(), IntLikeSerializer.instance);
+        allDeserializers.put(Short.class.getName(), ShortSerializer.instance);
+        allDeserializers.put(Short.TYPE.getName(), ShortSerializer.instance);
+
+        // Numbers, limited length floating point
+        allDeserializers.put(Float.class.getName(), FloatSerializer.instance);
+        allDeserializers.put(Float.TYPE.getName(), FloatSerializer.instance);
+        allDeserializers.put(Double.class.getName(), DoubleSerializer.instance);
+        allDeserializers.put(Double.TYPE.getName(), DoubleSerializer.instance);
+    }
+
+    /*
+    /**********************************************************
+    /* Concrete serializers, numerics
+    /**********************************************************
+     */
+
+    @JacksonStdImpl
+    public final static class ShortSerializer
+        extends StdScalarSerializer<Short>
+    {
+        final static ShortSerializer instance = new ShortSerializer();
+    
+        public ShortSerializer() { super(Short.class); }
+        
+        @Override
+        public void serialize(Short value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.shortValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("number", true);		// msteiger: maybe "integer" or "short" ?
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.INT);			// should be SHORT
+            }
+        }
+    }
+    
+    /**
+     * This is the special serializer for regular {@link java.lang.Integer}s
+     * (and primitive ints)
+     *<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)
+     */
+    @JacksonStdImpl
+    public final static class IntegerSerializer
+        extends NonTypedScalarSerializerBase<Integer>
+    {
+        public IntegerSerializer() { super(Integer.class); }
+    
+        @Override
+        public void serialize(Integer value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.intValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("integer", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.INT);
+            }
+        }
+    }
+
+    /**
+     * Similar to {@link IntegerSerializer}, but will not cast to Integer:
+     * instead, cast is to {@link java.lang.Number}, and conversion is
+     * by calling {@link java.lang.Number#intValue}.
+     */
+    @JacksonStdImpl
+    public final static class IntLikeSerializer
+        extends StdScalarSerializer<Number>
+    {
+        final static IntLikeSerializer instance = new IntLikeSerializer();
+    
+        public IntLikeSerializer() { super(Number.class); }
+        
+        @Override
+        public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.intValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("integer", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.INT);
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class LongSerializer
+        extends StdScalarSerializer<Long>
+    {
+        final static LongSerializer instance = new LongSerializer();
+    
+        public LongSerializer() { super(Long.class); }
+        
+        @Override
+        public void serialize(Long value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.longValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("number", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.LONG);
+            }
+        }
+    }
+    
+    @JacksonStdImpl
+    public final static class FloatSerializer
+        extends StdScalarSerializer<Float>
+    {
+        final static FloatSerializer instance = new FloatSerializer();
+    
+        public FloatSerializer() { super(Float.class); }
+        
+        @Override
+        public void serialize(Float value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.floatValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("number", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.FLOAT);
+            }
+        }
+    }
+
+    /**
+     * This is the special serializer for regular {@link java.lang.Double}s
+     * (and primitive doubles)
+     *<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)
+     */
+    @JacksonStdImpl
+    public final static class DoubleSerializer
+        extends NonTypedScalarSerializerBase<Double>
+    {
+        final static DoubleSerializer instance = new DoubleSerializer();
+    
+        public DoubleSerializer() { super(Double.class); }
+    
+        @Override
+        public void serialize(Double value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.doubleValue());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("number", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+        {
+            JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.DOUBLE);
+            }
+        }
+    }
+    
+    /**
+     * As a fallback, we may need to use this serializer for other
+     * types of {@link Number}s (custom types).
+     */
+    @JacksonStdImpl
+    public final static class NumberSerializer
+        extends StdScalarSerializer<Number>
+    {
+        public final static NumberSerializer instance = new NumberSerializer();
+    
+        public NumberSerializer() { super(Number.class); }
+    
+        @Override
+        public void serialize(Number value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            // As per [JACKSON-423], handling for BigInteger and BigDecimal was missing!
+            if (value instanceof BigDecimal) {
+                if (provider.isEnabled(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN)) {
+                    // [Issue#232]: Ok, rather clumsy, but let's try to work around the problem with:
+                    if (!(jgen instanceof TokenBuffer)) {
+                        jgen.writeNumber(((BigDecimal) value).toPlainString());
+                        return;
+                    }
+                }
+                jgen.writeNumber((BigDecimal) value);
+            } else if (value instanceof BigInteger) {
+                jgen.writeNumber((BigInteger) value);
+                
+            /* These shouldn't match (as there are more specific ones),
+             * but just to be sure:
+             */
+            } else if (value instanceof Integer) {
+                jgen.writeNumber(value.intValue());
+            } else if (value instanceof Long) {
+                jgen.writeNumber(value.longValue());
+            } else if (value instanceof Double) {
+                jgen.writeNumber(value.doubleValue());
+            } else if (value instanceof Float) {
+                jgen.writeNumber(value.floatValue());
+            } else if ((value instanceof Byte) || (value instanceof Short)) {
+                jgen.writeNumber(value.intValue()); // doesn't need to be cast to smaller numbers
+            } else {
+                // We'll have to use fallback "untyped" number write method
+                jgen.writeNumber(value.toString());
+            }
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("number", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+        {
+            // Hmmh. What should it be? Ideally should probably indicate BIG_DECIMAL
+            // to ensure no information is lost? But probably won't work that well...
+            JsonNumberFormatVisitor v2 = visitor.expectNumberFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.BIG_DECIMAL);
+            }
+        }
+    }
+}
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
new file mode 100644
index 0000000..894bfc3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ObjectArraySerializer.java
@@ -0,0 +1,388 @@
+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.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>).
+ */
+ at JacksonStdImpl
+public class ObjectArraySerializer
+    extends ArraySerializerBase<Object[]>
+    implements ContextualSerializer
+{
+    /**
+     * Whether we are using static typing (using declared types, ignoring
+     * runtime type) or not for elements.
+     */
+    protected final boolean _staticTyping;
+
+    /**
+     * Declared type of element entries
+     */
+    protected final JavaType _elementType;
+
+    /**
+     * Type serializer to use for values, if any.
+     */
+    protected final TypeSerializer _valueTypeSerializer;
+    
+    /**
+     * Value serializer to use, if it can be statically determined.
+     */
+    protected JsonSerializer<Object> _elementSerializer;
+
+    /**
+     * If element type can not be statically determined, mapping from
+     * runtime type to serializer is handled using this object
+     */
+    protected PropertySerializerMap _dynamicSerializers;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public ObjectArraySerializer(JavaType elemType, boolean staticTyping,
+            TypeSerializer vts, JsonSerializer<Object> elementSerializer)
+    {
+        super(Object[].class, null);
+        _elementType = elemType;
+        _staticTyping = staticTyping;
+        _valueTypeSerializer = vts;
+        _dynamicSerializers = PropertySerializerMap.emptyMap();
+        _elementSerializer = elementSerializer;
+    }
+
+    public ObjectArraySerializer(ObjectArraySerializer src, TypeSerializer vts)
+    {
+        super(src);
+        _elementType = src._elementType;
+        _valueTypeSerializer = vts;
+        _staticTyping = src._staticTyping;
+        _dynamicSerializers = src._dynamicSerializers;
+        _elementSerializer = src._elementSerializer;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public ObjectArraySerializer(ObjectArraySerializer src,
+            BeanProperty property, TypeSerializer vts, JsonSerializer<?> elementSerializer)
+    {
+        super(src,  property);
+        _elementType = src._elementType;
+        _valueTypeSerializer = vts;
+        _staticTyping = src._staticTyping;
+        _dynamicSerializers = src._dynamicSerializers;
+        _elementSerializer = (JsonSerializer<Object>) elementSerializer;
+    }
+    
+    @Override
+    public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts)
+    {
+        return new ObjectArraySerializer(_elementType, _staticTyping, vts, _elementSerializer);
+    }
+
+    public ObjectArraySerializer withResolved(BeanProperty prop,
+            TypeSerializer vts, JsonSerializer<?> ser) {
+        if (_property == prop && ser == _elementSerializer && _valueTypeSerializer == vts) {
+            return this;
+        }
+        return new ObjectArraySerializer(this, prop, vts, ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Post-processing
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property)
+        throws JsonMappingException
+    {
+        TypeSerializer vts = _valueTypeSerializer;
+        if (vts != null) {
+            vts = vts.forProperty(property);
+        }
+        /* 29-Sep-2012, tatu: Actually, we need to do much more contextual
+         *    checking here since we finally know for sure the property,
+         *    and it may have overrides
+         */
+        JsonSerializer<?> ser = null;
+        // First: if we have a property, may have property-annotation overrides
+        if (property != null) {
+            AnnotatedMember m = property.getMember();
+            if (m != null) {
+                Object serDef = provider.getAnnotationIntrospector().findContentSerializer(m);
+                if (serDef != null) {
+                    ser = provider.serializerInstance(m, serDef);
+                }
+            }
+        }
+        if (ser == null) {
+            ser = _elementSerializer;
+        }
+        // #124: May have a content converter
+        ser = findConvertingContentSerializer(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.
+            if (_elementType != null) {
+                if (_staticTyping || hasContentTypeAnnotation(provider, property)) {
+                    ser = provider.findValueSerializer(_elementType, property);
+                }
+            }
+        } else if (ser instanceof ContextualSerializer) {
+            ser = ((ContextualSerializer) ser).createContextual(provider, property);
+        }
+        return withResolved(property, vts, ser);
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+        
+    @Override
+    public JavaType getContentType() {
+        return _elementType;
+    }
+
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _elementSerializer;
+    }
+
+    @Override
+    public boolean isEmpty(Object[] value) {
+        return (value == null) || (value.length == 0);
+    }
+
+    @Override
+    public boolean hasSingleElement(Object[] value) {
+        return (value.length == 1);
+    }
+
+    /*
+    /**********************************************************
+    /* Actual serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public void serializeContents(Object[] value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.length;
+        if (len == 0) {
+            return;
+        }
+        if (_elementSerializer != null) {
+            serializeContentsUsing(value, jgen, provider, _elementSerializer);
+            return;
+        }
+        if (_valueTypeSerializer != null) {
+            serializeTypedContents(value, jgen, provider);
+            return;
+        }
+        int i = 0;
+        Object elem = null;
+        try {
+            PropertySerializerMap serializers = _dynamicSerializers;
+            for (; i < len; ++i) {
+                elem = value[i];
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                    continue;
+                }
+                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);
+                    } else {
+                        serializer = _findAndAddDynamic(serializers, cc, provider);
+                    }
+                }
+                serializer.serialize(elem, jgen, 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);
+        }
+    }
+
+    public void serializeContentsUsing(Object[] value, JsonGenerator jgen, SerializerProvider provider,
+            JsonSerializer<Object> ser)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.length;
+        final TypeSerializer typeSer = _valueTypeSerializer;
+
+        int i = 0;
+        Object elem = null;
+        try {
+            for (; i < len; ++i) {
+                elem = value[i];
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                    continue;
+                }
+                if (typeSer == null) {
+                    ser.serialize(elem, jgen, provider);
+                } else {
+                    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);
+        }
+    }
+
+    public void serializeTypedContents(Object[] value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        final int len = value.length;
+        final TypeSerializer typeSer = _valueTypeSerializer;
+        int i = 0;
+        Object elem = null;
+        try {
+            PropertySerializerMap serializers = _dynamicSerializers;
+            for (; i < len; ++i) {
+                elem = value[i];
+                if (elem == null) {
+                    provider.defaultSerializeNull(jgen);
+                    continue;
+                }
+                Class<?> cc = elem.getClass();
+                JsonSerializer<Object> serializer = serializers.serializerFor(cc);
+                if (serializer == null) {
+                    serializer = _findAndAddDynamic(serializers, cc, provider);
+                }
+                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);
+        }
+    }
+    
+    @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.put("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.put("items", schemaNode);
+                }
+            }
+        }
+        return o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        JsonArrayFormatVisitor arrayVisitor = visitor.expectArrayFormat(typeHint);
+        if (arrayVisitor != null) {
+            TypeFactory tf = visitor.getProvider().getTypeFactory();
+            JavaType contentType = tf.moreSpecificType(_elementType, typeHint.getContentType());
+            if (contentType == null) {
+                throw new JsonMappingException("Could not resolve type");
+            }
+            JsonSerializer<?> valueSer = _elementSerializer;
+            if (valueSer == null) {
+                valueSer = visitor.getProvider().findValueSerializer(contentType, _property);
+            }
+            arrayVisitor.itemsFormat(valueSer, contentType);
+        }
+    }
+
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            Class<?> type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        // did we get a new map of serializers? If so, start using it
+        if (map != result.map) {
+            _dynamicSerializers = result.map;
+        }
+        return result.serializer;
+    }
+
+    protected final JsonSerializer<Object> _findAndAddDynamic(PropertySerializerMap map,
+            JavaType type, SerializerProvider provider) throws JsonMappingException
+    {
+        PropertySerializerMap.SerializerAndMapResult result = map.findAndAddSerializer(type, provider, _property);
+        // did we get a new map of serializers? If so, start using it
+        if (map != result.map) {
+            _dynamicSerializers = result.map;
+        }
+        return result.serializer;
+    }
+
+}
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
new file mode 100644
index 0000000..453b54c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/RawSerializer.java
@@ -0,0 +1,60 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.lang.reflect.Type;
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * This is a simple dummy serializer that will just output raw values by calling
+ * toString() on value to serialize.
+ */
+ at JacksonStdImpl
+public class RawSerializer<T>
+    extends StdSerializer<T>
+{
+    /**
+     * Constructor takes in expected type of values; but since caller
+     * typically can not really provide actual type parameter, we will
+     * just take wild card and coerce type.
+     */
+    public RawSerializer(Class<?> cls) {
+        super(cls, false);
+    }
+
+    @Override
+    public void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeRawValue(value.toString());
+    }
+
+    @Override
+    public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        typeSer.writeTypePrefixForScalar(value, jgen);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        // type not really known, but since it is a JSON string:
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        visitor.expectStringFormat(typeHint);
+    }
+}
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
new file mode 100644
index 0000000..9b669c8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SerializableSerializer.java
@@ -0,0 +1,116 @@
+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.
+ */
+ at JacksonStdImpl
+public class SerializableSerializer
+    extends StdSerializer<JsonSerializable>
+{
+    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
+    public void serialize(JsonSerializable value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        value.serialize(jgen, provider);
+    }
+
+    @Override
+    public final void serializeWithType(JsonSerializable value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        value.serializeWithType(jgen, provider, 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.put("properties", _getObjectMapper().readTree(objectProperties));
+            } catch (IOException e) {
+                throw new JsonMappingException("Failed to parse @JsonSerializableSchema.schemaObjectPropertiesDefinition value");
+            }
+        }
+        if (itemDefinition != null) {
+            try {
+                objectNode.put("items", _getObjectMapper().readTree(itemDefinition));
+            } catch (IOException e) {
+                throw new JsonMappingException("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)
+        throws JsonMappingException
+    {
+        visitor.expectAnyFormat(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
new file mode 100644
index 0000000..0c0ea49
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlDateSerializer.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+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.jsonFormatVisitors.JsonStringFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
+
+/**
+ * Compared to regular {@link java.util.Date} serialization, we do use String
+ * representation here. Why? Basically to truncate of time part, since
+ * that should not be used by plain SQL date.
+ */
+ at JacksonStdImpl
+public class SqlDateSerializer
+    extends StdScalarSerializer<java.sql.Date>
+{
+    public SqlDateSerializer() { super(java.sql.Date.class); }
+
+    @Override
+    public void serialize(java.sql.Date value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeString(value.toString());
+    }
+
+    @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
+    {
+        JsonStringFormatVisitor v2 = (visitor == null) ? null : visitor.expectStringFormat(typeHint);
+        if (v2 != null) {
+            v2.format(JsonValueFormat.DATE_TIME);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java
new file mode 100644
index 0000000..9b4043f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/SqlTimeSerializer.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+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.jsonFormatVisitors.JsonStringFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat;
+
+ at JacksonStdImpl
+public class SqlTimeSerializer
+    extends StdScalarSerializer<java.sql.Time>
+{
+    public SqlTimeSerializer() { super(java.sql.Time.class); }
+
+    @Override
+    public void serialize(java.sql.Time value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeString(value.toString());
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        JsonStringFormatVisitor v2 = (visitor == null) ? null : visitor.expectStringFormat(typeHint);
+        if (v2 != null) {
+            v2.format(JsonValueFormat.DATE_TIME);
+        }
+    }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..59676ae
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StaticListSerializerBase.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.lang.reflect.Type;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Intermediate base class for Lists, Collections and Arrays
+ * that contain static (non-dynamic) value types.
+ */
+public abstract class StaticListSerializerBase<T extends Collection<?>>
+    extends StdSerializer<T>
+{
+    protected StaticListSerializerBase(Class<?> cls) {
+        super(cls, false);
+    }
+
+    @Override
+    public boolean isEmpty(T value) {
+        return (value == null) || (value.size() == 0);
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        ObjectNode o = createSchemaNode("array", true);
+        o.put("items", contentSchema());
+        return o;
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        acceptContentVisitor(visitor.expectArrayFormat(typeHint));
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract methods for sub-classes to implement
+    /**********************************************************
+     */
+
+    protected abstract JsonNode contentSchema();
+    
+    protected abstract void acceptContentVisitor(JsonArrayFormatVisitor visitor)
+        throws JsonMappingException;
+}
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
new file mode 100644
index 0000000..7bea5cb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdArraySerializers.java
@@ -0,0 +1,668 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.HashMap;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
+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.type.TypeFactory;
+
+/**
+ * Dummy container class to group standard homogenous array serializer implementations
+ * (primitive arrays and String array).
+ */
+public class StdArraySerializers
+{
+    protected final static HashMap<String, JsonSerializer<?>> _arraySerializers =
+        new HashMap<String, JsonSerializer<?>>();
+    static {
+        // Arrays of various types (including common object types)
+        _arraySerializers.put(boolean[].class.getName(), new StdArraySerializers.BooleanArraySerializer());
+        _arraySerializers.put(byte[].class.getName(), new StdArraySerializers.ByteArraySerializer());
+        _arraySerializers.put(char[].class.getName(), new StdArraySerializers.CharArraySerializer());
+        _arraySerializers.put(short[].class.getName(), new StdArraySerializers.ShortArraySerializer());
+        _arraySerializers.put(int[].class.getName(), new StdArraySerializers.IntArraySerializer());
+        _arraySerializers.put(long[].class.getName(), new StdArraySerializers.LongArraySerializer());
+        _arraySerializers.put(float[].class.getName(), new StdArraySerializers.FloatArraySerializer());
+        _arraySerializers.put(double[].class.getName(), new StdArraySerializers.DoubleArraySerializer());
+    }
+
+    protected StdArraySerializers() { }
+
+    /**
+     * Accessor for checking to see if there is a standard serializer for
+     * given primitive value type.
+     */
+    public static JsonSerializer<?> findStandardImpl(Class<?> cls)
+    {
+        return _arraySerializers.get(cls.getName());
+    }
+    
+    /*
+     ****************************************************************
+    /* Intermediate base classes
+     ****************************************************************
+     */
+
+    /**
+     * Intermediate base class used for cases where we may add
+     * type information (excludes boolean/int/double arrays).
+     */
+    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) {
+            super(src, prop);
+            _valueTypeSerializer = vts;
+        }
+    }
+    
+    /*
+    /****************************************************************
+    /* Concrete serializers, arrays
+    /****************************************************************
+     */
+
+    @JacksonStdImpl
+    public final static class BooleanArraySerializer
+        extends ArraySerializerBase<boolean[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Boolean.class);
+
+        public BooleanArraySerializer() { super(boolean[].class, null); }
+
+        /**
+         * Booleans never add type info; hence, even if type serializer is suggested,
+         * we'll ignore it...
+         */
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return this;
+        }
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(boolean[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(boolean[] value) {
+            return (value.length == 1);
+        }
+        
+        @Override
+        public void serializeContents(boolean[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeBoolean(value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("boolean"));
+            return o;
+        }
+
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.BOOLEAN);
+                }
+            }
+        }
+    }
+
+    /**
+     * Unlike other integral number array serializers, we do not just print out byte values
+     * 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
+     */
+    @JacksonStdImpl
+    public final static class ByteArraySerializer
+        extends StdSerializer<byte[]>
+    {
+        public ByteArraySerializer() {
+            super(byte[].class);
+        }
+        
+        @Override
+        public boolean isEmpty(byte[] value) {
+            return (value == null) || (value.length == 0);
+        }
+        
+        @Override
+        public void serialize(byte[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeBinary(provider.getConfig().getBase64Variant(),
+                    value, 0, value.length);
+        }
+
+        @Override
+        public void serializeWithType(byte[] value, JsonGenerator jgen, SerializerProvider provider,
+                TypeSerializer typeSer)
+            throws IOException, JsonGenerationException
+        {
+            typeSer.writeTypePrefixForScalar(value, jgen);
+            jgen.writeBinary(provider.getConfig().getBase64Variant(),
+                    value, 0, value.length);
+            typeSer.writeTypeSuffixForScalar(value, jgen);
+        }
+        
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            ObjectNode itemSchema = createSchemaNode("string"); //binary values written as strings?
+            o.put("items", itemSchema);
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.STRING);
+                }
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class ShortArraySerializer
+        extends TypedPrimitiveArraySerializer<short[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Short.TYPE);
+
+        public ShortArraySerializer() { super(short[].class); }
+        public ShortArraySerializer(ShortArraySerializer src, BeanProperty prop, TypeSerializer vts) {
+            super(src, prop, vts);
+        }
+
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return new ShortArraySerializer(this, _property, vts);
+        }
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(short[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(short[] value) {
+            return (value.length == 1);
+        }
+        
+        @SuppressWarnings("cast")
+        @Override
+        public void serializeContents(short[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            if (_valueTypeSerializer != null) {
+                for (int i = 0, len = value.length; i < len; ++i) {
+                    _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Short.TYPE);
+                    jgen.writeNumber(value[i]);
+                    _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen);
+                }
+                return;
+            }
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeNumber((int)value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            //no "short" type defined by json
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("integer"));
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.INTEGER);
+                }
+            }
+        }
+    }
+
+    /**
+     * Character arrays are different from other integral number arrays in that
+     * 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
+     */
+    @JacksonStdImpl
+    public final static class CharArraySerializer
+        extends StdSerializer<char[]>
+    {
+        public CharArraySerializer() { super(char[].class); }
+        
+        @Override
+        public boolean isEmpty(char[] value) {
+            return (value == null) || (value.length == 0);
+        }
+        
+        @Override
+        public void serialize(char[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            // [JACKSON-289] allows serializing as 'sparse' char array too:
+            if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
+                jgen.writeStartArray();
+                _writeArrayContents(jgen, value);
+                jgen.writeEndArray();
+            } else {
+                jgen.writeString(value, 0, value.length);
+            }
+        }
+
+        @Override
+        public void serializeWithType(char[] value, JsonGenerator jgen, SerializerProvider provider,
+                TypeSerializer typeSer)
+            throws IOException, JsonGenerationException
+        {
+            // [JACKSON-289] allows serializing as 'sparse' char array too:
+            if (provider.isEnabled(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS)) {
+                typeSer.writeTypePrefixForArray(value, jgen);
+                _writeArrayContents(jgen, value);
+                typeSer.writeTypeSuffixForArray(value, jgen);
+            } else { // default is to write as simple String
+                typeSer.writeTypePrefixForScalar(value, jgen);
+                jgen.writeString(value, 0, value.length);
+                typeSer.writeTypeSuffixForScalar(value, jgen);
+            }
+        }
+
+        private final void _writeArrayContents(JsonGenerator jgen, char[] value)
+            throws IOException, JsonGenerationException
+        {
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeString(value, i, 1);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            ObjectNode itemSchema = createSchemaNode("string");
+            itemSchema.put("type", "string");
+            o.put("items", itemSchema);
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.STRING);
+                }
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class IntArraySerializer
+        extends ArraySerializerBase<int[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Integer.TYPE);
+
+        public IntArraySerializer() { super(int[].class, null); }
+
+        /**
+         * Ints never add type info; hence, even if type serializer is suggested,
+         * we'll ignore it...
+         */
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return this;
+        }        
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(int[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(int[] value) {
+            return (value.length == 1);
+        }
+
+        @Override
+        public void serializeContents(int[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeNumber(value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("integer"));
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.INTEGER);
+                }
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class LongArraySerializer
+        extends TypedPrimitiveArraySerializer<long[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Long.TYPE);
+
+        public LongArraySerializer() { super(long[].class); }
+        public LongArraySerializer(LongArraySerializer src, BeanProperty prop,
+                TypeSerializer vts) {
+            super(src, prop, vts);
+        }
+
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return new LongArraySerializer(this, _property, vts);
+        }
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(long[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(long[] value) {
+            return (value.length == 1);
+        }
+        
+        @Override
+        public void serializeContents(long[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            if (_valueTypeSerializer != null) {
+                for (int i = 0, len = value.length; i < len; ++i) {
+                    _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Long.TYPE);
+                    jgen.writeNumber(value[i]);
+                    _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen);
+                }
+                return;
+            }
+            
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeNumber(value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("number", true));
+            return o;
+        }
+
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.NUMBER);
+                }
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class FloatArraySerializer
+        extends TypedPrimitiveArraySerializer<float[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Float.TYPE);
+        
+        public FloatArraySerializer() {
+            super(float[].class);
+        }
+        public FloatArraySerializer(FloatArraySerializer src, BeanProperty prop,
+                TypeSerializer vts) {
+            super(src, prop, vts);
+        }
+
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return new FloatArraySerializer(this, _property, vts);
+        }
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(float[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(float[] value) {
+            return (value.length == 1);
+        }
+        
+        @Override
+        public void serializeContents(float[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            if (_valueTypeSerializer != null) {
+                for (int i = 0, len = value.length; i < len; ++i) {
+                    _valueTypeSerializer.writeTypePrefixForScalar(null, jgen, Float.TYPE);
+                    jgen.writeNumber(value[i]);
+                    _valueTypeSerializer.writeTypeSuffixForScalar(null, jgen);
+                }
+                return;
+            }
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeNumber(value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("number"));
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.NUMBER);
+                }
+            }
+        }
+    }
+
+    @JacksonStdImpl
+    public final static class DoubleArraySerializer
+        extends ArraySerializerBase<double[]>
+    {
+        // as above, assuming no one re-defines primitive/wrapper types
+        private final static JavaType VALUE_TYPE = TypeFactory.defaultInstance().uncheckedSimpleType(Double.TYPE);
+
+        public DoubleArraySerializer() { super(double[].class, null); }
+
+        /**
+         * Doubles never add type info; hence, even if type serializer is suggested,
+         * we'll ignore it...
+         */
+        @Override
+        public ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer vts) {
+            return this;
+        }
+
+        @Override
+        public JavaType getContentType() {
+            return VALUE_TYPE;
+        }
+
+        @Override
+        public JsonSerializer<?> getContentSerializer() {
+            // 14-Jan-2012, tatu: We could refer to an actual serializer if absolutely necessary
+            return null;
+        }
+        
+        @Override
+        public boolean isEmpty(double[] value) {
+            return (value == null) || (value.length == 0);
+        }
+
+        @Override
+        public boolean hasSingleElement(double[] value) {
+            return (value.length == 1);
+        }
+        
+        @Override
+        public void serializeContents(double[] value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            for (int i = 0, len = value.length; i < len; ++i) {
+                jgen.writeNumber(value[i]);
+            }
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            ObjectNode o = createSchemaNode("array", true);
+            o.put("items", createSchemaNode("number"));
+            return o;
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            if (visitor != null) {
+                JsonArrayFormatVisitor v2 = visitor.expectArrayFormat(typeHint);
+                if (v2 != null) {
+                    v2.itemsFormat(JsonFormatTypes.NUMBER);
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java
new file mode 100644
index 0000000..de37a23
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdContainerSerializers.java
@@ -0,0 +1,81 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContainerSerializer;
+import com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer;
+import com.fasterxml.jackson.databind.ser.impl.IteratorSerializer;
+import com.fasterxml.jackson.databind.ser.std.CollectionSerializer;
+
+/**
+ * Dummy container class to group standard container serializers: serializers
+ * that can serialize things like {@link java.util.List}s,
+ * {@link java.util.Map}s and such.
+ */
+public class StdContainerSerializers
+{
+    protected StdContainerSerializers() { }
+    
+    /**
+     * @since 2.1
+     */
+    public static ContainerSerializer<?> indexedListSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer)
+    {
+        return new IndexedListSerializer(elemType, staticTyping, vts, null, valueSerializer);
+    }
+    
+    /**
+     * @since 2.1
+     */
+    public static ContainerSerializer<?> collectionSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts, JsonSerializer<Object> valueSerializer)
+    {
+        return new CollectionSerializer(elemType, staticTyping, vts, null, valueSerializer);
+    }
+
+    public static ContainerSerializer<?> iteratorSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts)
+    {
+        return new IteratorSerializer(elemType, staticTyping, vts, null);
+    }
+
+    public static ContainerSerializer<?> iterableSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts)
+    {
+        return new IterableSerializer(elemType, staticTyping, vts, null);
+    }
+
+    public static JsonSerializer<?> enumSetSerializer(JavaType enumType)
+    {
+        return new EnumSetSerializer(enumType, null);
+    }
+
+    /*
+    /**********************************************************
+    /* Deprecated methods
+    /**********************************************************
+     */
+
+    /**
+     * @deprecated Since 2.1; use variant that does not take 'property' argument
+     */
+    @Deprecated
+    public static ContainerSerializer<?> indexedListSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts, BeanProperty property,
+            JsonSerializer<Object> valueSerializer)
+    {
+        return indexedListSerializer(elemType, staticTyping, vts, valueSerializer);
+    }
+
+    /**
+     * @deprecated Since 2.1; use variant that does not take 'property' argument
+     */
+    @Deprecated
+    public static ContainerSerializer<?> collectionSerializer(JavaType elemType,
+            boolean staticTyping, TypeSerializer vts, BeanProperty property,
+            JsonSerializer<Object> valueSerializer)
+    {
+        return collectionSerializer(elemType, staticTyping, vts, valueSerializer);
+    }
+}
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
new file mode 100644
index 0000000..5f52e9a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java
@@ -0,0 +1,240 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+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.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
+import com.fasterxml.jackson.databind.util.Converter;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+/**
+ * Serializer implementation where given Java type is first converted
+ * to an intermediate "delegate type" (using a configured
+ * {@link Converter}, and then this delegate value is serialized by Jackson.
+ *<p>
+ * Note that although types may be related, they must not be same; trying
+ * to do this will result in an exception.
+ * 
+ * @since 2.1
+ */
+public class StdDelegatingSerializer
+    extends StdSerializer<Object>
+    implements ContextualSerializer, ResolvableSerializer,
+        JsonFormatVisitable, SchemaAware
+{
+    protected final Converter<Object,?> _converter;
+    
+    /**
+     * Fully resolved delegate type, with generic information if any available.
+     */
+    protected final JavaType _delegateType;
+    
+    /**
+     * Underlying serializer for type <code>T<.code>.
+     */
+    protected final JsonSerializer<Object> _delegateSerializer;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public StdDelegatingSerializer(Converter<?,?> converter)
+    {
+        super(Object.class);
+        _converter = (Converter<Object,?>)converter;
+        _delegateType = null;
+        _delegateSerializer = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> StdDelegatingSerializer(Class<T> cls, Converter<T,?> converter)
+    {
+        super(cls, false);
+        _converter = (Converter<Object,?>)converter;
+        _delegateType = null;
+        _delegateSerializer = null;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public StdDelegatingSerializer(Converter<Object,?> converter,
+            JavaType delegateType, JsonSerializer<?> delegateSerializer)
+    {
+        super(delegateType);
+        _converter = converter;
+        _delegateType = delegateType;
+        _delegateSerializer = (JsonSerializer<Object>) delegateSerializer;
+    }
+
+    /**
+     * Method used for creating resolved contextual instances. Must be
+     * overridden when sub-classing.
+     */
+    protected StdDelegatingSerializer withDelegate(Converter<Object,?> converter,
+            JavaType delegateType, JsonSerializer<?> delegateSerializer)
+    {
+        if (getClass() != StdDelegatingSerializer.class) {
+            throw new IllegalStateException("Sub-class "+getClass().getName()+" must override 'withDelegate'");
+        }
+        return new StdDelegatingSerializer(converter, delegateType, delegateSerializer);
+    }
+    
+    /*
+    /**********************************************************
+    /* Contextualization
+    /**********************************************************
+     */
+
+    @Override
+    public void resolve(SerializerProvider provider) throws JsonMappingException
+    {
+        if ((_delegateSerializer != null)
+                && (_delegateSerializer instanceof ResolvableSerializer)) {
+                ((ResolvableSerializer) _delegateSerializer).resolve(provider);
+        }
+    }
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
+        throws JsonMappingException
+    {
+        // First: if already got serializer to delegate to, contextualize it:
+        if (_delegateSerializer != null) {
+            if (_delegateSerializer instanceof ContextualSerializer) {
+                JsonSerializer<?> ser = ((ContextualSerializer)_delegateSerializer).createContextual(provider, property);
+                if (ser == _delegateSerializer) {
+                    return this;
+                }
+                return withDelegate(_converter, _delegateType, ser);
+            }
+            return this;
+        }
+        // Otherwise, need to locate serializer to delegate to. For that we need type information...
+        JavaType delegateType = _delegateType;
+        if (delegateType == null) {
+            delegateType = _converter.getOutputType(provider.getTypeFactory());
+        }
+        // and then find the thing...
+        return withDelegate(_converter, delegateType,
+                provider.findValueSerializer(delegateType, property));
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+
+    protected Converter<Object, ?> getConverter() {
+        return _converter;
+    }
+
+    @Override
+    public JsonSerializer<?> getDelegatee() {
+        return _delegateSerializer;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        Object delegateValue = convertValue(value);
+        // should we accept nulls?
+        if (delegateValue == null) {
+            provider.defaultSerializeNull(jgen);
+            return;
+        }
+        _delegateSerializer.serialize(delegateValue, jgen, provider);
+    }
+
+    @Override
+    public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        /* 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now,
+         *    let's give it a chance?
+         */
+        Object delegateValue = convertValue(value);
+        _delegateSerializer.serializeWithType(delegateValue, jgen, provider, typeSer);
+    }
+
+    @Override
+    public boolean isEmpty(Object value)
+    {
+        Object delegateValue = convertValue(value);
+        return _delegateSerializer.isEmpty(delegateValue);
+    }
+    
+    /*
+    /**********************************************************
+    /* Schema functionality
+    /**********************************************************
+     */
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        if (_delegateSerializer instanceof SchemaAware) {
+            return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint);
+        }
+        return super.getSchema(provider, typeHint);
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint,
+        boolean isOptional) throws JsonMappingException
+    {
+        if (_delegateSerializer instanceof SchemaAware) {
+            return ((SchemaAware) _delegateSerializer).getSchema(provider, typeHint, isOptional);
+        }
+        return super.getSchema(provider, typeHint);
+    }
+
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        /* 03-Sep-2012, tatu: Not sure if this can be made to really work
+         *    properly... but for now, try this:
+         */
+        _delegateSerializer.acceptJsonFormatVisitor(visitor, typeHint);
+    }
+
+    /*
+    /**********************************************************
+    /* Overridable methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to convert from source Java value into delegate
+     * value (which will be serialized using standard Jackson serializer for delegate type)
+     *<P>
+     * The default implementation uses configured {@link Converter} to do
+     * conversion.
+     * 
+     * @param value Value to convert
+     * 
+     * @return Result of conversion
+     */
+    protected Object convertValue(Object value) {
+        return _converter.convert(value);
+    }
+}
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
new file mode 100644
index 0000000..83bf831
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdJdkSerializers.java
@@ -0,0 +1,241 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.*;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.concurrent.atomic.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonIntegerFormatVisitor;
+import com.fasterxml.jackson.databind.ser.BasicSerializerFactory;
+
+/**
+ * Class that providers access to serializers user for non-structured JDK types that
+ * are serializer as scalars; some using basic {@link ToStringSerializer},
+ * others explicit serializers.
+ */
+public class StdJdkSerializers
+{
+    /**
+     * Method called by {@link BasicSerializerFactory} to access
+     * all serializers this class provides.
+     */
+    public static Collection<Map.Entry<Class<?>, Object>> all()
+    {
+        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, sls);
+        sers.put(java.net.URI.class, sls);
+
+        sers.put(Currency.class, sls);
+        sers.put(UUID.class, sls);
+        sers.put(java.util.regex.Pattern.class, sls);
+        sers.put(Locale.class, sls);
+
+        // starting with 1.7, use compact String for Locale
+        sers.put(Locale.class, sls);
+        
+        // then atomic types
+        sers.put(AtomicReference.class, AtomicReferenceSerializer.class);
+        sers.put(AtomicBoolean.class, AtomicBooleanSerializer.class);
+        sers.put(AtomicInteger.class, AtomicIntegerSerializer.class);
+        sers.put(AtomicLong.class, AtomicLongSerializer.class);
+        
+        // then types that need specialized serializers
+        sers.put(File.class, FileSerializer.class);
+        sers.put(Class.class, ClassSerializer.class);
+
+        // And then some stranger types... not 100% they are needed but:
+        sers.put(Void.TYPE, NullSerializer.class);
+        
+        return sers.entrySet();
+    }
+
+    /*
+    /**********************************************************
+    /* Serializers for atomic types
+    /**********************************************************
+     */
+
+    public final static class AtomicBooleanSerializer
+        extends StdScalarSerializer<AtomicBoolean>
+    {
+        public AtomicBooleanSerializer() { super(AtomicBoolean.class, false); }
+    
+        @Override
+        public void serialize(AtomicBoolean value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeBoolean(value.get());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("boolean", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            visitor.expectBooleanFormat(typeHint);
+        }
+    }
+    
+    public final static class AtomicIntegerSerializer
+        extends StdScalarSerializer<AtomicInteger>
+    {
+        public AtomicIntegerSerializer() { super(AtomicInteger.class, false); }
+    
+        @Override
+        public void serialize(AtomicInteger value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.get());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("integer", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.INT);
+            }
+        }
+    }
+
+    public final static class AtomicLongSerializer
+        extends StdScalarSerializer<AtomicLong>
+    {
+        public AtomicLongSerializer() { super(AtomicLong.class, false); }
+    
+        @Override
+        public void serialize(AtomicLong value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeNumber(value.get());
+        }
+    
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("integer", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            JsonIntegerFormatVisitor v2 = visitor.expectIntegerFormat(typeHint);
+            if (v2 != null) {
+                v2.numberType(JsonParser.NumberType.LONG);
+            }
+        }
+    }
+    
+    public final static class AtomicReferenceSerializer
+        extends StdSerializer<AtomicReference<?>>
+    {
+        public AtomicReferenceSerializer() { super(AtomicReference.class, false); }
+
+        @Override
+        public void serialize(AtomicReference<?> value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            provider.defaultSerializeValue(value.get(), jgen);
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("any", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            visitor.expectAnyFormat(typeHint);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Specialized serializers, referential types
+    /**********************************************************
+     */
+
+    /**
+     * For now, File objects get serialized by just outputting
+     * absolute (but not canonical) name as String value
+     */
+    public final static class FileSerializer
+        extends StdScalarSerializer<File>
+    {
+        public FileSerializer() { super(File.class); }
+
+        @Override
+        public void serialize(File value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeString(value.getAbsolutePath());
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("string", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            visitor.expectStringFormat(typeHint);
+        }
+    }
+
+    /**
+     * Also: default bean access will not do much good with Class.class. But
+     * we can just serialize the class name and that should be enough.
+     */
+    public final static class ClassSerializer
+        extends StdScalarSerializer<Class<?>>
+    {
+        public ClassSerializer() { super(Class.class, false); }
+
+        @Override
+        public void serialize(Class<?> value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeString(value.getName());
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        {
+            return createSchemaNode("string", true);
+        }
+        
+        @Override
+        public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+                throws JsonMappingException
+        {
+            visitor.expectStringFormat(typeHint);
+        }
+    }
+}
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
new file mode 100644
index 0000000..03c4f3d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializer.java
@@ -0,0 +1,51 @@
+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.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+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.
+ */
+public class StdKeySerializer
+    extends StdSerializer<Object>
+{
+    final static StdKeySerializer instace = new StdKeySerializer();
+
+    public StdKeySerializer() { super(Object.class); }
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (value instanceof Date) {
+            provider.defaultSerializeDateKey((Date) value, jgen);
+        } else {
+            jgen.writeFieldName(value.toString());
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return createSchemaNode("string");
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        visitor.expectStringFormat(typeHint);
+    }
+}
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
new file mode 100644
index 0000000..fe73e80
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdKeySerializers.java
@@ -0,0 +1,95 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Date;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public class StdKeySerializers
+{
+    protected final static JsonSerializer<Object> DEFAULT_KEY_SERIALIZER = new StdKeySerializer();
+
+    @SuppressWarnings("unchecked")
+    protected final static JsonSerializer<Object> DEFAULT_STRING_SERIALIZER
+        = (JsonSerializer<Object>)(JsonSerializer<?>) new StringKeySerializer();
+    
+    private StdKeySerializers() { }
+
+    @SuppressWarnings("unchecked")
+    public static JsonSerializer<Object> getStdKeySerializer(JavaType keyType)
+    {
+        if (keyType == null) {
+            return DEFAULT_KEY_SERIALIZER;
+        }
+        Class<?> cls = keyType.getRawClass();
+        if (cls == String.class) {
+            return DEFAULT_STRING_SERIALIZER;
+        }
+        if (cls == Object.class) {
+            return DEFAULT_KEY_SERIALIZER;
+        }
+        // [JACKSON-606] special handling for dates...
+        if (Date.class.isAssignableFrom(cls)) {
+            return (JsonSerializer<Object>) DateKeySerializer.instance;
+        }
+        if (Calendar.class.isAssignableFrom(cls)) {
+            return (JsonSerializer<Object>) CalendarKeySerializer.instance;
+        }
+        // If no match, just use default one:
+        return DEFAULT_KEY_SERIALIZER;
+    }
+
+    /*
+    /**********************************************************
+    /* Standard implementations
+    /**********************************************************
+     */
+
+    public static class StringKeySerializer
+        extends StdSerializer<String>
+    {
+        public StringKeySerializer() { super(String.class); }
+        
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeFieldName(value);
+        }
+    }
+
+    public static class DateKeySerializer
+        extends StdSerializer<Date>
+    {
+        protected final static JsonSerializer<?> instance = new DateKeySerializer();
+
+        public DateKeySerializer() { super(Date.class); }
+        
+        @Override
+        public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            provider.defaultSerializeDateKey(value, jgen);
+        }
+    }
+
+    public static class CalendarKeySerializer
+        extends StdSerializer<Calendar>
+    {
+        protected final static JsonSerializer<?> instance = new CalendarKeySerializer();
+
+        public CalendarKeySerializer() { super(Calendar.class); }
+        
+        @Override
+        public void serialize(Calendar value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            provider.defaultSerializeDateKey(value.getTimeInMillis(), jgen);
+        }
+    }
+}
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
new file mode 100644
index 0000000..29b77ad
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdScalarSerializer.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+public abstract class StdScalarSerializer<T>
+    extends StdSerializer<T>
+{
+    protected StdScalarSerializer(Class<T> t) {
+        super(t);
+    }
+
+    /**
+     * Alternate constructor that is (alas!) needed to work
+     * around kinks of generic type handling
+     */
+    @SuppressWarnings("unchecked")
+    protected StdScalarSerializer(Class<?> t, boolean dummy) {
+        super((Class<T>) t);
+    }
+    
+    /**
+     * Default implementation will write type prefix, call regular serialization
+     * method (since assumption is that value itself does not need JSON
+     * Array or Object start/end markers), and then write type suffix.
+     * This should work for most cases; some sub-classes may want to
+     * change this behavior.
+     */
+    @Override
+    public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForScalar(value, jgen);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        visitor.expectAnyFormat(typeHint);
+    }
+}
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
new file mode 100644
index 0000000..fc46061
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StdSerializer.java
@@ -0,0 +1,257 @@
+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.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsonschema.SchemaAware;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.util.Converter;
+
+/**
+ * Base class used by all standard serializers, and can also
+ * be used for custom serializers (in fact, this is the recommended
+ * base class to use).
+ * Provides convenience methods for implementing {@link SchemaAware}
+ */
+public abstract class StdSerializer<T>
+    extends JsonSerializer<T>
+    implements JsonFormatVisitable, SchemaAware
+{
+    /**
+     * Nominal type supported, usually declared type of
+     * property for which serializer is used.
+     */
+    protected final Class<T> _handledType;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected StdSerializer(Class<T> t) {
+        _handledType = t;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected StdSerializer(JavaType type) {
+        _handledType = (Class<T>) type.getRawClass();
+    }
+    
+    /**
+     * Alternate constructor that is (alas!) needed to work
+     * around kinks of generic type handling
+     */
+    @SuppressWarnings("unchecked")
+    protected StdSerializer(Class<?> t, boolean dummy) {
+        _handledType = (Class<T>) t;
+    }
+    /*
+    /**********************************************************
+    /* Accessors
+    /**********************************************************
+     */
+    
+    @Override
+    public Class<T> handledType() { return _handledType; }
+
+    /*
+    /**********************************************************
+    /* Serialization
+    /**********************************************************
+     */
+    
+    @Override
+    public abstract void serialize(T value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException;
+
+    /*
+    /**********************************************************
+    /* Helper methods for JSON Schema generation
+    /**********************************************************
+     */
+    
+    /**
+     * Default implementation simply claims type is "string"; usually
+     * overriden by custom serializers.
+     */
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return createSchemaNode("string");
+    }
+    
+    /**
+     * Default implementation simply claims type is "string"; usually
+     * overriden by custom serializers.
+     */
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint, boolean isOptional)
+        throws JsonMappingException
+    {
+    	ObjectNode schema = (ObjectNode) getSchema(provider, typeHint);
+    	if (!isOptional) {
+    		schema.put("required", !isOptional);
+    	}
+        return schema;
+    }
+    
+    protected ObjectNode createObjectNode() {
+        return JsonNodeFactory.instance.objectNode();
+    }
+    
+    protected ObjectNode createSchemaNode(String type)
+    {
+        ObjectNode schema = createObjectNode();
+        schema.put("type", type);
+        return schema;
+    }
+    
+    protected ObjectNode createSchemaNode(String type, boolean isOptional)
+    {
+        ObjectNode schema = createSchemaNode(type);
+        // as per [JACKSON-563]. Note that 'required' defaults to false
+        if (!isOptional) {
+            schema.put("required", !isOptional);
+        }
+        return schema;
+    }
+    
+    /**
+     * Default implementation specifies no format. This behavior is usually
+     * overriden by custom serializers.
+     */
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        visitor.expectAnyFormat(typeHint);
+    }
+            
+    /*
+    /**********************************************************
+    /* Helper methods for exception handling
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will modify caught exception (passed in as argument)
+     * as necessary to include reference information, and to ensure it
+     * is a subtype of {@link IOException}, or an unchecked exception.
+     *<p>
+     * Rules for wrapping and unwrapping are bit complicated; essentially:
+     *<ul>
+     * <li>Errors are to be passed as is (if uncovered via unwrapping)
+     * <li>"Plain" IOExceptions (ones that are not of type
+     *   {@link JsonMappingException} are to be passed as is
+     *</ul>
+     */
+    public void wrapAndThrow(SerializerProvider provider,
+            Throwable t, Object bean, String fieldName)
+        throws IOException
+    {
+        /* 05-Mar-2009, tatu: But one nasty edge is when we get
+         *   StackOverflow: usually due to infinite loop. But that
+         *   usually gets hidden within an InvocationTargetException...
+         */
+        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;
+        }
+        // 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;
+            }
+        }
+        // [JACKSON-55] Need to add reference information
+        throw JsonMappingException.wrapWithPath(t, bean, fieldName);
+    }
+
+    public void wrapAndThrow(SerializerProvider provider,
+            Throwable t, Object bean, int index)
+        throws IOException
+    {
+        while (t instanceof InvocationTargetException && t.getCause() != null) {
+            t = t.getCause();
+        }
+        // Errors are to be passed as is
+        if (t instanceof Error) {
+            throw (Error) 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;
+            }
+        }
+        // [JACKSON-55] Need to add reference information
+        throw JsonMappingException.wrapWithPath(t, bean, index);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods, other
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to determine if given serializer is the default
+     * serializer Jackson uses; as opposed to a custom serializer installed by
+     * a module or calling application. Determination is done using
+     * {@link JacksonStdImpl} annotation on serializer class.
+     */
+    protected boolean isDefaultSerializer(JsonSerializer<?> serializer) {
+        return (serializer != null && serializer.getClass().getAnnotation(JacksonStdImpl.class) != null);
+    }
+
+    /**
+     * 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
+     * of structured types; array/List/Map values)
+     * 
+     * @param existingSerializer (optional) configured content
+     *    serializer if one already exists.
+     * 
+     * @since 2.2
+     */
+    protected JsonSerializer<?> findConvertingContentSerializer(SerializerProvider provider,
+            BeanProperty prop, JsonSerializer<?> existingSerializer)
+        throws JsonMappingException
+    {
+        final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
+        if (intr != null && prop != null) {
+            Object convDef = intr.findSerializationContentConverter(prop.getMember());
+            if (convDef != null) {
+                Converter<Object,Object> conv = provider.converterInstance(prop.getMember(), convDef);
+                JavaType delegateType = conv.getOutputType(provider.getTypeFactory());
+                if (existingSerializer == null) {
+                    existingSerializer = provider.findValueSerializer(delegateType, prop);
+                }
+                return new StdDelegatingSerializer(conv, delegateType, existingSerializer);
+            }
+        }
+        return existingSerializer;
+    }
+}
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
new file mode 100644
index 0000000..03fe317
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/StringSerializer.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+
+/**
+ * This is the special serializer for regular {@link java.lang.String}s.
+ *<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)
+ */
+ at JacksonStdImpl
+public final class StringSerializer
+    extends NonTypedScalarSerializerBase<String>
+{
+    public StringSerializer() { super(String.class); }
+
+    /**
+     * For Strings, both null and Empty String qualify for emptiness.
+     */
+    @Override
+    public boolean isEmpty(String value) {
+        return (value == null) || (value.length() == 0);
+    }
+    
+    @Override
+    public void serialize(String value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeString(value);
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        if (visitor != null) visitor.expectStringFormat(typeHint);
+    }
+}
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
new file mode 100644
index 0000000..71f2bf5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TimeZoneSerializer.java
@@ -0,0 +1,35 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.util.TimeZone;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+public class TimeZoneSerializer
+    extends StdScalarSerializer<TimeZone>
+{
+    public final static TimeZoneSerializer instance = new TimeZoneSerializer();
+    
+    public TimeZoneSerializer() { super(TimeZone.class); }
+
+    @Override
+    public void serialize(TimeZone value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeString(value.getID());
+    }
+
+    @Override
+    public void serializeWithType(TimeZone value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        // Better ensure we don't use specific sub-classes:
+        typeSer.writeTypePrefixForScalar(value, jgen, TimeZone.class);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+}
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
new file mode 100644
index 0000000..f7a54a2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/ToStringSerializer.java
@@ -0,0 +1,93 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Simple general purpose serializer, useful for any
+ * type for which {@link Object#toString} returns the desired JSON
+ * value.
+ */
+ at JacksonStdImpl
+public class ToStringSerializer
+    extends StdSerializer<Object>
+{
+    /**
+     * Singleton instance to use.
+     */
+    public final static ToStringSerializer instance = new ToStringSerializer();
+
+    /**
+     *<p>
+     * Note: usually you should NOT create new instances, but instead use
+     * {@link #instance} which is stateless and fully thread-safe. However,
+     * there are cases where constructor is needed; for example,
+     * when using explicit serializer annotations like
+     * {@link com.fasterxml.jackson.databind.annotation.JsonSerialize#using}.
+     */
+    public ToStringSerializer() { super(Object.class); }
+
+    @Override
+    public boolean isEmpty(Object value) {
+        if (value == null) {
+            return true;
+        }
+        String str = value.toString();
+        // would use String.isEmpty(), but that's JDK 1.6
+        return (str == null) || (str.length() == 0);
+    }
+    
+    @Override
+    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeString(value.toString());
+    }
+
+    /* 01-Mar-2011, tatu: We were serializing as "raw" String; but generally that
+     *   is not what we want, since lack of type information would imply real
+     *   String type.
+     */
+    /**
+     * Default implementation will write type prefix, call regular serialization
+     * method (since assumption is that value itself does not need JSON
+     * Array or Object start/end markers), and then write type suffix.
+     * This should work for most cases; some sub-classes may want to
+     * change this behavior.
+     */
+    @Override
+    public void serializeWithType(Object value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForScalar(value, jgen);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+        throws JsonMappingException
+    {
+        return createSchemaNode("string", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+            throws JsonMappingException
+    {
+        if (visitor != null) {
+            visitor.expectStringFormat(typeHint);
+        }
+    }
+}
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
new file mode 100644
index 0000000..0871b5c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/TokenBufferSerializer.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.ser.std;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+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.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.util.TokenBuffer;
+
+/**
+ * We also want to directly support serialization of {@link TokenBuffer};
+ * and since it is part of core package, it can not implement
+ * {@link com.fasterxml.jackson.databind.JsonSerializable}
+ * (which is only included in the mapper package)
+ */
+ at JacksonStdImpl
+public class TokenBufferSerializer
+    extends StdSerializer<TokenBuffer>
+{
+    public TokenBufferSerializer() { super(TokenBuffer.class); }
+
+    @Override
+    public void serialize(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        value.serialize(jgen);
+    }
+
+    /**
+     * Implementing typed output for contents of a TokenBuffer is very tricky,
+     * since we do not know for sure what its contents might look like (or, rather,
+     * we do know when serializing, but not necessarily when deserializing!)
+     * One possibility would be to check the current token, and use that to
+     * determine if we would output JSON Array, Object or scalar value.
+     * Jackson 1.5 did NOT include any type information; but this seems wrong,
+     * and so 1.6 WILL include type information.
+     *<p>
+     * Note that we just claim it is scalar; this should work ok and is simpler
+     * than doing introspection on both serialization and deserialization.
+     */
+    @Override
+    public final void serializeWithType(TokenBuffer value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForScalar(value, jgen);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+    
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, Type typeHint)
+    {
+        /* 01-Jan-2010, tatu: Not 100% sure what we should say here:
+         *   type is basically not known. This seems closest
+         *   approximation
+         */
+        return createSchemaNode("any", true);
+    }
+    
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint)
+        throws JsonMappingException
+    {
+        /* 01-Jan-2010, tatu: Not 100% sure what we should say here:
+         *   type is basically not known. This seems closest
+         *   approximation
+         */
+        visitor.expectAnyFormat(typeHint);
+    }
+}    
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
new file mode 100644
index 0000000..673e704
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ArrayType.java
@@ -0,0 +1,257 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.lang.reflect.Array;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Array types represent Java arrays, both primitive and object valued.
+ * Further, Object-valued arrays can have element type of any other
+ * legal {@link JavaType}.
+ */
+public final class ArrayType
+    extends TypeBase
+{
+    private static final long serialVersionUID = 9040058063449087477L;
+
+    /**
+     * Type of elements in the array.
+     */
+    protected final JavaType _componentType;
+
+    /**
+     * We will also keep track of shareable instance of empty array,
+     * since it usually needs to be constructed any way; and because
+     * it is essentially immutable and thus can be shared.
+     */
+    protected final Object _emptyArray;
+    
+    private ArrayType(JavaType componentType, Object emptyInstance,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(emptyInstance.getClass(), componentType.hashCode(),
+                valueHandler, typeHandler, asStatic);
+        _componentType = componentType;
+        _emptyArray = emptyInstance;
+    }
+
+    public static ArrayType construct(JavaType componentType,
+            Object valueHandler, Object typeHandler)
+    {
+        /* This is bit messy: there is apparently no other way to
+         * reconstruct actual concrete/raw array class from component
+         * type, than to construct an instance, get class (same is
+         * true for GenericArracyType as well; hence we won't bother
+         * passing that in).
+         */
+        Object emptyInstance = Array.newInstance(componentType.getRawClass(), 0);
+        return new ArrayType(componentType, emptyInstance, null, null, false);
+    }                                   
+    
+    @Override
+    public ArrayType withTypeHandler(Object h)
+    {
+        if (h == _typeHandler) {
+            return this;
+        }
+        return new ArrayType(_componentType, _emptyArray, _valueHandler, h, _asStatic);
+    }
+
+    @Override
+    public ArrayType withContentTypeHandler(Object h)
+    {
+        if (h == _componentType.<Object>getTypeHandler()) {
+            return this;
+        }
+        return new ArrayType(_componentType.withTypeHandler(h), _emptyArray,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public ArrayType withValueHandler(Object h) {
+        if (h == _valueHandler) {
+            return this;
+        }
+        return new ArrayType(_componentType, _emptyArray, h, _typeHandler,_asStatic);
+    }
+
+    @Override
+    public ArrayType withContentValueHandler(Object h) {
+        if (h == _componentType.<Object>getValueHandler()) {
+            return this;
+        }
+        return new ArrayType(_componentType.withValueHandler(h), _emptyArray,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public ArrayType withStaticTyping() {
+        if (_asStatic) {
+            return this;
+        }
+        return new ArrayType(_componentType.withStaticTyping(),
+                _emptyArray, _valueHandler, _typeHandler, true);
+    }
+
+    @Override
+    protected String buildCanonicalName() {
+        return _class.getName();
+    }
+
+    /*
+    /**********************************************************
+    /* Methods for narrowing conversions
+    /**********************************************************
+     */
+
+    /**
+     * Handling of narrowing conversions for arrays is trickier: for now,
+     * it is not even allowed.
+     */
+    @Override
+    protected JavaType _narrow(Class<?> subclass)
+    {
+        /* Ok: need a bit of indirection here. First, must replace component
+         * type (and check that it is compatible), then re-construct.
+         */
+        if (!subclass.isArray()) { // sanity check, should never occur
+            throw new IllegalArgumentException("Incompatible narrowing operation: trying to narrow "+toString()+" to class "+subclass.getName());
+        }
+        /* Hmmh. This is an awkward back reference... but seems like the
+         * only simple way to do it.
+         */
+        Class<?> newCompClass = subclass.getComponentType();
+        /* 14-Mar-2011, tatu: it gets even worse, as we do not have access to
+         *   currently configured TypeFactory. This could theoretically cause
+         *   problems (when narrowing from array of Objects, to array of non-standard
+         *   Maps, for example); but for now need to defer solving this until
+         *   it actually becomes a real problem, not just potential one.
+         *   (famous last words?)
+         */
+        JavaType newCompType = TypeFactory.defaultInstance().constructType(newCompClass);
+        return construct(newCompType, _valueHandler, _typeHandler);
+    }
+
+    /**
+     * For array types, both main type and content type can be modified;
+     * but ultimately they are interchangeable.
+     */
+    @Override
+    public JavaType narrowContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _componentType.getRawClass()) {
+            return this;
+        }
+        return construct(_componentType.narrowBy(contentClass),
+                _valueHandler, _typeHandler);
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _componentType.getRawClass()) {
+            return this;
+        }
+        return construct(_componentType.widenBy(contentClass),
+                _valueHandler, _typeHandler);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isArrayType() { return true; }
+    
+    /**
+     * For some odd reason, modifiers for array classes would
+     * claim they are abstract types. Not so, at least for our
+     * purposes.
+     */
+    @Override
+    public boolean isAbstract() { return false; }
+
+    /**
+     * For some odd reason, modifiers for array classes would
+     * claim they are abstract types. Not so, at least for our
+     * purposes.
+     */
+    @Override
+    public boolean isConcrete() { return true; }
+
+    @Override
+    public boolean hasGenericTypes() {
+        // arrays are not parameterized, but element type may be:
+        return _componentType.hasGenericTypes();
+    }
+    
+    /**
+     * Not sure what symbolic name is used internally, if any;
+     * let's follow naming of Collection types here.
+     * Should not really matter since array types have no
+     * super types.
+     */
+    @Override
+    public String containedTypeName(int index) {
+        if (index == 0) return "E";
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isContainerType() { return true; }
+
+    @Override
+    public JavaType getContentType() { return  _componentType; }
+
+    @Override
+    public int containedTypeCount() { return 1; }
+    @Override
+    public JavaType containedType(int index) {
+            return (index == 0) ? _componentType : null;
+    }
+    
+    @Override
+    public StringBuilder getGenericSignature(StringBuilder sb) {
+        sb.append('[');
+        return _componentType.getGenericSignature(sb);
+    }
+
+    @Override
+    public StringBuilder getErasedSignature(StringBuilder sb) {
+        sb.append('[');
+        return _componentType.getErasedSignature(sb);
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[array type, component type: "+_componentType+"]";
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+
+        ArrayType other = (ArrayType) o;
+        return _componentType.equals(other._componentType);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/ClassKey.java b/src/main/java/com/fasterxml/jackson/databind/type/ClassKey.java
new file mode 100644
index 0000000..dd05ef6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/ClassKey.java
@@ -0,0 +1,97 @@
+package com.fasterxml.jackson.databind.type;
+
+/**
+ * Key class, used as an efficient and accurate key
+ * for locating per-class values, such as
+ * {@link com.fasterxml.jackson.databind.JsonSerializer}s.
+ *<p>
+ * The reason for having a separate key class instead of
+ * directly using {@link Class} as key is mostly
+ * to allow for redefining <code>hashCode</code> method --
+ * for some strange reason, {@link Class} does not
+ * redefine {@link Object#hashCode} and thus uses identity
+ * hash, which is pretty slow. This makes key access using
+ * {@link Class} unnecessarily slow.
+ *<p>
+ * Note: since class is not strictly immutable, caller must
+ * know what it is doing, if changing field values.
+ */
+public final class ClassKey
+    implements Comparable<ClassKey>,
+        java.io.Serializable // since 2.1
+{
+    private static final long serialVersionUID = 1L;
+
+    private String _className;
+
+    private Class<?> _class;
+
+    /**
+     * Let's cache hash code straight away, since we are
+     * almost certain to need it.
+     */
+    private int _hashCode;
+
+    public ClassKey() 
+    {
+        _class = null;
+        _className = null;
+        _hashCode = 0;
+    }
+
+    public ClassKey(Class<?> clz)
+    {
+        _class = clz;
+        _className = clz.getName();
+        _hashCode = _className.hashCode();
+    }
+
+    public void reset(Class<?> clz)
+    {
+        _class = clz;
+        _className = clz.getName();
+        _hashCode = _className.hashCode();
+    }
+
+    /*
+    /**********************************************************
+    /* Comparable
+    /**********************************************************
+     */
+
+    @Override
+    public int compareTo(ClassKey other)
+    {
+        // Just need to sort by name, ok to collide (unless used in TreeMap/Set!)
+        return _className.compareTo(other._className);
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+        public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+        ClassKey other = (ClassKey) o;
+
+        /* Is it possible to have different Class object for same name + class loader combo?
+         * Let's assume answer is no: if this is wrong, will need to uncomment following functionality
+         */
+        /*
+        return (other._className.equals(_className))
+            && (other._class.getClassLoader() == _class.getClassLoader());
+        */
+        return other._class == _class;
+    }
+
+    @Override public int hashCode() { return _hashCode; }
+
+    @Override public String toString() { return _className; }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
new file mode 100644
index 0000000..963b082
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionLikeType.java
@@ -0,0 +1,201 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.Collection;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Type that represents things that act similar to {@link java.util.Collection};
+ * but may or may not be instances of that interface.
+ * This specifically allows framework to check for configuration and annotation
+ * settings used for Map types, and pass these to custom handlers that may be more
+ * familiar with actual type.
+ */
+public class CollectionLikeType extends TypeBase
+{
+    private static final long serialVersionUID = 4611641304150899138L;
+
+    /**
+     * Type of elements in collection
+     */
+    protected final JavaType _elementType;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected CollectionLikeType(Class<?> collT, JavaType elemT,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(collT, elemT.hashCode(), valueHandler, typeHandler, asStatic);
+        _elementType = elemT;
+    }
+    
+    @Override
+    protected JavaType _narrow(Class<?> subclass) {
+        return new CollectionLikeType(subclass, _elementType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType narrowContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _elementType.getRawClass()) {
+            return this;
+        }
+        return new CollectionLikeType(_class, _elementType.narrowBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _elementType.getRawClass()) {
+            return this;
+        }
+        return new CollectionLikeType(_class, _elementType.widenBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    public static CollectionLikeType construct(Class<?> rawType, JavaType elemT)
+    {
+        // nominally component types will be just Object.class
+        return new CollectionLikeType(rawType, elemT, null, null, false);
+    }
+
+    @Override
+    public CollectionLikeType withTypeHandler(Object h)
+    {
+        return new CollectionLikeType(_class, _elementType, _valueHandler, h, _asStatic);
+    }
+
+    @Override
+    public CollectionLikeType withContentTypeHandler(Object h)
+    {
+        return new CollectionLikeType(_class, _elementType.withTypeHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public CollectionLikeType withValueHandler(Object h) {
+        return new CollectionLikeType(_class, _elementType, h, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public CollectionLikeType withContentValueHandler(Object h) {
+        return new CollectionLikeType(_class, _elementType.withValueHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public CollectionLikeType withStaticTyping() {
+        if (_asStatic) {
+            return this;
+        }
+        return new CollectionLikeType(_class, _elementType.withStaticTyping(),
+                _valueHandler, _typeHandler, true);
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isContainerType() { return true; }
+
+    @Override
+    public boolean isCollectionLikeType() { return true; }
+    
+    @Override
+    public JavaType getContentType() { return _elementType; }
+
+    @Override
+    public int containedTypeCount() { return 1; }
+
+    @Override
+    public JavaType containedType(int index) {
+            return (index == 0) ? _elementType : null;
+    }
+
+    /**
+     * Not sure if we should count on this, but type names
+     * for core interfaces use "E" for element type
+     */
+    @Override
+    public String containedTypeName(int index) {
+        if (index == 0) return "E";
+        return null;
+    }
+
+    @Override
+    public StringBuilder getErasedSignature(StringBuilder sb) {
+        return _classSignature(_class, sb, true);
+    }
+    
+    @Override
+    public StringBuilder getGenericSignature(StringBuilder sb) {
+        _classSignature(_class, sb, false);
+        sb.append('<');
+        _elementType.getGenericSignature(sb);
+        sb.append(">;");
+        return sb;
+    }
+    
+    @Override
+    protected String buildCanonicalName() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(_class.getName());
+        if (_elementType != null) {
+            sb.append('<');
+            sb.append(_elementType.toCanonical());
+            sb.append('>');
+        }
+        return sb.toString();
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used for checking whether this type is a
+     * "real" Collection type; meaning whether it represents a parameterized
+     * subtype of {@link java.util.Collection} or just something that acts
+     * like one.
+     */
+    public boolean isTrueCollectionType() {
+        return Collection.class.isAssignableFrom(_class);
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+
+        CollectionLikeType other = (CollectionLikeType) o;
+        return  (_class == other._class) && _elementType.equals(other._elementType);
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[collection-like type; class "+_class.getName()+", contains "+_elementType+"]";
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
new file mode 100644
index 0000000..530a1e2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/CollectionType.java
@@ -0,0 +1,103 @@
+package com.fasterxml.jackson.databind.type;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Type that represents Java Collection types (Lists, Sets).
+ */
+public final class CollectionType
+    extends CollectionLikeType
+{
+    private static final long serialVersionUID = -7834910259750909424L;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    private CollectionType(Class<?> collT, JavaType elemT,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(collT,  elemT, valueHandler, typeHandler, asStatic);
+    }
+
+    @Override
+    protected JavaType _narrow(Class<?> subclass) {
+        return new CollectionType(subclass, _elementType, null, null, _asStatic);
+    }
+
+    @Override
+    public JavaType narrowContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _elementType.getRawClass()) {
+            return this;
+        }
+        return new CollectionType(_class, _elementType.narrowBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _elementType.getRawClass()) {
+            return this;
+        }
+        return new CollectionType(_class, _elementType.widenBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    public static CollectionType construct(Class<?> rawType, JavaType elemT)
+    {
+        // nominally component types will be just Object.class
+        return new CollectionType(rawType, elemT, null, null, false);
+    }
+
+    // Since 1.7:
+    @Override
+    public CollectionType withTypeHandler(Object h) {
+        return new CollectionType(_class, _elementType, _valueHandler, h, _asStatic);
+    }
+
+    // Since 1.7:
+    @Override
+    public CollectionType withContentTypeHandler(Object h)
+    {
+        return new CollectionType(_class, _elementType.withTypeHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public CollectionType withValueHandler(Object h) {
+        return new CollectionType(_class, _elementType, h, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public  CollectionType withContentValueHandler(Object h) {
+        return new CollectionType(_class, _elementType.withValueHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public CollectionType withStaticTyping() {
+        if (_asStatic) {
+            return this;
+        }
+        return new CollectionType(_class, _elementType.withStaticTyping(),
+                _valueHandler, _typeHandler, true);
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[collection type; class "+_class.getName()+", contains "+_elementType+"]";
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/HierarchicType.java b/src/main/java/com/fasterxml/jackson/databind/type/HierarchicType.java
new file mode 100644
index 0000000..56518fe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/HierarchicType.java
@@ -0,0 +1,84 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.lang.reflect.*;
+
+/**
+ * Simple replacement for {@link java.lang.Class} (and/or various Type subtypes)
+ * that is used as part of single-path extends/implements chain to express
+ * specific relationship between one subtype and one supertype. This is needed
+ * for resolving type parameters. Instances are doubly-linked so that chain
+ * can be traversed in both directions
+ */
+public class HierarchicType
+{
+    /**
+     * Type which will be either plain {@link java.lang.Class} or
+     * {@link java.lang.reflect.ParameterizedType}.
+     */
+    protected final Type _actualType;
+    
+    protected final Class<?> _rawClass;
+
+    protected final ParameterizedType _genericType;
+    
+    protected HierarchicType _superType;
+
+    protected HierarchicType _subType;
+    
+    public HierarchicType(Type type)
+    {
+        this._actualType = type;
+        if (type instanceof Class<?>) {
+            _rawClass = (Class<?>) type;
+            _genericType = null;
+        } else if (type instanceof ParameterizedType) {
+            _genericType = (ParameterizedType) type;
+            _rawClass = (Class<?>) _genericType.getRawType();
+        } else { // should never happen... can't extend GenericArrayType?
+            throw new IllegalArgumentException("Type "+type.getClass().getName()+" can not be used to construct HierarchicType");
+        }
+    }
+
+    private HierarchicType(Type actualType, Class<?> rawClass, ParameterizedType genericType,
+        HierarchicType superType, HierarchicType subType)
+    {
+        _actualType = actualType;
+        _rawClass = rawClass;
+        _genericType = genericType;
+        _superType = superType;
+        _subType = subType;
+    }
+    
+    /**
+     * Method that can be used to create a deep clone of this hierarchic type, including
+     * super types (but not subtypes)
+     */
+    public HierarchicType deepCloneWithoutSubtype()
+    {
+        HierarchicType sup = (_superType == null) ? null : _superType.deepCloneWithoutSubtype();
+        HierarchicType result = new HierarchicType(_actualType, _rawClass, _genericType, sup, null);
+        if (sup != null) {
+            sup.setSubType(result);
+        }
+        return result;
+    }
+    
+    public void setSuperType(HierarchicType sup) { _superType = sup; }
+    public final HierarchicType getSuperType() { return _superType; }
+    public void setSubType(HierarchicType sub) { _subType = sub; }
+    public final HierarchicType getSubType() { return _subType; }
+    
+    public final boolean isGeneric() { return _genericType != null; }
+    public final ParameterizedType asGeneric() { return _genericType; }
+
+    public final Class<?> getRawClass() { return _rawClass; }
+    
+    @Override
+    public String toString() {
+        if (_genericType != null) {
+            return _genericType.toString();
+        }
+        return _rawClass.getName();
+    }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
new file mode 100644
index 0000000..fc39074
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapLikeType.java
@@ -0,0 +1,249 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Type that represents Map-like types; things that consist of key/value pairs but that
+ * do not necessarily implement {@link java.util.Map}, but that do not have enough
+ * introspection functionality to allow for some level of generic handling.
+ * This specifically allows framework to check for configuration and annotation
+ * settings used for Map types, and pass these to custom handlers that may be more
+ * familiar with actual type.
+ */
+public class MapLikeType extends TypeBase
+{
+    private static final long serialVersionUID = 416067702302823522L;
+
+    /**
+     * Type of keys of Map.
+     */
+    protected final JavaType _keyType;
+
+    /**
+     * Type of values of Map.
+     */
+    protected final JavaType _valueType;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected MapLikeType(Class<?> mapType, JavaType keyT, JavaType valueT,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(mapType, keyT.hashCode() ^ valueT.hashCode(), valueHandler, typeHandler, asStatic);
+        _keyType = keyT;
+        _valueType = valueT;
+    }
+    
+    public static MapLikeType construct(Class<?> rawType, JavaType keyT, JavaType valueT)
+    {
+        // nominally component types will be just Object.class
+        return new MapLikeType(rawType, keyT, valueT, null, null, false);
+    }
+
+    @Override
+    protected JavaType _narrow(Class<?> subclass) {
+        return new MapLikeType(subclass, _keyType, _valueType, _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType narrowContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _valueType.getRawClass()) {
+            return this;
+        }
+        return new MapLikeType(_class, _keyType, _valueType.narrowBy(contentClass),
+               _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> contentClass)
+    {
+        if (contentClass == _valueType.getRawClass()) {
+            return this;
+        }
+        return new MapLikeType(_class, _keyType, _valueType.widenBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    public JavaType narrowKey(Class<?> keySubclass)
+    {
+        // Can do a quick check first:
+        if (keySubclass == _keyType.getRawClass()) {
+            return this;
+        }
+        return new MapLikeType(_class, _keyType.narrowBy(keySubclass), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    public JavaType widenKey(Class<?> keySubclass)
+    {
+        // Can do a quick check first:
+        if (keySubclass == _keyType.getRawClass()) {
+            return this;
+        }
+        return new MapLikeType(_class, _keyType.widenBy(keySubclass), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    @Override
+    public MapLikeType withTypeHandler(Object h)
+    {
+        return new MapLikeType(_class, _keyType, _valueType, _valueHandler, h, _asStatic);
+    }
+
+    @Override
+    public MapLikeType withContentTypeHandler(Object h)
+    {
+        return new MapLikeType(_class, _keyType, _valueType.withTypeHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapLikeType withValueHandler(Object h) {
+        return new MapLikeType(_class, _keyType, _valueType, h, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapLikeType withContentValueHandler(Object h) {
+        return new MapLikeType(_class, _keyType, _valueType.withValueHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapLikeType withStaticTyping() {
+        if (_asStatic) {
+            return this;
+        }
+        return new MapLikeType(_class, _keyType, _valueType.withStaticTyping(),
+                _valueHandler, _typeHandler, true);
+    }
+
+    @Override
+    protected String buildCanonicalName() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(_class.getName());
+        if (_keyType != null) {
+            sb.append('<');
+            sb.append(_keyType.toCanonical());
+            sb.append(',');
+            sb.append(_valueType.toCanonical());
+            sb.append('>');
+        }
+        return sb.toString();
+    }
+ 
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isContainerType() { return true; }
+
+    @Override
+    public boolean isMapLikeType() { return true; }
+    
+    @Override
+    public JavaType getKeyType() { return _keyType; }
+
+    @Override
+    public JavaType getContentType() { return _valueType; }
+
+    @Override
+    public int containedTypeCount() { return 2; }
+    
+    @Override
+    public JavaType containedType(int index) {
+        if (index == 0) return _keyType;
+        if (index == 1) return _valueType;
+        return null;
+    }
+
+    /**
+     * Not sure if we should count on this, but type names
+     * for core interfaces are "K" and "V" respectively.
+     * For now let's assume this should work.
+     */
+    @Override
+    public String containedTypeName(int index) {
+        if (index == 0) return "K";
+        if (index == 1) return "V";
+        return null;
+    }
+
+    @Override
+    public StringBuilder getErasedSignature(StringBuilder sb) {
+        return _classSignature(_class, sb, true);
+    }
+    
+    @Override
+    public StringBuilder getGenericSignature(StringBuilder sb)
+    {
+        _classSignature(_class, sb, false);
+        sb.append('<');
+        _keyType.getGenericSignature(sb);
+        _valueType.getGenericSignature(sb);
+        sb.append(">;");
+        return sb;
+    }
+ 
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    public MapLikeType withKeyTypeHandler(Object h)
+    {
+        return new MapLikeType(_class, _keyType.withTypeHandler(h), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    public MapLikeType withKeyValueHandler(Object h) {
+        return new MapLikeType(_class, _keyType.withValueHandler(h), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    /**
+     * Method that can be used for checking whether this type is a
+     * "real" Collection type; meaning whether it represents a parameterized
+     * subtype of {@link java.util.Collection} or just something that acts
+     * like one.
+     */
+    public boolean isTrueMapType() {
+        return Map.class.isAssignableFrom(_class);
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[map-like type; class "+_class.getName()+", "+_keyType+" -> "+_valueType+"]";
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+
+        MapLikeType other = (MapLikeType) o;
+        return (_class == other._class)
+            && _keyType.equals(other._keyType)
+            && _valueType.equals(other._valueType);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/MapType.java b/src/main/java/com/fasterxml/jackson/databind/type/MapType.java
new file mode 100644
index 0000000..2b14896
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/MapType.java
@@ -0,0 +1,139 @@
+package com.fasterxml.jackson.databind.type;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Type that represents "true" Java Map types.
+ */
+public final class MapType extends MapLikeType
+{
+    private static final long serialVersionUID = -811146779148281500L;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    private MapType(Class<?> mapType, JavaType keyT, JavaType valueT,
+            Object valueHandler, Object typeHandler, boolean asStatic) {
+        super(mapType, keyT, valueT, valueHandler, typeHandler, asStatic);
+    }
+    
+    public static MapType construct(Class<?> rawType, JavaType keyT, JavaType valueT) {
+        // nominally component types will be just Object.class
+        return new MapType(rawType, keyT, valueT, null, null, false);
+    }
+
+    @Override
+    protected JavaType _narrow(Class<?> subclass) {
+        return new MapType(subclass, _keyType, _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType narrowContentsBy(Class<?> contentClass)
+    {
+        // Can do a quick check first:
+        if (contentClass == _valueType.getRawClass()) {
+            return this;
+        }
+        return new MapType(_class, _keyType, _valueType.narrowBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> contentClass)
+    {
+        if (contentClass == _valueType.getRawClass()) {
+            return this;
+        }
+        return new MapType(_class, _keyType, _valueType.widenBy(contentClass),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    @Override
+    public JavaType narrowKey(Class<?> keySubclass)
+    {
+        // Can do a quick check first:
+        if (keySubclass == _keyType.getRawClass()) {
+            return this;
+        }
+        return new MapType(_class, _keyType.narrowBy(keySubclass), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public JavaType widenKey(Class<?> keySubclass)
+    {
+        // Can do a quick check first:
+        if (keySubclass == _keyType.getRawClass()) {
+            return this;
+        }
+        return new MapType(_class, _keyType.widenBy(keySubclass), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    @Override
+    public MapType withTypeHandler(Object h) {
+        return new MapType(_class, _keyType, _valueType, _valueHandler, h, _asStatic);
+    }
+
+    @Override
+    public MapType withContentTypeHandler(Object h)
+    {
+        return new MapType(_class, _keyType, _valueType.withTypeHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    @Override
+    public MapType withValueHandler(Object h) {
+        return new MapType(_class, _keyType, _valueType, h, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapType withContentValueHandler(Object h) {
+        return new MapType(_class, _keyType, _valueType.withValueHandler(h),
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapType withStaticTyping() {
+        if (_asStatic) {
+            return this;
+        }
+        return new MapType(_class, _keyType.withStaticTyping(), _valueType.withStaticTyping(),
+                _valueHandler, _typeHandler, true);
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+    
+    @Override
+    public MapType withKeyTypeHandler(Object h)
+    {
+        return new MapType(_class, _keyType.withTypeHandler(h), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    public MapType withKeyValueHandler(Object h) {
+        return new MapType(_class, _keyType.withValueHandler(h), _valueType,
+                _valueHandler, _typeHandler, _asStatic);
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        return "[map type; class "+_class.getName()+", "+_keyType+" -> "+_valueType+"]";
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
new file mode 100644
index 0000000..b7c89b2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/SimpleType.java
@@ -0,0 +1,249 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Simple types are defined as anything other than one of recognized
+ * container types (arrays, Collections, Maps). For our needs we
+ * need not know anything further, since we have no way of dealing
+ * with generic types other than Collections and Maps.
+ */
+public final class SimpleType
+    extends TypeBase
+{
+    private static final long serialVersionUID = -800374828948534376L;
+
+    /**
+     * Generic type arguments for this type.
+     */
+    protected final JavaType[] _typeParameters;
+
+    /**
+     * Names of generic type arguments for this type; will
+     * match values in {@link #_typeParameters}
+     */
+    protected final String[] _typeNames;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected SimpleType(Class<?> cls) {
+        this(cls, null, null, null, null, false);
+    }
+
+    protected SimpleType(Class<?> cls, String[] typeNames, JavaType[] typeParams,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(cls, 0, valueHandler, typeHandler, asStatic);
+        if (typeNames == null || typeNames.length == 0) {
+            _typeNames = null;
+            _typeParameters = null;
+        } else {
+            _typeNames = typeNames;
+            _typeParameters = typeParams;
+        }
+    }
+
+    /**
+     * Method used by core Jackson classes: NOT to be used by application code.
+     *<p>
+     * NOTE: public only because it is called by <code>ObjectMapper</code> which is
+     * not in same package
+     */
+    public static SimpleType constructUnsafe(Class<?> raw) {
+        return new SimpleType(raw, null, null, null, null, false);
+    }
+
+    @Override
+    protected JavaType _narrow(Class<?> subclass)
+    {
+        // Should we check that there is a sub-class relationship?
+        return new SimpleType(subclass, _typeNames, _typeParameters, _valueHandler, _typeHandler,
+                _asStatic);
+    }
+
+    @Override
+    public JavaType narrowContentsBy(Class<?> subclass)
+    {
+        // should never get called
+        throw new IllegalArgumentException("Internal error: SimpleType.narrowContentsBy() should never be called");
+    }
+
+    @Override
+    public JavaType widenContentsBy(Class<?> subclass)
+    {
+        // should never get called
+        throw new IllegalArgumentException("Internal error: SimpleType.widenContentsBy() should never be called");
+    }
+    
+    public static SimpleType construct(Class<?> cls)
+    {
+        /* Let's add sanity checks, just to ensure no
+         * Map/Collection entries are constructed
+         */
+        if (Map.class.isAssignableFrom(cls)) {
+            throw new IllegalArgumentException("Can not 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()+")");
+        }
+        // ... 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()+")");
+        }
+        return new SimpleType(cls);
+    }
+
+    @Override
+    public SimpleType withTypeHandler(Object h)
+    {
+        return new SimpleType(_class, _typeNames, _typeParameters, _valueHandler, h, _asStatic);
+    }
+
+    @Override
+    public JavaType withContentTypeHandler(Object h) {
+        // no content type, so:
+        throw new IllegalArgumentException("Simple types have no content types; can not call withContenTypeHandler()");
+    }
+
+    @Override
+    public SimpleType withValueHandler(Object h) {
+        if (h == _valueHandler) {
+            return this;
+        }
+        return new SimpleType(_class, _typeNames, _typeParameters, h, _typeHandler, _asStatic);
+    }
+    
+    @Override
+    public  SimpleType withContentValueHandler(Object h) {
+        // no content type, so:
+        throw new IllegalArgumentException("Simple types have no content types; can not call withContenValueHandler()");
+    }
+
+    @Override
+    public SimpleType withStaticTyping() {
+        return _asStatic ? this : new SimpleType(_class,
+                _typeNames, _typeParameters, _valueHandler, _typeHandler, _asStatic);
+    }
+
+    @Override
+    protected String buildCanonicalName()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append(_class.getName());
+        if (_typeParameters != null && _typeParameters.length > 0) {
+            sb.append('<');
+            boolean first = true;
+            for (JavaType t : _typeParameters) {
+                if (first) {
+                    first = false;
+                } else {
+                    sb.append(',');
+                }
+                sb.append(t.toCanonical());
+            }
+            sb.append('>');
+        }
+        return sb.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override
+    public boolean isContainerType() { return false; }
+    
+    @Override
+    public int containedTypeCount() {
+        return (_typeParameters == null) ? 0 : _typeParameters.length;
+    }
+
+    @Override
+    public JavaType containedType(int index)
+    {
+        if (index < 0 || _typeParameters == null || index >= _typeParameters.length) {
+            return null;
+        }
+        return _typeParameters[index];
+    }
+
+    @Override
+    public String containedTypeName(int index)
+    {
+        if (index < 0 || _typeNames == null || index >= _typeNames.length) {
+            return null;
+        }
+        return _typeNames[index];
+    }
+    
+    @Override
+    public StringBuilder getErasedSignature(StringBuilder sb) {
+        return _classSignature(_class, sb, true);
+    }
+    
+    @Override
+    public StringBuilder getGenericSignature(StringBuilder sb)
+    {
+        _classSignature(_class, sb, false);
+        if (_typeParameters != null) {
+            sb.append('<');
+            for (JavaType param : _typeParameters) {
+                sb = param.getGenericSignature(sb);
+            }
+            sb.append('>');
+        }
+        sb.append(';');
+        return sb;
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(40);
+        sb.append("[simple type, class ").append(buildCanonicalName()).append(']');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+
+        SimpleType other = (SimpleType) o;
+
+        // Classes must be identical... 
+        if (other._class != this._class) return false;
+
+        // And finally, generic bindings, if any
+        JavaType[] p1 = _typeParameters;
+        JavaType[] p2 = other._typeParameters;
+        if (p1 == null) {
+            return (p2 == null) || p2.length == 0;
+        }
+        if (p2 == null) return false;
+
+        if (p1.length != p2.length) return false;
+        for (int i = 0, len = p1.length; i < len; ++i) {
+            if (!p1[i].equals(p2[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
new file mode 100644
index 0000000..78d1ad2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBase.java
@@ -0,0 +1,147 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonSerializable;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+public abstract class TypeBase
+    extends JavaType
+    implements JsonSerializable
+{
+    private static final long serialVersionUID = -3581199092426900829L;
+
+    /**
+     * Lazily initialized external representation of the type
+     */
+    volatile transient String _canonicalName;
+
+    /**
+     * @deprecated Since 2.2 use method that takes 'asStatic' argument
+     */
+    @Deprecated
+    protected TypeBase(Class<?> raw, int hash,
+            Object valueHandler, Object typeHandler)
+    {
+        this(raw, hash, valueHandler, typeHandler, false);
+    }
+
+    /**
+     * Main constructor to use by extending classes.
+     */
+    protected TypeBase(Class<?> raw, int hash,
+            Object valueHandler, Object typeHandler, boolean asStatic)
+    {
+        super(raw, hash, valueHandler, typeHandler, asStatic);
+    }
+
+    @Override
+    public String toCanonical()
+    {
+        String str = _canonicalName;
+        if (str == null) {
+            str = buildCanonicalName();
+        }
+        return str;
+    }
+    
+    protected abstract String buildCanonicalName();
+
+    @Override
+    public abstract StringBuilder getGenericSignature(StringBuilder sb);
+
+    @Override
+    public abstract StringBuilder getErasedSignature(StringBuilder sb);
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getValueHandler() { return (T) _valueHandler; }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T getTypeHandler() { return (T) _typeHandler; }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializableWithType base implementation
+    /**********************************************************
+     */
+
+    @Override
+    public void serializeWithType(JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        typeSer.writeTypePrefixForScalar(this, jgen);
+        this.serialize(jgen, provider);
+        typeSer.writeTypeSuffixForScalar(this, jgen);
+    }
+
+    @Override
+    public void serialize(JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+    {
+        jgen.writeString(toCanonical());
+    } 
+    
+    /*
+    /**********************************************************
+    /* Methods for sub-classes to use
+    /**********************************************************
+     */
+
+    /**
+     * @param trailingSemicolon Whether to add trailing semicolon for non-primitive
+     *   (reference) types or not
+     */
+    protected static StringBuilder _classSignature(Class<?> cls, StringBuilder sb,
+           boolean trailingSemicolon)
+    {
+        if (cls.isPrimitive()) {
+            if (cls == Boolean.TYPE) {                
+                sb.append('Z');
+            } else if (cls == Byte.TYPE) {
+                sb.append('B');
+            }
+            else if (cls == Short.TYPE) {
+                sb.append('S');
+            }
+            else if (cls == Character.TYPE) {
+                sb.append('C');
+            }
+            else if (cls == Integer.TYPE) {
+                sb.append('I');
+            }
+            else if (cls == Long.TYPE) {
+                sb.append('J');
+            }
+            else if (cls == Float.TYPE) {
+                sb.append('F');
+            }
+            else if (cls == Double.TYPE) {
+                sb.append('D');
+            }
+            else if (cls == Void.TYPE) {
+                sb.append('V');
+            } else {
+                throw new IllegalStateException("Unrecognized primitive type: "+cls.getName());
+            }
+        } else {
+            sb.append('L');
+            String name = cls.getName();
+            for (int i = 0, len = name.length(); i < len; ++i) {
+                char c = name.charAt(i);
+                if (c == '.') c = '/';
+                sb.append(c);
+            }
+            if (trailingSemicolon) {
+                sb.append(';');
+            }
+        }
+        return sb;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java
new file mode 100644
index 0000000..9a92af2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeBindings.java
@@ -0,0 +1,338 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Helper class used for resolving type parameters for given class
+ */
+public class TypeBindings
+{
+    private final static JavaType[] NO_TYPES = new JavaType[0];
+    
+    /**
+     * Marker to use for (temporarily) unbound references.
+     */
+    public final static JavaType UNBOUND = new SimpleType(Object.class);
+
+    /**
+     * Factory to use for constructing resolved related types.
+     */
+    protected final TypeFactory _typeFactory;
+    
+    /**
+     * Context type used for resolving all types, if specified. May be null,
+     * in which case {@link #_contextClass} is used instead.
+     */
+    protected final JavaType _contextType;
+
+    /**
+     * Specific class to use for resolving all types, for methods and fields
+     * class and its superclasses and -interfaces contain.
+     */
+    protected final Class<?> _contextClass;
+
+    /**
+     * Lazily-instantiated bindings of resolved type parameters
+     */
+    protected Map<String,JavaType> _bindings;
+
+    /**
+     * Also: we may temporarily want to mark certain named types
+     * as resolved (but without exact type); if so, we'll just store
+     * names here.
+     */
+    protected HashSet<String> _placeholders;
+
+    /**
+     * Sometimes it is necessary to allow hierarchic resolution of types: specifically
+     * in cases where there are local bindings (for methods, constructors). If so,
+     * we'll just use simple delegation model.
+     */
+    private final TypeBindings _parentBindings;
+
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+    
+    public TypeBindings(TypeFactory typeFactory, Class<?> cc)
+    {
+        this(typeFactory, null, cc, null);
+    }
+
+    public TypeBindings(TypeFactory typeFactory, JavaType type)
+    {
+        this(typeFactory, null, type.getRawClass(), type);
+    }
+
+    /**
+     * Constructor used to create "child" instances; mostly to
+     * allow delegation from explicitly defined local overrides
+     * (local type variables for methods, constructors) to
+     * contextual (class-defined) ones.
+     */
+    public TypeBindings childInstance() {
+        return new TypeBindings(_typeFactory, this, _contextClass, _contextType);
+    }
+
+    private TypeBindings(TypeFactory tf, TypeBindings parent,
+            Class<?> cc, JavaType type)
+    {
+        _typeFactory = tf;
+        _parentBindings = parent;
+        _contextClass = cc;
+        _contextType = type;
+    }
+
+    /*
+    /**********************************************************
+    /* Pass-through type resolution methods
+    /**********************************************************
+     */
+
+    public JavaType resolveType(Class<?> cls) {
+        return _typeFactory._constructType(cls, this);
+    }
+
+    public JavaType resolveType(Type type) {
+        return _typeFactory._constructType(type, this);
+    }
+    
+    /*
+    /**********************************************************
+    /* Accesors
+    /**********************************************************
+     */
+
+    public int getBindingCount() {
+        if (_bindings == null) {
+            _resolve();
+        }
+        return _bindings.size();
+    }
+    
+    public JavaType findType(String name)
+    {
+        if (_bindings == null) {
+            _resolve();
+        }
+        JavaType t = _bindings.get(name);
+        if (t != null) {
+            return t;
+        }
+        if (_placeholders != null && _placeholders.contains(name)) {
+            return UNBOUND;
+        }
+        if (_parentBindings != null) {
+            return _parentBindings.findType(name);
+        }
+        // nothing found, so...
+        // Should we throw an exception or just return null?
+        
+        /* [JACKSON-499] 18-Feb-2011, tatu: There are some tricky type bindings within
+         *   java.util, such as HashMap$KeySet; so let's punt the problem
+         *   (honestly not sure what to do -- they are unbound for good, I think)
+         */
+        if (_contextClass != null) {
+            Class<?> enclosing = _contextClass.getEnclosingClass();
+            if (enclosing != null) {
+                // [JACKSON-572]: Actually, let's skip this for all non-static inner classes
+                //   (which will also cover 'java.util' type cases...
+                if (!Modifier.isStatic(_contextClass.getModifiers())) {
+                    return UNBOUND;
+                }
+
+                // ... so this piece of code should not be needed any more
+                /*
+                Package pkg = enclosing.getPackage();
+                if (pkg != null) {
+                    // as per [JACKSON-533], also include "java.util.concurrent":
+                    if (pkg.getName().startsWith("java.util")) {
+                        return UNBOUND;
+                    }
+                }
+                */
+            }
+        }
+        
+        String className;
+        if (_contextClass != null) {
+            className = _contextClass.getName();
+        } else if (_contextType != null) {
+            className = _contextType.toString();
+        } else {
+            className = "UNKNOWN";
+        }
+        throw new IllegalArgumentException("Type variable '"+name
+                +"' can not be resolved (with context of class "+className+")");
+        //t = UNBOUND;                
+    }
+
+    public void addBinding(String name, JavaType type)
+    {
+        // note: emptyMap() is unmodifiable, hence second check is needed:
+        if (_bindings == null || _bindings.size() == 0) {
+            _bindings = new LinkedHashMap<String,JavaType>();
+        }
+        _bindings.put(name, type);
+    }
+
+    public JavaType[] typesAsArray()
+    {
+        if (_bindings == null) {
+            _resolve();
+        }
+        if (_bindings.size() == 0) {
+            return NO_TYPES;
+        }
+        return _bindings.values().toArray(new JavaType[_bindings.size()]);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    protected void _resolve()
+    {
+        _resolveBindings(_contextClass);
+
+        // finally: may have root level type info too
+        if (_contextType != null) {
+            int count = _contextType.containedTypeCount();
+            if (count > 0) {
+                for (int i = 0; i < count; ++i) {
+                    String name = _contextType.containedTypeName(i);
+                    JavaType type = _contextType.containedType(i);
+                    addBinding(name, type);
+                }
+            }
+        }
+
+        // nothing bound? mark with empty map to prevent further calls
+        if (_bindings == null) {
+            _bindings = Collections.emptyMap();
+        }
+    }
+
+    public void _addPlaceholder(String name) {
+        if (_placeholders == null) {
+            _placeholders = new HashSet<String>();
+        }
+        _placeholders.add(name);
+    }
+
+    protected void _resolveBindings(Type t)
+    {
+        if (t == null) return;
+        
+        Class<?> raw;
+        if (t instanceof ParameterizedType) {
+            ParameterizedType pt = (ParameterizedType) t;
+            Type[] args = pt.getActualTypeArguments();
+            if (args  != null && args.length > 0) {
+                Class<?> rawType = (Class<?>) pt.getRawType();    
+                TypeVariable<?>[] vars = rawType.getTypeParameters();
+                if (vars.length != args.length) {
+                    throw new IllegalArgumentException("Strange parametrized type (in class "+rawType.getName()+"): number of type arguments != number of type parameters ("+args.length+" vs "+vars.length+")");
+                }
+                for (int i = 0, len = args.length; i < len; ++i) {
+                    TypeVariable<?> var = vars[i];
+                    String name = var.getName();
+                    if (_bindings == null) {
+                        _bindings = new LinkedHashMap<String,JavaType>();
+                    } else {
+                        /* 24-Mar-2010, tatu: Better ensure that we do not overwrite something
+                         *  collected earlier (since we descend towards super-classes):
+                         */
+                        if (_bindings.containsKey(name)) continue;
+                    }
+                    // first: add a placeholder to prevent infinite loops
+                    _addPlaceholder(name);
+                    // then resolve type
+                    _bindings.put(name, _typeFactory._constructType(args[i], this));
+                }
+            }
+            raw = (Class<?>)pt.getRawType();
+        } else if (t instanceof Class<?>) {
+            raw = (Class<?>) t;
+            /* [JACKSON-677]: If this is an inner class then the generics are defined on the 
+             * enclosing class so we have to check there as well.  We don't
+             * need to call getEnclosingClass since anonymous classes declare 
+             * generics
+             */
+            Class<?> decl = raw.getDeclaringClass();
+            /* 08-Feb-2013, tatu: Except that if context is also super-class, we must
+             *   skip it; context will be checked anyway, and we'd get StackOverflow if
+             *   we went there.
+             */
+            if (decl != null && !decl.isAssignableFrom(raw)) {
+                _resolveBindings(raw.getDeclaringClass());
+            }
+
+            /* 24-Mar-2010, tatu: Can not have true generics definitions, but can
+             *   have lower bounds ("<T extends BeanBase>") in declaration itself
+             */
+            TypeVariable<?>[] vars = raw.getTypeParameters();
+            if (vars != null && vars.length > 0) {
+                JavaType[] typeParams = null;
+
+                if (_contextType != null && raw.isAssignableFrom(_contextType.getRawClass())) {
+                    typeParams = _typeFactory.findTypeParameters(_contextType, raw);
+                }
+
+                for (int i = 0; i < vars.length; i++) {
+                    TypeVariable<?> var = vars[i];
+
+                    String name = var.getName();
+                    Type varType = var.getBounds()[0];
+                    if (varType != null) {
+                        if (_bindings == null) {
+                            _bindings = new LinkedHashMap<String,JavaType>();
+                        } else { // and no overwriting...
+                            if (_bindings.containsKey(name)) continue;
+                        }
+                        _addPlaceholder(name); // to prevent infinite loops
+
+                        if (typeParams != null) {
+                            _bindings.put(name, typeParams[i]);
+                        } else {
+                            _bindings.put(name, _typeFactory._constructType(varType, this));
+                        }
+                    }
+                }
+            }
+        } else { // probably can't be any of these... so let's skip for now
+            //if (type instanceof GenericArrayType) {
+            //if (type instanceof TypeVariable<?>) {
+            // if (type instanceof WildcardType) {
+            return;
+        }
+        // but even if it's not a parameterized type, its super types may be:
+        _resolveBindings(raw.getGenericSuperclass());
+        for (Type intType : raw.getGenericInterfaces()) {
+            _resolveBindings(intType);
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        if (_bindings == null) {
+            _resolve();
+        }
+        StringBuilder sb = new StringBuilder("[TypeBindings for ");
+        if (_contextType != null) {
+            sb.append(_contextType.toString());
+        } else {
+            sb.append(_contextClass.getName());
+        }
+        sb.append(": ").append(_bindings).append("]");
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
new file mode 100644
index 0000000..44e6f84
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeFactory.java
@@ -0,0 +1,1049 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+import java.lang.reflect.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.util.ArrayBuilders;
+import com.fasterxml.jackson.databind.util.LRUMap;
+
+
+/**
+ * Class used for creating concrete {@link JavaType} instances,
+ * given various inputs.
+ *<p>
+ * Instances of this class are accessible using {@link com.fasterxml.jackson.databind.ObjectMapper}
+ * as well as many objects it constructs (like
+* {@link com.fasterxml.jackson.databind.DeserializationConfig} and
+ * {@link com.fasterxml.jackson.databind.SerializationConfig})),
+ * but usually those objects also 
+ * expose convenience methods (<code>constructType</code>).
+ * So, you can do for example:
+ *<pre>
+ *   JavaType stringType = mapper.constructType(String.class);
+ *</pre>
+ * However, more advanced methods are only exposed by factory so that you
+ * may need to use:
+ *<pre>
+ *   JavaType stringCollection = mapper.getTypeFactory().constructCollectionType(List.class, String.class);
+ *</pre>
+ */
+ at SuppressWarnings({"rawtypes", "unchecked"})
+public final class TypeFactory
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private final static JavaType[] NO_TYPES = new JavaType[0];
+
+    /**
+     * Globally shared singleton. Not accessed directly; non-core
+     * code should use per-ObjectMapper instance (via configuration objects).
+     * Core Jackson code uses {@link #defaultInstance} for accessing it.
+     */
+    protected final static TypeFactory instance = new TypeFactory();
+    
+    /*
+    /**********************************************************
+    /* Caching
+    /**********************************************************
+     */
+
+    // // // Let's assume that a small set of core primitive/basic types
+    // // // will not be modified, and can be freely shared to streamline
+    // // // parts of processing
+    
+    protected final static SimpleType CORE_TYPE_STRING = new SimpleType(String.class);
+    protected final static SimpleType CORE_TYPE_BOOL = new SimpleType(Boolean.TYPE);
+    protected final static SimpleType CORE_TYPE_INT = new SimpleType(Integer.TYPE);
+    protected final static SimpleType CORE_TYPE_LONG = new SimpleType(Long.TYPE);
+    
+    /**
+     * Since type resolution can be expensive (specifically when resolving
+     * actual generic types), we will use small cache to avoid repetitive
+     * resolution of core types
+     */
+    protected final LRUMap<ClassKey, JavaType> _typeCache = new LRUMap<ClassKey, JavaType>(16, 100);
+
+    /*
+     * Looks like construction of {@link JavaType} instances can be
+     * a bottleneck, esp. for root-level Maps, so we better do bit
+     * of low-level component caching here...
+     */
+    
+    /**
+     * Lazily constructed copy of type hierarchy from {@link java.util.HashMap}
+     * to its supertypes.
+     */
+    protected transient HierarchicType _cachedHashMapType;
+
+    /**
+     * Lazily constructed copy of type hierarchy from {@link java.util.ArrayList}
+     * to its supertypes.
+     */
+    protected transient HierarchicType _cachedArrayListType;
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+    
+    /**
+     * Registered {@link TypeModifier}s: objects that can change details
+     * of {@link JavaType} instances factory constructs.
+     */
+    protected final TypeModifier[] _modifiers;
+    
+    protected final TypeParser _parser;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    private TypeFactory() {
+        _parser = new TypeParser(this);
+        _modifiers = null;
+    }
+
+    protected TypeFactory(TypeParser p, TypeModifier[] mods) {
+        _parser = p;
+        _modifiers = mods;
+    }
+
+    public TypeFactory withModifier(TypeModifier mod) 
+    {
+        if (_modifiers == null) {
+            return new TypeFactory(_parser, new TypeModifier[] { mod });
+        }
+        return new TypeFactory(_parser, ArrayBuilders.insertInListNoDup(_modifiers, mod));
+    }
+
+    /**
+     * Method used to access the globally shared instance, which has
+     * no custom configuration. Used by <code>ObjectMapper</code> to
+     * get the default factory when constructed.
+     */
+    public static TypeFactory defaultInstance() { return instance; }
+
+    /*
+    /**********************************************************
+    /* Static methods for non-instance-specific functionality
+    /**********************************************************
+     */
+    
+    /**
+     * Method for constructing a marker type that indicates missing generic
+     * type information, which is handled same as simple type for
+     * <code>java.lang.Object</code>.
+     */
+    public static JavaType unknownType() {
+        return defaultInstance()._unknownType();
+    }
+
+    /**
+     * Static helper method that can be called to figure out type-erased
+     * call for given JDK type. It can be called statically since type resolution
+     * process can never change actual type-erased class; thereby static
+     * default instance is used for determination.
+     */
+    public static Class<?> rawClass(Type t) {
+        if (t instanceof Class<?>) {
+            return (Class<?>) t;
+        }
+        // Shouldbe able to optimize bit more in future...
+        return defaultInstance().constructType(t).getRawClass();
+    }
+    
+    /*
+    /**********************************************************
+    /* Type conversion, parameterization resolution methods
+    /**********************************************************
+     */
+
+    /**
+     * Factory method for creating a subtype of given base type, as defined
+     * by specified subclass; but retaining generic type information if any.
+     * Can be used, for example, to get equivalent of "HashMap<String,Integer>"
+     * from "Map&ltString,Integer>" by giving <code>HashMap.class</code>
+     * as subclass.
+     */
+    public JavaType constructSpecializedType(JavaType baseType, Class<?> subclass)
+    {
+        // Currently only SimpleType instances can become something else
+        if (baseType instanceof SimpleType) {
+            // and only if subclass is an array, Collection or Map
+            if (subclass.isArray()
+                || Map.class.isAssignableFrom(subclass)
+                || Collection.class.isAssignableFrom(subclass)) {
+                // need to assert type compatibility...
+                if (!baseType.getRawClass().isAssignableFrom(subclass)) {
+                    throw new IllegalArgumentException("Class "+subclass.getClass().getName()+" not subtype of "+baseType);
+                }
+                // this _should_ work, right?
+                JavaType subtype = _fromClass(subclass, new TypeBindings(this, baseType.getRawClass()));
+                // one more thing: handlers to copy?
+                Object h = baseType.getValueHandler();
+                if (h != null) {
+                    subtype = subtype.withValueHandler(h);
+                }
+                h = baseType.getTypeHandler();
+                if (h != null) {
+                    subtype = subtype.withTypeHandler(h);
+                }
+                return subtype;
+            }
+        }
+        // otherwise regular narrowing should work just fine
+        return baseType.narrowBy(subclass);
+    }
+
+    /**
+     * Factory method for constructing a {@link JavaType} out of its canonical
+     * representation (see {@link JavaType#toCanonical()}).
+     * 
+     * @param canonical Canonical string representation of a type
+     * 
+     * @throws IllegalArgumentException If canonical representation is malformed,
+     *   or class that type represents (including its generic parameters) is
+     *   not found
+     */
+    public JavaType constructFromCanonical(String canonical) throws IllegalArgumentException
+    {
+        return _parser.parse(canonical);
+    }
+    
+    /**
+     * Method that is to figure out actual type parameters that given
+     * class binds to generic types defined by given (generic)
+     * interface or class.
+     * This could mean, for example, trying to figure out
+     * key and value types for Map implementations.
+     * 
+     * @param type Sub-type (leaf type) that implements <code>expType</code>
+     */
+    public JavaType[] findTypeParameters(JavaType type, Class<?> expType)
+    {
+        /* Tricky part here is that some JavaType instances have been constructed
+         * from generic type (usually via TypeReference); and in those case
+         * types have been resolved. Alternative is that the leaf type is type-erased
+         * class, in which case this has not been done.
+         * For now simplest way to handle this is to split processing in two: latter
+         * case actually fully works; and former mostly works. In future may need to
+         * rewrite former part, which requires changes to JavaType as well.
+         */
+        Class<?> raw = type.getRawClass();
+        if (raw == expType) {
+            // Direct type info; good since we can return it as is
+            int count = type.containedTypeCount();
+            if (count == 0) return null;
+            JavaType[] result = new JavaType[count];
+            for (int i = 0; i < count; ++i) {
+                result[i] = type.containedType(i);
+            }
+            return result;
+        }
+        /* Otherwise need to go through type-erased class. This may miss cases where
+         * we get generic type; ideally JavaType/SimpleType would retain information
+         * about generic declaration at main level... but let's worry about that
+         * if/when there are problems; current handling is an improvement over earlier
+         * code.
+         */
+        return findTypeParameters(raw, expType, new TypeBindings(this, type));
+    }
+
+    public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType) {
+        return findTypeParameters(clz, expType, new TypeBindings(this, clz));
+    }
+    
+    public JavaType[] findTypeParameters(Class<?> clz, Class<?> expType, TypeBindings bindings)
+    {
+        // First: find full inheritance chain
+        HierarchicType subType = _findSuperTypeChain(clz, expType);
+        // Caller is supposed to ensure this never happens, so:
+        if (subType == null) {
+            throw new IllegalArgumentException("Class "+clz.getName()+" is not a subtype of "+expType.getName());
+        }
+        // Ok and then go to the ultimate super-type:
+        HierarchicType superType = subType;
+        while (superType.getSuperType() != null) {
+            superType = superType.getSuperType();
+            Class<?> raw = superType.getRawClass();
+            TypeBindings newBindings = new TypeBindings(this, raw);
+            if (superType.isGeneric()) { // got bindings, need to resolve
+                ParameterizedType pt = superType.asGeneric();
+                Type[] actualTypes = pt.getActualTypeArguments();
+                TypeVariable<?>[] vars = raw.getTypeParameters();
+                int len = actualTypes.length;
+                for (int i = 0; i < len; ++i) {
+                    String name = vars[i].getName();
+                    JavaType type = _constructType(actualTypes[i], bindings);
+                    newBindings.addBinding(name, type);
+                }
+            }
+            bindings = newBindings;
+        }
+
+        // which ought to be generic (if not, it's raw type)
+        if (!superType.isGeneric()) {
+            return null;
+        }
+        return bindings.typesAsArray();
+    }
+
+    /**
+     * Method that can be called to figure out more specific of two
+     * types (if they are related; that is, one implements or extends the
+     * other); or if not related, return the primary type.
+     * 
+     * @param type1 Primary type to consider
+     * @param type2 Secondary type to consider
+     * 
+     * @since 2.2
+     */
+    public JavaType moreSpecificType(JavaType type1, JavaType type2)
+    {
+        if (type1 == null) {
+            return type2;
+        }
+        if (type2 == null) {
+            return type1;
+        }
+        Class<?> raw1 = type1.getRawClass();
+        Class<?> raw2 = type2.getRawClass();
+        if (raw1 == raw2) {
+            return type1;
+        }
+        // TODO: maybe try sub-classing, to retain generic types?
+        if (raw1.isAssignableFrom(raw2)) {
+            return type2;
+        }
+        return type1;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public factory methods
+    /**********************************************************
+     */
+
+    public JavaType constructType(Type type) {
+        return _constructType(type, null);
+    }
+
+    public JavaType constructType(Type type, TypeBindings bindings) {
+        return _constructType(type, bindings);
+    }
+    
+    public JavaType constructType(TypeReference<?> typeRef) {
+        return _constructType(typeRef.getType(), null);
+    }
+    
+    public JavaType constructType(Type type, Class<?> context) {
+        TypeBindings b = (context == null) ? null : new TypeBindings(this, context);
+        return _constructType(type, b);
+    }
+
+    public JavaType constructType(Type type, JavaType context) {
+        TypeBindings b = (context == null) ? null : new TypeBindings(this, context);
+        return _constructType(type, b);
+    }
+    
+    /**
+     * Factory method that can be used if type information is passed
+     * as Java typing returned from <code>getGenericXxx</code> methods
+     * (usually for a return or argument type).
+     */
+    protected JavaType _constructType(Type type, TypeBindings context)
+    {
+        JavaType resultType;
+
+        // simple class?
+        if (type instanceof Class<?>) {
+            Class<?> cls = (Class<?>) type;
+            resultType = _fromClass(cls, context);
+        }
+        // But if not, need to start resolving.
+        else if (type instanceof ParameterizedType) {
+            resultType = _fromParamType((ParameterizedType) type, context);
+        }
+        else if (type instanceof JavaType) { // [Issue#116]
+            return (JavaType) type;
+        }
+        else if (type instanceof GenericArrayType) {
+            resultType = _fromArrayType((GenericArrayType) type, context);
+        }
+        else if (type instanceof TypeVariable<?>) {
+            resultType = _fromVariable((TypeVariable<?>) type, context);
+        }
+        else if (type instanceof WildcardType) {
+            resultType = _fromWildcard((WildcardType) type, context);
+        } else {
+            // sanity check
+            throw new IllegalArgumentException("Unrecognized Type: "+((type == null) ? "[null]" : type.toString()));
+        }
+        /* [JACKSON-521]: Need to allow TypeModifiers to alter actual type; however,
+         * for now only call for simple types (i.e. not for arrays, map or collections).
+         * Can be changed in future it necessary
+         */
+        if (_modifiers != null && !resultType.isContainerType()) {
+            for (TypeModifier mod : _modifiers) {
+                resultType = mod.modifyType(resultType, type, context, this);
+            }
+        }
+        return resultType;
+    }
+
+    /*
+    /**********************************************************
+    /* Direct factory methods
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing an {@link ArrayType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on array type itself; but are called
+     * for element type (and other contained types)
+     */
+    public ArrayType constructArrayType(Class<?> elementType) {
+        return ArrayType.construct(_constructType(elementType, null), null, null);
+    }
+    
+    /**
+     * Method for constructing an {@link ArrayType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on array type itself; but are called
+     * for contained types.
+     */
+    public ArrayType constructArrayType(JavaType elementType) {
+        return ArrayType.construct(elementType, null, null);
+    }
+
+    /**
+     * Method for constructing a {@link CollectionType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on Collection type itself; but are called
+     * for contained types.
+     */
+    public CollectionType constructCollectionType(Class<? extends Collection> collectionClass, Class<?> elementClass) {
+        return CollectionType.construct(collectionClass, constructType(elementClass));
+    }
+    
+    /**
+     * Method for constructing a {@link CollectionType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on Collection type itself; but are called
+     * for contained types.
+     */
+    public CollectionType constructCollectionType(Class<? extends Collection> collectionClass, JavaType elementType) {
+        return CollectionType.construct(collectionClass, elementType);
+    }
+
+    /**
+     * Method for constructing a {@link CollectionLikeType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public CollectionLikeType constructCollectionLikeType(Class<?> collectionClass, Class<?> elementClass) {
+        return CollectionLikeType.construct(collectionClass, constructType(elementClass));
+    }
+    
+    /**
+     * Method for constructing a {@link CollectionLikeType}.
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public CollectionLikeType constructCollectionLikeType(Class<?> collectionClass, JavaType elementType) {
+        return CollectionLikeType.construct(collectionClass, elementType);
+    }
+    
+    /**
+     * Method for constructing a {@link MapType} instance
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public MapType constructMapType(Class<? extends Map> mapClass, JavaType keyType, JavaType valueType) {
+        return MapType.construct(mapClass, keyType, valueType);
+    }
+
+    /**
+     * Method for constructing a {@link MapType} instance
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public MapType constructMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
+        return MapType.construct(mapClass, constructType(keyClass), constructType(valueClass));
+    }
+
+    /**
+     * Method for constructing a {@link MapLikeType} instance
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public MapLikeType constructMapLikeType(Class<?> mapClass, JavaType keyType, JavaType valueType) {
+        return MapLikeType.construct(mapClass, keyType, valueType);
+    }
+    
+    /**
+     * Method for constructing a {@link MapLikeType} instance
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public MapLikeType constructMapLikeType(Class<?> mapClass, Class<?> keyClass, Class<?> valueClass) {
+        return MapType.construct(mapClass, constructType(keyClass), constructType(valueClass));
+    }
+    
+    /**
+     * Method for constructing a type instance with specified parameterization.
+     */
+    public JavaType constructSimpleType(Class<?> rawType, JavaType[] parameterTypes)
+    {
+        // Quick sanity check: must match numbers of types with expected...
+        TypeVariable<?>[] typeVars = rawType.getTypeParameters();
+        if (typeVars.length != parameterTypes.length) {
+            throw new IllegalArgumentException("Parameter type mismatch for "+rawType.getName()
+                    +": expected "+typeVars.length+" parameters, was given "+parameterTypes.length);
+        }
+        String[] names = new String[typeVars.length];
+        for (int i = 0, len = typeVars.length; i < len; ++i) {
+            names[i] = typeVars[i].getName();
+        }
+        JavaType resultType = new SimpleType(rawType, names, parameterTypes, null, null, false);
+        return resultType;
+    } 
+
+    /**
+     * Method that will force construction of a simple type, without trying to
+     * check for more specialized types.
+     *<p> 
+     * NOTE: no type modifiers are called on type either, so calling this method
+     * should only be used if caller really knows what it's doing...
+     */
+    public JavaType uncheckedSimpleType(Class<?> cls) {
+        return new SimpleType(cls);
+    }
+    
+    /**
+     * Factory method for constructing {@link JavaType} that
+     * represents a parameterized type. For example, to represent
+     * type <code>List<Set<Integer>></code>, you could
+     * call
+     *<pre>
+     *  TypeFactory.parametricType(List.class, Integer.class);
+     *</pre>
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses)
+    {
+        int len = parameterClasses.length;
+        JavaType[] pt = new JavaType[len];
+        for (int i = 0; i < len; ++i) {
+            pt[i] = _fromClass(parameterClasses[i], null);
+        }
+        return constructParametricType(parametrized, pt);
+    }
+
+    /**
+     * Factory method for constructing {@link JavaType} that
+     * represents a parameterized type. For example, to represent
+     * type <code>List<Set<Integer>></code>, you could
+     * call
+     *<pre>
+     *  JavaType inner = TypeFactory.parametricType(Set.class, Integer.class);
+     *  TypeFactory.parametricType(List.class, inner);
+     *</pre>
+     *<p>
+     * NOTE: type modifiers are NOT called on constructed type itself; but are called
+     * for contained types.
+     */
+    public JavaType constructParametricType(Class<?> parametrized, JavaType... parameterTypes)
+    {
+        JavaType resultType;
+        
+        // Need to check kind of class we are dealing with...
+        if (parametrized.isArray()) {
+            // 19-Jan-2010, tatus: should we support multi-dimensional arrays directly?
+            if (parameterTypes.length != 1) {
+                throw new IllegalArgumentException("Need exactly 1 parameter type for arrays ("+parametrized.getName()+")");
+            }
+            resultType = constructArrayType(parameterTypes[0]);
+        }
+        else if (Map.class.isAssignableFrom(parametrized)) {
+            if (parameterTypes.length != 2) {
+                throw new IllegalArgumentException("Need exactly 2 parameter types for Map types ("+parametrized.getName()+")");
+            }
+            resultType = constructMapType((Class<Map<?,?>>)parametrized, parameterTypes[0], parameterTypes[1]);
+        }
+        else if (Collection.class.isAssignableFrom(parametrized)) {
+            if (parameterTypes.length != 1) {
+                throw new IllegalArgumentException("Need exactly 1 parameter type for Collection types ("+parametrized.getName()+")");
+            }
+            resultType = constructCollectionType((Class<Collection<?>>)parametrized, parameterTypes[0]);
+        } else {
+            resultType = constructSimpleType(parametrized, parameterTypes);
+        }
+        return resultType;
+    }
+
+    /*
+    /**********************************************************
+    /* Direct factory methods for "raw" variants, used when
+    /* parameterization is unknown
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to construct "raw" Collection type; meaning that its
+     * parameterization is unknown.
+     * This is similar to using <code>Object.class</code> parameterization,
+     * and is equivalent to calling:
+     *<pre>
+     *  typeFactory.constructCollectionType(collectionClass, typeFactory.unknownType());
+     *<pre>
+     *<p>
+     * This method should only be used if parameterization is completely unavailable.
+     */
+    public CollectionType constructRawCollectionType(Class<? extends Collection> collectionClass) {
+        return CollectionType.construct(collectionClass, unknownType());
+    }
+
+    /**
+     * Method that can be used to construct "raw" Collection-like type; meaning that its
+     * parameterization is unknown.
+     * This is similar to using <code>Object.class</code> parameterization,
+     * and is equivalent to calling:
+     *<pre>
+     *  typeFactory.constructCollectionLikeType(collectionClass, typeFactory.unknownType());
+     *<pre>
+     *<p>
+     * This method should only be used if parameterization is completely unavailable.
+     */
+    public CollectionLikeType constructRawCollectionLikeType(Class<?> collectionClass) {
+        return CollectionLikeType.construct(collectionClass, unknownType());
+    }
+
+    /**
+     * Method that can be used to construct "raw" Map type; meaning that its
+     * parameterization is unknown.
+     * This is similar to using <code>Object.class</code> parameterization,
+     * and is equivalent to calling:
+     *<pre>
+     *  typeFactory.constructMapType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType());
+     *<pre>
+     *<p>
+     * This method should only be used if parameterization is completely unavailable.
+     */
+    public MapType constructRawMapType(Class<? extends Map> mapClass) {
+        return MapType.construct(mapClass, unknownType(), unknownType());
+    }
+
+    /**
+     * Method that can be used to construct "raw" Map-like type; meaning that its
+     * parameterization is unknown.
+     * This is similar to using <code>Object.class</code> parameterization,
+     * and is equivalent to calling:
+     *<pre>
+     *  typeFactory.constructMapLikeType(collectionClass, typeFactory.unknownType(), typeFactory.unknownType());
+     *<pre>
+     *<p>
+     * This method should only be used if parameterization is completely unavailable.
+     */
+    public MapLikeType constructRawMapLikeType(Class<?> mapClass) {
+        return MapLikeType.construct(mapClass, unknownType(), unknownType());
+    }
+
+    /*
+    /**********************************************************
+    /* Actual factory methods
+    /**********************************************************
+     */
+
+    /**
+     * @param context Mapping of formal parameter declarations (for generic
+     *   types) into actual types
+     */
+    protected JavaType _fromClass(Class<?> clz, TypeBindings context)
+    {
+        // Very first thing: small set of core types we know well:
+        if (clz == String.class) return CORE_TYPE_STRING;
+        if (clz == Boolean.TYPE) return CORE_TYPE_BOOL;
+        if (clz == Integer.TYPE) return CORE_TYPE_INT;
+        if (clz == Long.TYPE) return CORE_TYPE_LONG;
+        
+        // Barring that, we may have recently constructed an instance:
+        ClassKey key = new ClassKey(clz);
+        JavaType result;
+        
+        synchronized (_typeCache) {
+            result = _typeCache.get(key);
+        }
+        if (result != null) {
+            return result;
+        }
+
+        // If context was needed, weed do:
+        /*
+        if (context == null) {
+            context = new TypeBindings(this, cls);
+        }
+        */
+        
+        // First: do we have an array type?
+        if (clz.isArray()) {
+            result = ArrayType.construct(_constructType(clz.getComponentType(), null), null, null);
+        /* Also: although enums can also be fully resolved, there's little
+         * point in doing so (T extends Enum<T>) etc.
+         */
+        } else if (clz.isEnum()) {
+            result = new SimpleType(clz);
+        /* Maps and Collections aren't quite as hot; problem is, due
+         * to type erasure we often do not know typing and can only assume
+         * base Object.
+         */
+        } else if (Map.class.isAssignableFrom(clz)) {
+            result = _mapType(clz);
+        } else if (Collection.class.isAssignableFrom(clz)) {
+            result =  _collectionType(clz);
+        } else {
+            result = new SimpleType(clz);
+        }
+        
+        synchronized (_typeCache) {
+            _typeCache.put(key, result);
+        }
+        
+        return result;
+    }
+    
+    /**
+     * Method used by {@link TypeParser} when generics-aware version
+     * is constructed.
+     */
+    protected JavaType _fromParameterizedClass(Class<?> clz, List<JavaType> paramTypes)
+    {
+        if (clz.isArray()) { // ignore generics (should never have any)
+            return ArrayType.construct(_constructType(clz.getComponentType(), null), null, null);
+        }
+        if (clz.isEnum()) { // ditto for enums
+            return new SimpleType(clz);
+        }
+        if (Map.class.isAssignableFrom(clz)) {
+            // First: if we do have param types, use them
+            JavaType keyType, contentType;
+            if (paramTypes.size() > 0) {
+                keyType = paramTypes.get(0);
+                contentType = (paramTypes.size() >= 2) ?
+                        paramTypes.get(1) : _unknownType();
+                return MapType.construct(clz, keyType, contentType);
+            }
+            return _mapType(clz);
+        }
+        if (Collection.class.isAssignableFrom(clz)) {
+            if (paramTypes.size() >= 1) {
+                return CollectionType.construct(clz, paramTypes.get(0));
+            }
+            return _collectionType(clz);
+        }
+        if (paramTypes.size() == 0) {
+            return new SimpleType(clz);
+        }
+        JavaType[] pt = paramTypes.toArray(new JavaType[paramTypes.size()]);
+        return constructSimpleType(clz, pt);
+    }
+    
+    /**
+     * This method deals with parameterized types, that is,
+     * first class generic classes.
+     */
+    protected JavaType _fromParamType(ParameterizedType type, TypeBindings context)
+    {
+        /* First: what is the actual base type? One odd thing
+         * is that 'getRawType' returns Type, not Class<?> as
+         * one might expect. But let's assume it is always of
+         * type Class: if not, need to add more code to resolve
+         * it to Class.
+         */
+        Class<?> rawType = (Class<?>) type.getRawType();
+        Type[] args = type.getActualTypeArguments();
+        int paramCount = (args == null) ? 0 : args.length;
+
+        JavaType[] pt;
+        
+        if (paramCount == 0) {
+            pt = NO_TYPES;
+        } else {
+            pt = new JavaType[paramCount];
+            for (int i = 0; i < paramCount; ++i) {
+                pt[i] = _constructType(args[i], context);
+            }
+        }
+
+        // Ok: Map or Collection?
+        if (Map.class.isAssignableFrom(rawType)) {
+            JavaType subtype = constructSimpleType(rawType, pt);
+            JavaType[] mapParams = findTypeParameters(subtype, Map.class);
+            if (mapParams.length != 2) {
+                throw new IllegalArgumentException("Could not find 2 type parameters for Map class "+rawType.getName()+" (found "+mapParams.length+")");
+            }
+            return MapType.construct(rawType, mapParams[0], mapParams[1]);
+        }
+        if (Collection.class.isAssignableFrom(rawType)) {
+            JavaType subtype = constructSimpleType(rawType, pt);
+            JavaType[] collectionParams = findTypeParameters(subtype, Collection.class);
+            if (collectionParams.length != 1) {
+                throw new IllegalArgumentException("Could not find 1 type parameter for Collection class "+rawType.getName()+" (found "+collectionParams.length+")");
+            }
+            return CollectionType.construct(rawType, collectionParams[0]);
+        }
+        if (paramCount == 0) { // no generics
+            return new SimpleType(rawType);
+        }
+        return constructSimpleType(rawType, pt);
+    }
+
+    
+    protected JavaType _fromArrayType(GenericArrayType type, TypeBindings context)
+    {
+        JavaType compType = _constructType(type.getGenericComponentType(), context);
+        return ArrayType.construct(compType, null, null);
+    }
+
+    protected JavaType _fromVariable(TypeVariable<?> type, TypeBindings context)
+    {
+        /* 26-Sep-2009, tatus: It should be possible to try "partial"
+         *  resolution; meaning that it is ok not to find bindings.
+         *  For now this is indicated by passing null context.
+         */
+        if (context == null) {
+            return _unknownType();
+        }
+
+        // Ok: here's where context might come in handy!
+        String name = type.getName();
+        JavaType actualType = context.findType(name);
+        if (actualType != null) {
+            return actualType;
+        }
+
+        /* 29-Jan-2010, tatu: We used to throw exception here, if type was
+         *   bound: but the problem is that this can occur for generic "base"
+         *   method, overridden by sub-class. If so, we will want to ignore
+         *   current type (for method) since it will be masked.
+         */
+        Type[] bounds = type.getBounds();
+
+        // With type variables we must use bound information.
+        // Theoretically this gets tricky, as there may be multiple
+        // bounds ("... extends A & B"); and optimally we might
+        // want to choose the best match. Also, bounds are optional;
+        // but here we are lucky in that implicit "Object" is
+        // added as bounds if so.
+        // Either way let's just use the first bound, for now, and
+        // worry about better match later on if there is need.
+
+        /* 29-Jan-2010, tatu: One more problem are recursive types
+         *   (T extends Comparable<T>). Need to add "placeholder"
+         *   for resolution to catch those.
+         */
+        context._addPlaceholder(name);        
+        return _constructType(bounds[0], context);
+    }
+
+    protected JavaType _fromWildcard(WildcardType type, TypeBindings context)
+    {
+        /* Similar to challenges with TypeVariable, we may have
+         * multiple upper bounds. But it is also possible that if
+         * upper bound defaults to Object, we might want to consider
+         * lower bounds instead.
+         *
+         * For now, we won't try anything more advanced; above is
+         * just for future reference.
+         */
+        return _constructType(type.getUpperBounds()[0], context);
+    }
+
+    private JavaType _mapType(Class<?> rawClass)
+    {
+        JavaType[] typeParams = findTypeParameters(rawClass, Map.class);
+        // ok to have no types ("raw")
+        if (typeParams == null) {
+            return MapType.construct(rawClass, _unknownType(), _unknownType());
+        }
+        // but exactly 2 types if any found
+        if (typeParams.length != 2) {
+            throw new IllegalArgumentException("Strange Map type "+rawClass.getName()+": can not determine type parameters");
+        }
+        return MapType.construct(rawClass, typeParams[0], typeParams[1]);
+    }
+
+    private JavaType _collectionType(Class<?> rawClass)
+    {
+        JavaType[] typeParams = findTypeParameters(rawClass, Collection.class);
+        // ok to have no types ("raw")
+        if (typeParams == null) {
+            return CollectionType.construct(rawClass, _unknownType());
+        }
+        // but exactly 2 types if any found
+        if (typeParams.length != 1) {
+            throw new IllegalArgumentException("Strange Collection type "+rawClass.getName()+": can not determine type parameters");
+        }
+        return CollectionType.construct(rawClass, typeParams[0]);
+    }    
+
+    protected JavaType _resolveVariableViaSubTypes(HierarchicType leafType, String variableName, TypeBindings bindings)
+    {
+        // can't resolve raw types; possible to have as-of-yet-unbound types too:
+        if (leafType != null && leafType.isGeneric()) {
+            TypeVariable<?>[] typeVariables = leafType.getRawClass().getTypeParameters();
+            for (int i = 0, len = typeVariables.length; i < len; ++i) {
+                TypeVariable<?> tv = typeVariables[i];
+                if (variableName.equals(tv.getName())) {
+                    // further resolution needed?
+                    Type type = leafType.asGeneric().getActualTypeArguments()[i];
+                    if (type instanceof TypeVariable<?>) {
+                        return _resolveVariableViaSubTypes(leafType.getSubType(), ((TypeVariable<?>) type).getName(), bindings);
+                    }
+                    // no we're good for the variable (but it may have parameterization of its own)
+                    return _constructType(type, bindings);
+                }
+            }
+        }
+        return _unknownType();
+    }
+    
+    protected JavaType _unknownType() {
+        return new SimpleType(Object.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used to find inheritance (implements, extends) path
+     * between given types, if one exists (caller generally checks before
+     * calling this method). Returned type represents given <b>subtype</b>,
+     * with supertype linkage extending to <b>supertype</b>.
+     */
+    protected HierarchicType  _findSuperTypeChain(Class<?> subtype, Class<?> supertype)
+    {
+        // If super-type is a class (not interface), bit simpler
+        if (supertype.isInterface()) {
+            return _findSuperInterfaceChain(subtype, supertype);
+        }
+        return _findSuperClassChain(subtype, supertype);
+    }
+
+    protected HierarchicType _findSuperClassChain(Type currentType, Class<?> target)
+    {
+        HierarchicType current = new HierarchicType(currentType);
+        Class<?> raw = current.getRawClass();
+        if (raw == target) {
+            return current;
+        }
+        // Otherwise, keep on going down the rat hole...
+        Type parent = raw.getGenericSuperclass();
+        if (parent != null) {
+            HierarchicType sup = _findSuperClassChain(parent, target);
+            if (sup != null) {
+                sup.setSubType(current);
+                current.setSuperType(sup);
+                return current;
+            }
+        }
+        return null;
+    }
+
+    protected HierarchicType _findSuperInterfaceChain(Type currentType, Class<?> target)
+    {
+        HierarchicType current = new HierarchicType(currentType);
+        Class<?> raw = current.getRawClass();
+        if (raw == target) {
+            return new HierarchicType(currentType);
+        }
+        // Otherwise, keep on going down the rat hole; first implemented interfaces
+        /* 16-Aug-2011, tatu: Minor optimization based on profiled hot spot; let's
+         *   try caching certain commonly needed cases
+         */
+        if (raw == HashMap.class) {
+            if (target == Map.class) {
+                return _hashMapSuperInterfaceChain(current);
+            }
+        }
+        if (raw == ArrayList.class) {
+            if (target == List.class) {
+                return _arrayListSuperInterfaceChain(current);
+            }
+        }
+        return _doFindSuperInterfaceChain(current, target);
+    }
+    
+    protected HierarchicType _doFindSuperInterfaceChain(HierarchicType current, Class<?> target)
+    {
+        Class<?> raw = current.getRawClass();
+        Type[] parents = raw.getGenericInterfaces();
+        // as long as there are superclasses
+        // and unless we have already seen the type (<T extends X<T>>)
+        if (parents != null) {
+            for (Type parent : parents) {
+                HierarchicType sup = _findSuperInterfaceChain(parent, target);
+                if (sup != null) {
+                    sup.setSubType(current);
+                    current.setSuperType(sup);
+                    return current;
+                }
+            }
+        }
+        // and then super-class if any
+        Type parent = raw.getGenericSuperclass();
+        if (parent != null) {
+            HierarchicType sup = _findSuperInterfaceChain(parent, target);
+            if (sup != null) {
+                sup.setSubType(current);
+                current.setSuperType(sup);
+                return current;
+            }
+        }
+        return null;
+    }
+
+    protected synchronized HierarchicType _hashMapSuperInterfaceChain(HierarchicType current)
+    {
+        if (_cachedHashMapType == null) {
+            HierarchicType base = current.deepCloneWithoutSubtype();
+            _doFindSuperInterfaceChain(base, Map.class);
+            _cachedHashMapType = base.getSuperType();
+        }
+        HierarchicType t = _cachedHashMapType.deepCloneWithoutSubtype();
+        current.setSuperType(t);
+        t.setSubType(current);
+        return current;
+    }
+
+    protected synchronized HierarchicType _arrayListSuperInterfaceChain(HierarchicType current)
+    {
+        if (_cachedArrayListType == null) {
+            HierarchicType base = current.deepCloneWithoutSubtype();
+            _doFindSuperInterfaceChain(base, List.class);
+            _cachedArrayListType = base.getSuperType();
+        }
+        HierarchicType t = _cachedArrayListType.deepCloneWithoutSubtype();
+        current.setSuperType(t);
+        t.setSubType(current);
+        return current;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java b/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java
new file mode 100644
index 0000000..0b59011
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeModifier.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Class that defines API that can be used to modify details of
+ * {@link JavaType} instances constructed using {@link TypeFactory}.
+ * Registered modifiers are called in order, to let them modify (or
+ * 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.
+ */
+public abstract class TypeModifier
+{
+    /**
+     * Method called to let modifier change constructed type definition.
+     * Note that this is only guaranteed to be called for
+     * non-container types ("simple" types not recognized as arrays,
+     * <code>java.util.Collection</code> or <code>java.util.Map</code>).
+     * 
+     * @param type Instance to modify
+     * @param jdkType JDK type that was used to construct instance to modify
+     * @param context Type resolution context used for the type
+     * @param typeFactory Type factory that can be used to construct parameter type; note,
+     *   however, that care must be taken to avoid infinite loops -- specifically, do not
+     *   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.
+     */
+    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
new file mode 100644
index 0000000..4f47048
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/TypeParser.java
@@ -0,0 +1,132 @@
+package com.fasterxml.jackson.databind.type;
+
+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}
+ * representations and constructing type instances.
+ * 
+ * @author tatu
+ */
+public class TypeParser
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final TypeFactory _factory;
+        
+    public TypeParser(TypeFactory f) {
+        _factory = f;
+    }
+
+    public JavaType parse(String canonical)
+        throws IllegalArgumentException
+    {
+        canonical = canonical.trim();
+        MyTokenizer tokens = new MyTokenizer(canonical);
+        JavaType type = parseType(tokens);
+        // must be end, now
+        if (tokens.hasMoreTokens()) {
+            throw _problem(tokens, "Unexpected tokens after complete type");
+        }
+        return type;
+    }
+
+    protected JavaType parseType(MyTokenizer tokens)
+        throws IllegalArgumentException
+    {
+        if (!tokens.hasMoreTokens()) {
+            throw _problem(tokens, "Unexpected end-of-string");
+        }
+        Class<?> base = findClass(tokens.nextToken(), tokens);
+        // either end (ok, non generic type), or generics
+        if (tokens.hasMoreTokens()) {
+            String token = tokens.nextToken();
+            if ("<".equals(token)) {
+                return _factory._fromParameterizedClass(base, parseTypes(tokens));
+            }
+            // can be comma that separates types, or closing '>'
+            tokens.pushBack(token);
+        }
+        return _factory._fromClass(base, null);
+    }
+
+    protected List<JavaType> parseTypes(MyTokenizer tokens)
+        throws IllegalArgumentException
+    {
+        ArrayList<JavaType> types = new ArrayList<JavaType>();
+        while (tokens.hasMoreTokens()) {
+            types.add(parseType(tokens));
+            if (!tokens.hasMoreTokens()) break;
+            String token = tokens.nextToken();
+            if (">".equals(token)) return types;
+            if (!",".equals(token)) {
+                throw _problem(tokens, "Unexpected token '"+token+"', expected ',' or '>')");
+            }
+        }
+        throw _problem(tokens, "Unexpected end-of-string");
+    }
+
+    protected Class<?> findClass(String className, MyTokenizer tokens)
+    {
+        try {
+            return ClassUtil.findClass(className);
+        } catch (Exception e) {
+            if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            }
+            throw _problem(tokens, "Can not locate class '"+className+"', problem: "+e.getMessage());
+        }
+    }
+
+    protected IllegalArgumentException _problem(MyTokenizer tokens, String msg)
+    {
+        return new IllegalArgumentException("Failed to parse type '"+tokens.getAllInput()
+                +"' (remaining: '"+tokens.getRemainingInput()+"'): "+msg);
+    }
+
+    final static class MyTokenizer
+        extends StringTokenizer
+    {
+        protected final String _input;
+
+        protected int _index;
+
+        protected String _pushbackToken;
+        
+        public MyTokenizer(String str) {            
+            super(str, "<,>", true);
+            _input = str;
+        }
+
+        @Override
+        public boolean hasMoreTokens() {
+            return (_pushbackToken != null) || super.hasMoreTokens();
+        }
+        
+        @Override
+        public String nextToken() {
+            String token;
+            if (_pushbackToken != null) {
+                token = _pushbackToken;
+                _pushbackToken = null;
+            } else {
+                token = super.nextToken();
+            }
+            _index += token.length();
+            return token;
+        }
+
+        public void pushBack(String token) {
+            _pushbackToken = token;
+            _index -= token.length();
+        }
+        
+        public String getAllInput() { return _input; }
+        public String getUsedInput() { return _input.substring(0, _index); }
+        public String getRemainingInput() { return _input.substring(_index); }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/type/package-info.java b/src/main/java/com/fasterxml/jackson/databind/type/package-info.java
new file mode 100644
index 0000000..13b6d33
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/type/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * Package that contains concrete implementations of
+ * {@link com.fasterxml.jackson.databind.JavaType}, as
+ * well as the factory ({@link com.fasterxml.jackson.databind.type.TypeFactory}) for
+ * constructing instances from various input data types
+ * (like {@link java.lang.Class}, {@link java.lang.reflect.Type})
+ * and programmatically (for structured types, arrays,
+ * {@link java.util.List}s and {@link java.util.Map}s).
+ */
+package com.fasterxml.jackson.databind.type;
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java b/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java
new file mode 100644
index 0000000..cdb7ea9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Annotations.java
@@ -0,0 +1,24 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Interface that defines interface for accessing contents of a
+ * collection of annotations. This is needed when introspecting
+ * annotation-based features from different kinds of things, not
+ * just objects that Java Reflection interface exposes.
+ *<p>
+ * Standard mutable implementation is {@link com.fasterxml.jackson.databind.introspect.AnnotationMap}
+ */
+public interface Annotations
+{
+    /**
+     * Main access method used to find value for given annotation.
+     */
+    public <A extends Annotation> A get(Class<A> cls);
+
+    /**
+     * 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
new file mode 100644
index 0000000..2c5847d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ArrayBuilders.java
@@ -0,0 +1,358 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.lang.reflect.Array;
+import java.util.*;
+
+/**
+ * Helper class that contains set of distinct builders for different
+ * arrays of primitive values. It also provides trivially simple
+ * reuse scheme, which assumes that caller knows not to use instances
+ * concurrently (which works ok with primitive arrays since they can
+ * not contain other non-primitive types).
+ */
+public final class ArrayBuilders
+{
+    private BooleanBuilder _booleanBuilder = null;
+
+    // note: no need for char[] builder, assume they are Strings
+
+    private ByteBuilder _byteBuilder = null;
+    private ShortBuilder _shortBuilder = null;
+    private IntBuilder _intBuilder = null;
+    private LongBuilder _longBuilder = null;
+    
+    private FloatBuilder _floatBuilder = null;
+    private DoubleBuilder _doubleBuilder = null;
+
+    public ArrayBuilders() { }
+
+    public BooleanBuilder getBooleanBuilder()
+    {
+        if (_booleanBuilder == null) {
+            _booleanBuilder = new BooleanBuilder();
+        }
+        return _booleanBuilder;
+    }
+
+    public ByteBuilder getByteBuilder()
+    {
+        if (_byteBuilder == null) {
+            _byteBuilder = new ByteBuilder();
+        }
+        return _byteBuilder;
+    }
+    public ShortBuilder getShortBuilder()
+    {
+        if (_shortBuilder == null) {
+            _shortBuilder = new ShortBuilder();
+        }
+        return _shortBuilder;
+    }
+    public IntBuilder getIntBuilder()
+    {
+        if (_intBuilder == null) {
+            _intBuilder = new IntBuilder();
+        }
+        return _intBuilder;
+    }
+    public LongBuilder getLongBuilder()
+    {
+        if (_longBuilder == null) {
+            _longBuilder = new LongBuilder();
+        }
+        return _longBuilder;
+    }
+
+    public FloatBuilder getFloatBuilder()
+    {
+        if (_floatBuilder == null) {
+            _floatBuilder = new FloatBuilder();
+        }
+        return _floatBuilder;
+    }
+    public DoubleBuilder getDoubleBuilder()
+    {
+        if (_doubleBuilder == null) {
+            _doubleBuilder = new DoubleBuilder();
+        }
+        return _doubleBuilder;
+    }
+
+    /*
+    /**********************************************************
+    /* Impl classes
+    /**********************************************************
+     */
+
+    public final static class BooleanBuilder
+        extends PrimitiveArrayBuilder<boolean[]>
+    {
+        public BooleanBuilder() { }
+        @Override
+        public final boolean[] _constructArray(int len) { return new boolean[len]; }
+    }
+
+    public final static class ByteBuilder
+        extends PrimitiveArrayBuilder<byte[]>
+    {
+        public ByteBuilder() { }
+        @Override
+        public final byte[] _constructArray(int len) { return new byte[len]; }
+    }
+    public final static class ShortBuilder
+        extends PrimitiveArrayBuilder<short[]>
+    {
+        public ShortBuilder() { }
+        @Override
+        public final short[] _constructArray(int len) { return new short[len]; }
+    }
+    public final static class IntBuilder
+        extends PrimitiveArrayBuilder<int[]>
+    {
+        public IntBuilder() { }
+        @Override
+        public final int[] _constructArray(int len) { return new int[len]; }
+    }
+    public final static class LongBuilder
+        extends PrimitiveArrayBuilder<long[]>
+    {
+        public LongBuilder() { }
+        @Override
+        public final long[] _constructArray(int len) { return new long[len]; }
+    }
+
+    public final static class FloatBuilder
+        extends PrimitiveArrayBuilder<float[]>
+    {
+        public FloatBuilder() { }
+        @Override
+        public final float[] _constructArray(int len) { return new float[len]; }
+    }
+    public final static class DoubleBuilder
+        extends PrimitiveArrayBuilder<double[]>
+    {
+        public DoubleBuilder() { }
+        @Override
+        public final double[] _constructArray(int len) { return new double[len]; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Static helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method used for constructing simple value comparator used for
+     * comparing arrays for content equality.
+     *<p>
+     * Note: current implementation is not optimized for speed; if performance
+     * ever becomes an issue, it is possible to construct much more efficient
+     * typed instances (one for Object[] and sub-types; one per primitive type).
+     * 
+     * @since 2.2 Moved from earlier <code>Comparators</code> class
+     */
+    public static Object getArrayComparator(final Object defaultValue)
+    {
+        final int length = Array.getLength(defaultValue);
+        final Class<?> defaultValueType = defaultValue.getClass();
+        return new Object() {
+            @Override
+            public boolean equals(Object other) {
+                if (other == this) return true;
+                if (other == null || other.getClass() != defaultValueType) {
+                    return false;
+                }
+                if (Array.getLength(other) != length) return false;
+                // so far so good: compare actual equality; but only shallow one
+                for (int i = 0; i < length; ++i) {
+                    Object value1 = Array.get(defaultValue, i);
+                    Object value2 = Array.get(other, i);
+                    if (value1 == value2) continue;
+                    if (value1 != null) {
+                        if (!value1.equals(value2)) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+        };
+    }
+    
+    public static <T> HashSet<T> arrayToSet(T[] elements)
+    {
+        HashSet<T> result = new HashSet<T>();
+        if (elements != null) {
+            for (T elem : elements) {
+                result.add(elem);
+            }
+        }
+        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;
+    }
+
+    /**
+     * Helper method for constructing a new array that contains specified
+     * element followed by contents of the given array but never contains
+     * duplicates.
+     * If element already existed, one of two things happens: if the element
+     * was already the first one in array, array is returned as is; but
+     * if not, a new copy is created in which element has moved as the head.
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T[] insertInListNoDup(T[] array, T element)
+    {
+        final int len = array.length;
+        
+        // First: see if the element already exists
+        for (int ix = 0; ix < len; ++ix) {
+            if (array[ix] == element) {
+                // if at head already, return as is
+                if (ix == 0) {
+                    return array;
+                }
+                // otherwise move things around
+                T[] result = (T[]) Array.newInstance(array.getClass().getComponentType(), len);
+                System.arraycopy(array, 0, result, 1, ix);
+                result[0] = element;
+                ++ix;
+                int left = len - ix;
+                if (left > 0) {
+                	System.arraycopy(array, ix, result, ix, left);
+                }
+                return result;
+            }
+        }
+
+        // but if not, allocate new array, move
+        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;
+    }
+    
+    /**
+     * Helper method for exposing contents of arrays using a read-only iterator
+     */
+    public static <T> Iterator<T> arrayAsIterator(T[] array)
+    {
+        return new ArrayIterator<T>(array);
+    }
+
+    public static <T> Iterable<T> arrayAsIterable(T[] array)
+    {
+        return new ArrayIterator<T>(array);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Iterator implementation used to efficiently expose contents of an
+     * Array as read-only iterator.
+     */
+    private final static class ArrayIterator<T>
+        implements Iterator<T>, Iterable<T>
+    {
+        private final T[] _array;
+        
+        private int _index;
+
+        public ArrayIterator(T[] array) {
+            _array = array;
+            _index = 0;
+        }
+        
+       @Override
+        public boolean hasNext() {
+            return _index < _array.length;
+        }
+
+        @Override
+        public T next()
+        {
+            if (_index >= _array.length) {
+                throw new NoSuchElementException();
+            }
+            return _array[_index++];
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
new file mode 100644
index 0000000..5c61f35
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/BeanUtil.java
@@ -0,0 +1,194 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+
+/**
+ * Helper class that contains functionality needed by both serialization
+ * and deserialization side.
+ */
+public class BeanUtil
+{
+    /*
+    /**********************************************************
+    /* Handling "getter" names
+    /**********************************************************
+     */
+
+    public static String okNameForGetter(AnnotatedMethod am)
+    {
+        String name = am.getName();
+        String str = okNameForIsGetter(am, name);
+        if (str == null) {
+            str = okNameForRegularGetter(am, name);
+        }
+        return str;
+    }
+
+    public static String okNameForRegularGetter(AnnotatedMethod am, String name)
+    {
+        if (name.startsWith("get")) {
+            /* 16-Feb-2009, tatu: To handle [JACKSON-53], need to block
+             *   CGLib-provided method "getCallbacks". Not sure of exact
+             *   safe criteria to get decent coverage without false matches;
+             *   but for now let's assume there's no reason to use any 
+             *   such getter from CGLib.
+             *   But let's try this approach...
+             */
+            if ("getCallbacks".equals(name)) {
+                if (isCglibGetCallbacks(am)) {
+                    return null;
+                }
+            } else if ("getMetaClass".equals(name)) {
+                /* 30-Apr-2009, tatu: [JACKSON-103], need to suppress
+                 *    serialization of a cyclic (and useless) reference
+                 */
+                if (isGroovyMetaClassGetter(am)) {
+                    return null;
+                }
+            }
+            return manglePropertyName(name.substring(3));
+        }
+        return null;
+    }
+
+    public static String okNameForIsGetter(AnnotatedMethod am, String name)
+    {
+        if (name.startsWith("is")) {
+            // plus, must return boolean...
+            Class<?> rt = am.getRawType();
+            if (rt != Boolean.class && rt != Boolean.TYPE) {
+                return null;
+            }
+            return manglePropertyName(name.substring(2));
+        }
+        // no, not a match by name
+        return null;
+    }
+
+    public static String okNameForSetter(AnnotatedMethod am)
+    {
+    	String name = okNameForMutator(am, "set");
+    	if (name != null) {
+	        // 26-Nov-2009 [JACSON-103], need to suppress this internal groovy method
+	        if ("metaClass".equals(name)) {
+	            if (isGroovyMetaClassSetter(am)) {
+	                return null;
+	            }
+	        }
+	        return name;
+    	}
+    	return null;
+    }
+
+    public static String okNameForMutator(AnnotatedMethod am, String prefix)
+    {
+	    String name = am.getName();
+        if (name.startsWith(prefix)) {
+        	return manglePropertyName(name.substring(prefix.length()));
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods for bean property name handling
+    /**********************************************************
+     */
+
+    /**
+     * This method was added to address [JACKSON-53]: need to weed out
+     * CGLib-injected "getCallbacks". 
+     * 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
+     * result type is "net.sf.cglib.proxy.Callback[]"
+     *<p>
+     * Also, see [JACKSON-177]; Hibernate may repackage cglib
+     * it uses, so we better catch that too
+     */
+    protected static boolean isCglibGetCallbacks(AnnotatedMethod am)
+    {
+        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
+        Package pkg = compType.getPackage();
+        if (pkg != null) {
+            String pname = pkg.getName();
+            if (pname.startsWith("net.sf.cglib")
+                // also, as per [JACKSON-177]
+                || pname.startsWith("org.hibernate.repackage.cglib")) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Similar to {@link #isCglibGetCallbacks}, need to suppress
+     * a cyclic reference to resolve [JACKSON-103]
+     */
+    protected static boolean isGroovyMetaClassSetter(AnnotatedMethod am)
+    {
+        Class<?> argType = am.getRawParameterType(0);
+        Package pkg = argType.getPackage();
+        if (pkg != null && pkg.getName().startsWith("groovy.lang")) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Another helper method to deal with rest of [JACKSON-103]
+     */
+    protected static boolean isGroovyMetaClassGetter(AnnotatedMethod am)
+    {
+        Class<?> rt = am.getRawType();
+        if (rt == null || rt.isArray()) {
+            return false;
+        }
+        Package pkg = rt.getPackage();
+        if (pkg != null && pkg.getName().startsWith("groovy.lang")) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Method called to figure out name of the property, given 
+     * corresponding suggested name based on a method or field name.
+     *
+     * @param basename Name of accessor/mutator method, not including prefix
+     *  ("get"/"is"/"set")
+     */
+    protected static String manglePropertyName(String basename)
+    {
+        int len = basename.length();
+
+        // First things first: empty basename is no good
+        if (len == 0) {
+            return null;
+        }
+        // otherwise, lower case initial chars
+        StringBuilder sb = null;
+        for (int i = 0; i < len; ++i) {
+            char upper = basename.charAt(i);
+            char lower = Character.toLowerCase(upper);
+            if (upper == lower) {
+                break;
+            }
+            if (sb == null) {
+                sb = new StringBuilder(basename);
+            }
+            sb.setCharAt(i, lower);
+        }
+        return (sb == null) ? basename : sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
new file mode 100644
index 0000000..c7bf18f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ClassUtil.java
@@ -0,0 +1,688 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
+
+public final class ClassUtil
+{
+    /*
+    /**********************************************************
+    /* Methods that deal with inheritance
+    /**********************************************************
+     */
+
+    /**
+     * Method that will find all sub-classes and implemented interfaces
+     * of a given class or interface. Classes are listed in order of
+     * precedence, starting with the immediate super-class, followed by
+     * interfaces class directly declares to implemented, and then recursively
+     * followed by parent of super-class and so forth.
+     * Note that <code>Object.class</code> is not included in the list
+     * regardless of whether <code>endBefore</code> argument is defined or not.
+     *
+     * @param endBefore Super-type to NOT include in results, if any; when
+     *    encountered, will be ignored (and no super types are checked).
+     */
+    public static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore)
+    {
+        return findSuperTypes(cls, endBefore, new ArrayList<Class<?>>(8));
+    }
+    
+    public static List<Class<?>> findSuperTypes(Class<?> cls, Class<?> endBefore, List<Class<?>> result)
+    {
+        _addSuperTypes(cls, endBefore, result, false);
+        return result;
+    }
+    
+    private static void _addSuperTypes(Class<?> cls, Class<?> endBefore, Collection<Class<?>> result, boolean addClassItself)
+    {
+        if (cls == endBefore || cls == null || cls == Object.class) {
+            return;
+        }
+        if (addClassItself) {
+            if (result.contains(cls)) { // already added, no need to check supers
+                return;
+            }
+            result.add(cls);
+        }
+        for (Class<?> intCls : cls.getInterfaces()) {
+            _addSuperTypes(intCls, endBefore, result, true);
+        }
+        _addSuperTypes(cls.getSuperclass(), endBefore, result, true);
+    }
+    
+    /*
+    /**********************************************************
+    /* Class type detection methods
+    /**********************************************************
+     */
+
+    /**
+     * @return Null if class might be a bean; type String (that identifies
+     *   why it's not a bean) if not
+     */
+    public static String canBeABeanType(Class<?> type)
+    {
+        // First: language constructs that ain't beans:
+        if (type.isAnnotation()) {
+            return "annotation";
+        }
+        if (type.isArray()) {
+            return "array";
+        }
+        if (type.isEnum()) {
+            return "enum";
+        }
+        if (type.isPrimitive()) {
+            return "primitive";
+        }
+
+        // Anything else? Seems valid, then
+        return null;
+    }
+    
+    public static String isLocalType(Class<?> type, boolean allowNonStatic)
+    {
+        /* As per [JACKSON-187], GAE seems to throw SecurityExceptions
+         * here and there... and GAE itself has a bug, too
+         * (see []). Bah. So we need to catch some wayward exceptions on GAE
+         */
+        try {
+            // one more: method locals, anonymous, are not good:
+            if (type.getEnclosingMethod() != null) {
+                return "local/anonymous";
+            }
+            
+            /* But how about non-static inner classes? Can't construct
+             * easily (theoretically, we could try to check if parent
+             * happens to be enclosing... but that gets convoluted)
+             */
+            if (!allowNonStatic) {
+                if (type.getEnclosingClass() != null) {
+                    if (!Modifier.isStatic(type.getModifiers())) {
+                        return "non-static member class";
+                    }
+                }
+            }
+        }
+        catch (SecurityException e) { }
+        catch (NullPointerException e) { }
+        return null;
+    }
+
+    /**
+     * Method for finding enclosing class for non-static inner classes
+     */
+    public static Class<?> getOuterClass(Class<?> type)
+    {
+        // as above, GAE has some issues...
+        try {
+            // one more: method locals, anonymous, are not good:
+            if (type.getEnclosingMethod() != null) {
+                return null;
+            }
+            if (!Modifier.isStatic(type.getModifiers())) {
+                return type.getEnclosingClass();
+            }
+        } catch (SecurityException e) { }
+        catch (NullPointerException e) { }
+        return null;
+    }
+    
+    
+    /**
+     * Helper method used to weed out dynamic Proxy types; types that do
+     * not expose concrete method API that we could use to figure out
+     * automatic Bean (property) based serialization.
+     */
+    public static boolean isProxyType(Class<?> type)
+    {
+        // As per [Issue#57], should NOT disqualify JDK proxy:
+        /*
+        // Then: well-known proxy (etc) classes
+        if (Proxy.isProxyClass(type)) {
+            return true;
+        }
+        */
+        String name = type.getName();
+        // Hibernate uses proxies heavily as well:
+        if (name.startsWith("net.sf.cglib.proxy.")
+            || name.startsWith("org.hibernate.proxy.")) {
+            return true;
+        }
+        // Not one of known proxies, nope:
+        return false;
+    }
+
+    /**
+     * Helper method that checks if given class is a concrete one;
+     * that is, not an interface or abstract class.
+     */
+    public static boolean isConcrete(Class<?> type)
+    {
+        int mod = type.getModifiers();
+        return (mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0;
+    }
+
+    public static boolean isConcrete(Member member)
+    {
+        int mod = member.getModifiers();
+        return (mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0;
+    }
+    
+    public static boolean isCollectionMapOrArray(Class<?> type)
+    {
+        if (type.isArray()) return true;
+        if (Collection.class.isAssignableFrom(type)) return true;
+        if (Map.class.isAssignableFrom(type)) return true;
+        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();
+    }
+
+    /*
+    /**********************************************************
+    /* Class loading
+    /**********************************************************
+     */
+
+    public static Class<?> findClass(String className) throws ClassNotFoundException
+    {
+        // [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;
+        }
+        // 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);
+    }
+    
+    /*
+    /**********************************************************
+    /* Method type detection methods
+    /**********************************************************
+     */
+
+    public static boolean hasGetterSignature(Method m)
+    {
+        // First: static methods can't be getters
+        if (Modifier.isStatic(m.getModifiers())) {
+            return false;
+        }
+        // Must take no args
+        Class<?>[] pts = m.getParameterTypes();
+        if (pts != null && pts.length != 0) {
+            return false;
+        }
+        // Can't be a void method
+        if (Void.TYPE == m.getReturnType()) {
+            return false;
+        }
+        // Otherwise looks ok:
+        return true;
+    }
+
+    /*
+    /**********************************************************
+    /* Exception handling
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to find the "root cause", innermost
+     * of chained (wrapped) exceptions.
+     */
+    public static Throwable getRootCause(Throwable t)
+    {
+        while (t.getCause() != null) {
+            t = t.getCause();
+        }
+        return t;
+    }
+
+    /**
+     * 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 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)
+    {
+        throwAsIAE(t, t.getMessage());
+    }
+
+    /**
+     * Method that will wrap 't' as an {@link IllegalArgumentException} (and with
+     * specified message) if it
+     * is a checked exception; otherwise (runtime exception or error) throw as is
+     */
+    public static void throwAsIAE(Throwable t, String msg)
+    {
+        if (t instanceof RuntimeException) {
+            throw (RuntimeException) t;
+        }
+        if (t instanceof Error) {
+            throw (Error) t;
+        }
+        throw new IllegalArgumentException(msg, t);
+    }
+
+    /**
+     * 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
+     */
+    public static void unwrapAndThrowAsIAE(Throwable t)
+    {
+        throwAsIAE(getRootCause(t));
+    }
+
+    /**
+     * 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
+     */
+    public static void unwrapAndThrowAsIAE(Throwable t, String msg)
+    {
+        throwAsIAE(getRootCause(t), msg);
+    }
+
+    /*
+    /**********************************************************
+    /* Instantiation
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to try to create an instantiate of
+     * specified type. Instantiation is done using default no-argument
+     * constructor.
+     *
+     * @param canFixAccess Whether it is possible to try to change access
+     *   rights of the default constructor (in case it is not publicly
+     *   accessible) or not.
+     *
+     * @throws IllegalArgumentException If instantiation fails for any reason;
+     *    except for cases where constructor throws an unchecked exception
+     *    (which will be passed as is)
+     */
+    public static <T> T createInstance(Class<T> cls, boolean canFixAccess)
+        throws IllegalArgumentException
+    {
+        Constructor<T> ctor = findConstructor(cls, canFixAccess);
+        if (ctor == null) {
+            throw new IllegalArgumentException("Class "+cls.getName()+" has no default (no arg) constructor");
+        }
+        try {
+            return ctor.newInstance();
+        } catch (Exception e) {
+            ClassUtil.unwrapAndThrowAsIAE(e, "Failed to instantiate class "+cls.getName()+", problem: "+e.getMessage());
+            return null;
+        }
+    }
+
+    public static <T> Constructor<T> findConstructor(Class<T> cls, boolean canFixAccess)
+        throws IllegalArgumentException
+    {
+        try {
+            Constructor<T> ctor = cls.getDeclaredConstructor();
+            if (canFixAccess) {
+                checkAndFixAccess(ctor);
+            } 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");
+                }
+            }
+            return ctor;
+        } catch (NoSuchMethodException e) {
+            ;
+        } catch (Exception e) {
+            ClassUtil.unwrapAndThrowAsIAE(e, "Failed to find default constructor of class "+cls.getName()+", problem: "+e.getMessage());
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Primitive type support
+    /**********************************************************
+     */
+    
+    /**
+     * Helper method used to get default value for wrappers used for primitive types
+     * (0 for Integer etc)
+     */
+    public static Object defaultValue(Class<?> cls)
+    {
+        if (cls == Integer.TYPE) {
+            return Integer.valueOf(0);
+        }
+        if (cls == Long.TYPE) {
+            return Long.valueOf(0L);
+        }
+        if (cls == Boolean.TYPE) {
+            return Boolean.FALSE;
+        }
+        if (cls == Double.TYPE) {
+            return Double.valueOf(0.0);
+        }
+        if (cls == Float.TYPE) {
+            return Float.valueOf(0.0f);
+        }
+        if (cls == Byte.TYPE) {
+            return Byte.valueOf((byte) 0);
+        }
+        if (cls == Short.TYPE) {
+            return Short.valueOf((short) 0);
+        }
+        if (cls == Character.TYPE) {
+            return '\0';
+        }
+        throw new IllegalArgumentException("Class "+cls.getName()+" is not a primitive type");
+    }
+
+    /**
+     * Helper method for finding wrapper type for given primitive type (why isn't
+     * there one in JDK?)
+     */
+    public static Class<?> wrapperType(Class<?> primitiveType)
+    {
+        if (primitiveType == Integer.TYPE) {
+            return Integer.class;
+        }
+        if (primitiveType == Long.TYPE) {
+            return Long.class;
+        }
+        if (primitiveType == Boolean.TYPE) {
+            return Boolean.class;
+        }
+        if (primitiveType == Double.TYPE) {
+            return Double.class;
+        }
+        if (primitiveType == Float.TYPE) {
+            return Float.class;
+        }
+        if (primitiveType == Byte.TYPE) {
+            return Byte.class;
+        }
+        if (primitiveType == Short.TYPE) {
+            return Short.class;
+        }
+        if (primitiveType == Character.TYPE) {
+            return Character.class;
+        }
+        throw new IllegalArgumentException("Class "+primitiveType.getName()+" is not a primitive type");
+    }
+    
+    /*
+    /**********************************************************
+    /* Access checking/handling methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to check if we can use the passed method or constructor
+     * (wrt access restriction -- public methods can be called, others
+     * usually not); and if not, if there is a work-around for
+     * the problem.
+     */
+    public static void checkAndFixAccess(Member member)
+    {
+        // We know all members are also accessible objects...
+        AccessibleObject ao = (AccessibleObject) member;
+
+        /* 14-Jan-2009, tatu: It seems safe and potentially beneficial to
+         *   always to make it accessible (latter because it will force
+         *   skipping checks we have no use for...), so let's always call it.
+         */
+        //if (!ao.isAccessible()) {
+        try {
+            ao.setAccessible(true);
+        } catch (SecurityException se) {
+            /* 17-Apr-2009, tatu: Related to [JACKSON-101]: this can fail on
+             *    platforms like EJB and 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());
+            }
+        }
+        //}
+    }
+
+    /*
+    /**********************************************************
+    /* Enum type detection
+    /**********************************************************
+     */
+
+    /**
+     * Helper method that can be used to dynamically figure out
+     * enumeration type of given {@link EnumSet}, without having
+     * access to its declaration.
+     * Code is needed to work around design flaw in JDK.
+     */
+    public static Class<? extends Enum<?>> findEnumType(EnumSet<?> s)
+    {
+    	// First things first: if not empty, easy to determine
+    	if (!s.isEmpty()) {
+    		return findEnumType(s.iterator().next());
+    	}
+    	// Otherwise need to locate using an internal field
+    	return EnumTypeLocator.instance.enumTypeFor(s);
+    }
+
+    /**
+     * Helper method that can be used to dynamically figure out
+     * enumeration type of given {@link EnumSet}, without having
+     * access to its declaration.
+     * Code is needed to work around design flaw in JDK.
+     */
+    public static Class<? extends Enum<?>> findEnumType(EnumMap<?,?> m)
+    {
+    	if (!m.isEmpty()) {
+    		return findEnumType(m.keySet().iterator().next());
+    	}
+    	// Otherwise need to locate using an internal field
+    	return EnumTypeLocator.instance.enumTypeFor(m);
+    }
+
+    /**
+     * Helper method that can be used to dynamically figure out formal
+     * enumeration type (class) for given enumeration. This is either
+     * class of enum instance (for "simple" enumerations), or its
+     * superclass (for enums with instance fields or methods)
+     */
+    @SuppressWarnings("unchecked")
+	public static Class<? extends Enum<?>> findEnumType(Enum<?> en)
+    {
+        // enums with "body" are sub-classes of the formal type
+    	Class<?> ec = en.getClass();
+    	if (ec.getSuperclass() != Enum.class) {
+    	    ec = ec.getSuperclass();
+    	}
+    	return (Class<? extends Enum<?>>) ec;
+    }
+
+    /**
+     * Helper method that can be used to dynamically figure out formal
+     * enumeration type (class) for given class of an enumeration value.
+     * This is either class of enum instance (for "simple" enumerations),
+     * or its superclass (for enums with instance fields or methods)
+     */
+    @SuppressWarnings("unchecked")
+    public static Class<? extends Enum<?>> findEnumType(Class<?> cls)
+    {
+        // enums with "body" are sub-classes of the formal type
+        if (cls.getSuperclass() != Enum.class) {
+            cls = cls.getSuperclass();
+        }
+        return (Class<? extends Enum<?>>) cls;
+    }
+
+    /*
+    /**********************************************************
+    /* 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.
+     */
+    public static boolean isJacksonStdImpl(Object impl) {
+        return (impl != null) && isJacksonStdImpl(impl.getClass());
+    }
+
+    public static boolean isJacksonStdImpl(Class<?> implClass) {
+        return (implClass.getAnnotation(JacksonStdImpl.class) != null);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Inner class used to contain gory details of how we can determine
+     * details of instances of common JDK types like {@link EnumMap}s.
+     */
+    private static class EnumTypeLocator
+    {
+    	final static EnumTypeLocator instance = new EnumTypeLocator();
+
+    	private final Field enumSetTypeField;
+    	private final Field enumMapTypeField;
+    	
+    	private EnumTypeLocator() {
+    	    /* JDK uses following fields to store information about actual Enumeration
+    	     * type for EnumSets, EnumMaps...
+    	     */
+    	    enumSetTypeField = locateField(EnumSet.class, "elementType", Class.class);
+    	    enumMapTypeField = locateField(EnumMap.class, "elementType", Class.class);
+    	}
+
+    	@SuppressWarnings("unchecked")
+    	public Class<? extends Enum<?>> enumTypeFor(EnumSet<?> set)
+    	{
+    	    if (enumSetTypeField != null) {
+    	        return (Class<? extends Enum<?>>) get(set, enumSetTypeField);
+    	    }
+    	    throw new IllegalStateException("Can not figure out type for EnumSet (odd JDK platform?)");
+    	}
+
+    	@SuppressWarnings("unchecked")
+    	public Class<? extends Enum<?>> enumTypeFor(EnumMap<?,?> set)
+        {
+    	    if (enumMapTypeField != null) {
+    	        return (Class<? extends Enum<?>>) get(set, enumMapTypeField);
+    	    }
+    	    throw new IllegalStateException("Can not figure out type for EnumMap (odd JDK platform?)");
+        }
+    	
+    	private Object get(Object bean, Field field)
+    	{
+    	    try {
+    	        return field.get(bean);
+    	    } catch (Exception e) {
+    	        throw new IllegalArgumentException(e);
+    	    }
+    	}
+    	
+    	private static Field locateField(Class<?> fromClass, String expectedName, Class<?> type)
+    	{
+    	    Field found = null;
+    	    // First: let's see if we can find exact match:
+    	    Field[] fields = fromClass.getDeclaredFields();
+    	    for (Field f : fields) {
+    	        if (expectedName.equals(f.getName()) && f.getType() == type) {
+    	            found = f;
+    	            break;
+    	        }
+    	    }
+    	    // And if not, if there is just one field with the type, that field
+    	    if (found == null) {
+    	        for (Field f : fields) {
+    	            if (f.getType() == type) {
+    	                // If more than one, can't choose
+    	                if (found != null) return null;
+    	                found = f;
+    	            }
+    	        }
+    	    }
+    	    if (found != null) { // it's non-public, need to force accessible
+    	        try {
+    	            found.setAccessible(true);
+    	        } catch (Throwable t) { }
+    	    }
+    	    return found;
+    	}
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Converter.java b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java
new file mode 100644
index 0000000..d41834c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Converter.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Helper interface for things that convert Objects of
+ * one type to another.
+ *<p>
+ * NOTE: implementors are strongly encouraged to extend {@link StdConverter}
+ * instead of directly implementing {@link Converter}, since that can
+ * help with default implementation of typically boiler-plate code.
+ *
+ * @param <IN> Type of values converter takes
+ * @param <OUT> Result type from conversion
+ * 
+ * @see com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer
+ * 
+ * @since 2.1
+ */
+public interface Converter<IN,OUT>
+{
+    /**
+     * Main conversion method.
+     */
+    public OUT convert(IN value);
+
+    /**
+     * 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).
+     * 
+     * @since 2.2
+     */
+    public JavaType getInputType(TypeFactory typeFactory);
+
+    /**
+     * 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).
+     * 
+     * @since 2.2
+     */
+    public JavaType getOutputType(TypeFactory typeFactory);
+    
+    /*
+    /**********************************************************
+    /* Helper class(es)
+    /**********************************************************
+     */
+
+    /**
+     * This marker class is only to be used with annotations, to
+     * indicate that <b>no converter is to be used</b>.
+     *<p>
+     * Specifically, this class is to be used as the marker for
+     * annotation {@link com.fasterxml.jackson.databind.annotation.JsonSerialize},
+     * property <code>converter</code> (and related)
+     * 
+     * @since 2.2
+     */
+    public abstract static class None
+        implements Converter<Object,Object> { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EmptyIterator.java b/src/main/java/com/fasterxml/jackson/databind/util/EmptyIterator.java
new file mode 100644
index 0000000..c341d8a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EmptyIterator.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Implementation of {@link Iterator} for Empty collections.
+ * While JDK has an implementation starting with 1.7, we need this
+ * class until then.
+ * 
+ * @since 2.2 (before, we had embedded instances)
+ */
+public class EmptyIterator<T> implements Iterator<T>
+{
+    private final static EmptyIterator<?> instance = new EmptyIterator<Object>();
+
+    private EmptyIterator() { }
+
+    @SuppressWarnings("unchecked")
+    public static <T> Iterator<T> instance() {
+        return (Iterator<T>) instance;
+    }
+    
+    @Override
+    public boolean hasNext() { return false; }
+    @Override
+    public T next() { throw new NoSuchElementException(); }
+
+    @Override
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
new file mode 100644
index 0000000..48acc5a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumResolver.java
@@ -0,0 +1,138 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Helper class used to resolve String values (either JSON Object field
+ * names or regular String values) into Java Enum instances.
+ */
+public class EnumResolver<T extends Enum<T>>
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final Class<T> _enumClass;
+
+    protected final T[] _enums;
+
+    protected final HashMap<String, T> _enumsById;
+
+    protected EnumResolver(Class<T> enumClass, T[] enums, HashMap<String, T> map)
+    {
+        _enumClass = enumClass;
+        _enums = enums;
+        _enumsById = map;
+    }
+
+    /**
+     * Factory method for constructing resolver that maps from Enum.name() into
+     * Enum value
+     */
+    public static <ET extends Enum<ET>> EnumResolver<ET> constructFor(Class<ET> enumCls, AnnotationIntrospector ai)
+    {
+        ET[] enumValues = enumCls.getEnumConstants();
+        if (enumValues == null) {
+            throw new IllegalArgumentException("No enum constants for class "+enumCls.getName());
+        }
+        HashMap<String, ET> map = new HashMap<String, ET>();
+        for (ET e : enumValues) {
+            map.put(ai.findEnumValue(e), e);
+        }
+        return new EnumResolver<ET>(enumCls, enumValues, map);
+    }
+
+    /**
+     * Factory method for constructing resolver that maps from Enum.toString() into
+     * Enum value
+     */
+    public static <ET extends Enum<ET>> EnumResolver<ET> constructUsingToString(Class<ET> enumCls)
+    {
+        ET[] enumValues = enumCls.getEnumConstants();
+        HashMap<String, ET> map = new HashMap<String, ET>();
+        // from last to first, so that in case of duplicate values, first wins
+        for (int i = enumValues.length; --i >= 0; ) {
+            ET e = enumValues[i];
+            map.put(e.toString(), e);
+        }
+        return new EnumResolver<ET>(enumCls, enumValues, map);
+    }    
+
+    public static <ET extends Enum<ET>> EnumResolver<ET> constructUsingMethod(Class<ET> enumCls,
+            Method accessor)
+    {
+        ET[] enumValues = enumCls.getEnumConstants();
+        HashMap<String, ET> map = new HashMap<String, ET>();
+        // from last to first, so that in case of duplicate values, first wins
+        for (int i = enumValues.length; --i >= 0; ) {
+            ET en = enumValues[i];
+            try {
+                Object o = accessor.invoke(en);
+                if (o != null) {
+                    map.put(o.toString(), en);
+                }
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Failed to access @JsonValue of Enum value "+en+": "+e.getMessage());
+            }
+        }
+        return new EnumResolver<ET>(enumCls, enumValues, map);
+    }    
+    
+    /**
+     * This method is needed because of the dynamic nature of constructing Enum
+     * resolvers.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static EnumResolver<?> constructUnsafe(Class<?> rawEnumCls, AnnotationIntrospector ai)
+    {            
+        /* This is oh so wrong... but at least ugliness is mostly hidden in just
+         * this one place.
+         */
+        Class<Enum> enumCls = (Class<Enum>) rawEnumCls;
+        return constructFor(enumCls, ai);
+    }
+
+    /**
+     * Method that needs to be used instead of {@link #constructUsingToString}
+     * if static type of enum is not known.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static EnumResolver<?> constructUnsafeUsingToString(Class<?> rawEnumCls)
+    {            
+        // oh so wrong... not much that can be done tho
+        Class<Enum> enumCls = (Class<Enum>) rawEnumCls;
+        return constructUsingToString(enumCls);
+    }
+
+    /**
+     * Method used when actual String serialization is indicated using @JsonValue
+     * on a method.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public static EnumResolver<?> constructUnsafeUsingMethod(Class<?> rawEnumCls, Method accessor)
+    {            
+        // wrong as ever but:
+        Class<Enum> enumCls = (Class<Enum>) rawEnumCls;
+        return constructUsingMethod(enumCls, accessor);
+    }
+    
+    public T findEnum(String key)
+    {
+        return _enumsById.get(key);
+    }
+
+    public T getEnum(int index)
+    {
+        if (index < 0 || index >= _enums.length) {
+            return null;
+        }
+        return _enums[index];
+    }
+
+    public Class<T> getEnumClass() { return _enumClass; }
+
+    public int lastValidIndex() { return _enums.length-1; }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java
new file mode 100644
index 0000000..40752a5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/EnumValues.java
@@ -0,0 +1,96 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Helper class used for storing String serializations of
+ * enumerations.
+ */
+public final class EnumValues
+{
+    /**
+     * @since 2.2
+     */
+    private final Class<Enum<?>> _enumClass;
+    
+    /**
+     * Since 1.7, we are storing values as SerializedStrings, to further
+     * speed up serialization.
+     */
+    private final EnumMap<?,SerializedString> _values;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private EnumValues(Class<Enum<?>> enumClass, Map<Enum<?>,SerializedString> v) {
+        _enumClass = enumClass;
+        _values = new EnumMap(v);
+    }
+
+    public static EnumValues construct(Class<Enum<?>> enumClass, AnnotationIntrospector intr)
+    {
+        return constructFromName(enumClass, intr);
+    }
+
+    public static EnumValues constructFromName(Class<Enum<?>> enumClass, AnnotationIntrospector intr)
+    {
+        /* [JACKSON-214]: Enum types with per-instance sub-classes
+         *   need special handling
+         */
+        Class<? extends Enum<?>> cls = ClassUtil.findEnumType(enumClass);
+        Enum<?>[] values = cls.getEnumConstants();
+        if (values != null) {
+            // Type juggling... unfortunate
+            Map<Enum<?>,SerializedString> map = new HashMap<Enum<?>,SerializedString>();
+            for (Enum<?> en : values) {
+                String value = intr.findEnumValue(en);
+                map.put(en, new SerializedString(value));
+            }
+            return new EnumValues(enumClass, map);
+        }
+        throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName());
+    }
+
+    public static EnumValues constructFromToString(Class<Enum<?>> enumClass, AnnotationIntrospector intr)
+    {
+        Class<? extends Enum<?>> cls = ClassUtil.findEnumType(enumClass);
+        Enum<?>[] values = cls.getEnumConstants();
+        if (values != null) {
+            // Type juggling... unfortunate
+            Map<Enum<?>,SerializedString> map = new HashMap<Enum<?>,SerializedString>();
+            for (Enum<?> en : values) {
+                map.put(en, new SerializedString(en.toString()));
+            }
+            return new EnumValues(enumClass, map);
+        }
+        throw new IllegalArgumentException("Can not determine enum constants for Class "+enumClass.getName());
+    }
+
+    public SerializedString serializedValueFor(Enum<?> key)
+    {
+        return _values.get(key);
+    }
+    
+    public Collection<SerializedString> values() {
+        return _values.values();
+    }
+
+    /**
+     * Method used for serialization and introspection by core Jackson
+     * code.
+     * 
+     * @since 2.1
+     */
+    public EnumMap<?,SerializedString> internalMap() {
+        return _values;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public Class<Enum<?>> getEnumClass() {
+        return _enumClass;
+    }
+    
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java
new file mode 100644
index 0000000..8f240fc
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601DateFormat.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.text.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * Provide a fast thread-safe formatter/parser DateFormat for ISO8601 dates ONLY.
+ * It was mainly done to be used with Jackson JSON Processor.
+ * <p/>
+ * Watch out for clone implementation that returns itself.
+ * <p/>
+ * All other methods but parse and format and clone are undefined behavior.
+ *
+ * @see ISO8601Utils
+ */
+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;
+    }
+
+    @Override
+    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
+    {
+        String value = ISO8601Utils.format(date);
+        toAppendTo.append(value);
+        return toAppendTo;
+    }
+
+    @Override
+    public Date parse(String source, ParsePosition pos)
+    {
+        // index must be set to other than 0, I would swear this requirement is not there in
+        // some version of jdk 6.
+        pos.setIndex(source.length());
+        return ISO8601Utils.parse(source);
+    }
+
+    @Override
+    public Object clone() {
+        return this;    // jackson calls clone everytime. We are threadsafe so just returns the instance
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
new file mode 100644
index 0000000..293dd5e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
@@ -0,0 +1,254 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+/**
+ * Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than
+ * using SimpleDateFormat so highly suitable if you (un)serialize lots of date objects.
+ */
+public class ISO8601Utils {
+
+    /**
+     * ID to represent the 'GMT' string
+     */
+    private static final String GMT_ID = "GMT";
+
+    /**
+     * The GMT timezone
+     */
+    private static final TimeZone TIMEZONE_GMT = TimeZone.getTimeZone(GMT_ID);
+
+    /*
+    /**********************************************************
+    /* Static factories
+    /**********************************************************
+     */
+    
+    /**
+     * Accessor for static GMT timezone instance.
+     */
+    public static TimeZone timeZoneGMT() {
+        return TIMEZONE_GMT;
+    }
+
+    /*
+    /**********************************************************
+    /* Formatting
+    /**********************************************************
+     */
+    
+    /**
+     * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (GMT timezone, no milliseconds precision)
+     *
+     * @param date the date to format
+     * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
+     */
+    public static String format(Date date) {
+        return format(date, false, TIMEZONE_GMT);
+    }
+
+    /**
+     * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
+     *
+     * @param date   the date to format
+     * @param millis true to include millis precision otherwise false
+     * @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_GMT);
+    }
+
+    /**
+     * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     *
+     * @param date   the date to format
+     * @param millis true to include millis precision otherwise false
+     * @param tz     timezone to use for the formatting (GMT will produce 'Z')
+     * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     */
+    public static String format(Date date, boolean millis, TimeZone tz) {
+        Calendar calendar = new GregorianCalendar(tz, Locale.US);
+        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());
+        if (millis) {
+            formatted.append('.');
+            padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
+        }
+
+        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());
+        } else {
+            formatted.append('Z');
+        }
+
+        return formatted.toString();
+    }
+
+    /*
+    /**********************************************************
+    /* Parsing
+    /**********************************************************
+     */
+
+    /**
+     * Parse a date from ISO-8601 formatted string. It expects a format yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     *
+     * @param date ISO string to parse in the appropriate format.
+     * @return the parsed date
+     * @throws IllegalArgumentException if the date is not in the appropriate format
+     */
+    public static Date parse(String date) {
+        try {
+            int offset = 0;
+
+            // extract year
+            int year = parseInt(date, offset, offset += 4);
+            checkOffset(date, offset, '-');
+
+            // extract month
+            int month = parseInt(date, offset += 1, offset += 2);
+            checkOffset(date, offset, '-');
+
+            // extract day
+            int day = parseInt(date, offset += 1, offset += 2);
+            checkOffset(date, offset, 'T');
+
+            // extract hours, minutes, seconds and milliseconds
+            int hour = parseInt(date, offset += 1, offset += 2);
+            checkOffset(date, offset, ':');
+
+            int minutes = parseInt(date, offset += 1, offset += 2);
+            checkOffset(date, offset, ':');
+
+            int seconds = parseInt(date, offset += 1, offset += 2);
+            // milliseconds can be optional in the format
+            int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
+            if (date.charAt(offset) == '.') {
+                checkOffset(date, offset, '.');
+                milliseconds = parseInt(date, offset += 1, offset += 3);
+            }
+
+            // extract timezone
+            String timezoneId;
+            char timezoneIndicator = date.charAt(offset);
+            if (timezoneIndicator == '+' || timezoneIndicator == '-') {
+                timezoneId = GMT_ID + date.substring(offset);
+            } else if (timezoneIndicator == 'Z') {
+                timezoneId = GMT_ID;
+            } else {
+                throw new IndexOutOfBoundsException("Invalid time zone indicator " + timezoneIndicator);
+            }
+            TimeZone timezone = TimeZone.getTimeZone(timezoneId);
+            if (!timezone.getID().equals(timezoneId)) {
+                throw new IndexOutOfBoundsException();
+            }
+
+            Calendar calendar = new GregorianCalendar(timezone);
+            calendar.setLenient(false);
+            calendar.set(Calendar.YEAR, year);
+            calendar.set(Calendar.MONTH, month - 1);
+            calendar.set(Calendar.DAY_OF_MONTH, day);
+            calendar.set(Calendar.HOUR_OF_DAY, hour);
+            calendar.set(Calendar.MINUTE, minutes);
+            calendar.set(Calendar.SECOND, seconds);
+            calendar.set(Calendar.MILLISECOND, milliseconds);
+
+            return calendar.getTime();
+        } catch (IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException("Failed to parse date " + date, e);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("Failed to parse date " + date, e);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException("Failed to parse date " + date, e);
+        }
+    }
+
+    /**
+     * Check if the expected character exist at the given offset of the
+     *
+     * @param value    the string to check at the specified offset
+     * @param offset   the offset to look for the expected character
+     * @param expected the expected character
+     * @throws IndexOutOfBoundsException if the expected character is not found
+     */
+    private static void checkOffset(String value, int offset, char expected) throws IndexOutOfBoundsException {
+        char found = value.charAt(offset);
+        if (found != expected) {
+            throw new IndexOutOfBoundsException("Expected '" + expected + "' character but found '" + found + "'");
+        }
+    }
+
+    /**
+     * Parse an integer located between 2 given offsets in a string
+     *
+     * @param value      the string to parse
+     * @param beginIndex the start index for the integer in the string
+     * @param endIndex   the end index for the integer in the string
+     * @return the int
+     * @throws NumberFormatException if the value is not a number
+     */
+    private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException {
+        if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
+            throw new NumberFormatException(value);
+        }
+        // use same logic as in Integer.parseInt() but less generic we're not supporting negative values
+        int i = beginIndex;
+        int result = 0;
+        int digit;
+        if (i < endIndex) {
+            digit = Character.digit(value.charAt(i++), 10);
+            if (digit < 0) {
+                throw new NumberFormatException("Invalid number: " + value);
+            }
+            result = -digit;
+        }
+        while (i < endIndex) {
+            digit = Character.digit(value.charAt(i++), 10);
+            if (digit < 0) {
+                throw new NumberFormatException("Invalid number: " + value);
+            }
+            result *= 10;
+            result -= digit;
+        }
+        return -result;
+    }
+
+    /**
+     * 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);
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java b/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java
new file mode 100644
index 0000000..3379d40
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/JSONPObject.java
@@ -0,0 +1,92 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * Container class that can be used to wrap any Object instances (including
+ * nulls), and will serialize embedded in
+ * <a href="http://en.wikipedia.org/wiki/JSONP">JSONP</a> wrapping.
+ * 
+ * @see com.fasterxml.jackson.databind.util.JSONWrappedObject
+ * 
+ * @author tatu
+ */
+public class JSONPObject
+    implements JsonSerializable
+{
+    /**
+     * JSONP function name to use for serialization
+     */
+    protected final String _function;
+    
+    /**
+     * Value to be serialized as JSONP padded; can be null.
+     */
+    protected final Object _value;
+
+    /**
+     * Optional static type to use for serialization; if null, runtime
+     * type is used. Can be used to specify declared type which defines
+     * serializer to use, as well as aspects of extra type information
+     * to include (if any).
+     */
+    protected final JavaType _serializationType;
+
+    public JSONPObject(String function, Object value) {
+        this(function, value, (JavaType) null);
+    }
+
+    public JSONPObject(String function, Object value, JavaType asType)
+    {
+        _function = function;
+        _value = value;
+        _serializationType = asType;
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonSerializable(WithType) implementation
+    /**********************************************************
+     */
+
+    @Override
+    public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)
+            throws IOException, JsonProcessingException
+    {
+        // No type for JSONP wrapping: value serializer will handle typing for value:
+        serialize(jgen, provider);
+    }
+
+    @Override
+    public void serialize(JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+    {
+        // First, wrapping:
+        jgen.writeRaw(_function);
+        jgen.writeRaw('(');
+        if (_value == null) {
+            provider.defaultSerializeNull(jgen);
+        } else if (_serializationType != null) {
+            provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, jgen, provider);
+        } else {
+            Class<?> cls = _value.getClass();
+            provider.findTypedValueSerializer(cls, true, null).serialize(_value, jgen, provider);
+        }
+        jgen.writeRaw(')');
+    }
+
+    /*
+    /**************************************************************
+    /* Accessors
+    /**************************************************************
+     */
+    
+    public String getFunction() { return _function; }
+    public Object getValue() { return _value; }
+    public JavaType getSerializationType() { return _serializationType; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java
new file mode 100644
index 0000000..90bbdf8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/JSONWrappedObject.java
@@ -0,0 +1,107 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+
+/**
+ * General-purpose wrapper class that can be used to decorate serialized
+ * value with arbitrary literal prefix and suffix. This can be used for
+ * example to construct arbitrary Javascript values (similar to how basic
+ * function name and parenthesis are used with JSONP).
+ * 
+ * @see com.fasterxml.jackson.databind.util.JSONPObject
+ * 
+ * @author tatu
+ */
+public class JSONWrappedObject
+    implements JsonSerializable
+{
+    /**
+     * Literal String to output before serialized value.
+     * Will not be quoted when serializing value.
+     */
+    protected final String _prefix;
+
+    /**
+     * Literal String to output after serialized value.
+     * Will not be quoted when serializing value.
+     */
+    protected final String _suffix;
+    
+    /**
+     * Value to be serialized as JSONP padded; can be null.
+     */
+    protected final Object _value;
+
+    /**
+     * Optional static type to use for serialization; if null, runtime
+     * type is used. Can be used to specify declared type which defines
+     * serializer to use, as well as aspects of extra type information
+     * to include (if any).
+     */
+    protected final JavaType _serializationType;
+    
+    public JSONWrappedObject(String prefix, String suffix, Object value) {
+        this(prefix, suffix, value, (JavaType) null);
+    }
+
+    /**
+     * Constructor that should be used when specific serialization type to use
+     * is important, and needs to be passed instead of just using runtime
+     * (type-erased) type of the value.
+     */
+    public JSONWrappedObject(String prefix, String suffix, Object value, JavaType asType)
+    {
+        _prefix = prefix;
+        _suffix = suffix;
+        _value = value;
+        _serializationType = asType;
+    }
+    
+    /*
+    /**************************************************************
+    /* JsonSerializable(WithType) implementation
+    /**************************************************************
+     */
+
+    @Override
+    public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)
+            throws IOException, JsonProcessingException
+    {
+        // No type for JSONP wrapping: value serializer will handle typing for value:
+        serialize(jgen, provider);
+    }
+
+    @Override
+    public void serialize(JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+    {
+        // First, wrapping:
+    	if (_prefix != null) jgen.writeRaw(_prefix);
+        if (_value == null) {
+            provider.defaultSerializeNull(jgen);
+        } else if (_serializationType != null) {
+            provider.findTypedValueSerializer(_serializationType, true, null).serialize(_value, jgen, provider);
+        } else {
+            Class<?> cls = _value.getClass();
+            provider.findTypedValueSerializer(cls, true, null).serialize(_value, jgen, provider);
+        }
+        if (_suffix != null) jgen.writeRaw(_suffix);
+    }
+
+    /*
+    /**************************************************************
+    /* Accessors
+    /**************************************************************
+     */
+    
+    public String getPrefix() { return _prefix; }
+    public String getSuffix() { return _suffix; }
+    public Object getValue() { return _value; }
+    public JavaType getSerializationType() { return _serializationType; }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java b/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java
new file mode 100644
index 0000000..64247c9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/LRUMap.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.*;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Helper for simple bounded LRU maps used for reusing lookup values.
+ *<p>
+ * Note that serialization behavior is such that contents are NOT serialized,
+ * on assumption that all use cases are for caching where persistence
+ * does not make sense. The only thing serialized is the cache size of Map.
+ */
+public class LRUMap<K,V> extends LinkedHashMap<K,V>
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final int _maxEntries;
+    
+    public LRUMap(int initialEntries, int maxEntries)
+    {
+        super(initialEntries, 0.8f, true);
+        _maxEntries = maxEntries;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<K,V> eldest)
+    {
+        return size() > _maxEntries;
+    }
+
+    /*
+    /**********************************************************
+    /* Serializable overrides
+    /**********************************************************
+     */
+
+    /**
+     * Ugly hack, to work through the requirement that _value is indeed final,
+     * and that JDK serialization won't call ctor(s) if Serializable is implemented.
+     * 
+     * @since 2.1
+     */
+    protected transient int _jdkSerializeMaxEntries;
+
+    private void readObject(ObjectInputStream in) throws IOException {
+        _jdkSerializeMaxEntries = in.readInt();
+    }
+
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        out.writeInt(_jdkSerializeMaxEntries);
+    }
+
+    protected Object readResolve() {
+        return new LRUMap<Object,Object>(_jdkSerializeMaxEntries, _jdkSerializeMaxEntries);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java
new file mode 100644
index 0000000..8031bbd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/LinkedNode.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Node of a forward-only linked list.
+ * 
+ * @author tatu
+ *
+ * @param <T> Type of contained object
+ */
+public final class LinkedNode<T>
+{
+    final T _value;
+    final LinkedNode<T> _next;
+    
+    public LinkedNode(T value, LinkedNode<T> next)
+    {
+        _value = value;
+        _next = next;
+    }
+    
+    public LinkedNode<T> next() { return _next; }
+    
+    public T value() { return _value; }
+    
+    /**
+     * Convenience method that can be used to check if a linked list
+     * with given head node (which may be null to indicate empty list)
+     * contains given value
+     * 
+     * @param <ST> Type argument that defines contents of the linked list parameter
+     * @param node Head node of the linked list
+     * @param value Value to look for
+     * @return True if linked list contains the value, false otherwise
+     */
+    public static <ST> boolean contains(LinkedNode<ST> node, ST value)
+    {
+        while (node != null) {
+            if (node.value() == value) {
+                return true;
+            }
+            node = node.next();
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java b/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java
new file mode 100644
index 0000000..79767a9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/NameTransformer.java
@@ -0,0 +1,135 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Helper class used to encapsulate details of name mangling, transforming
+ * of names using different strategies (prefixes, suffixes).
+ * Default implementation is "no-operation" (aka identity transformation).
+ */
+public abstract class NameTransformer
+{
+    /**
+     * Singleton "no-operation" transformer which simply returns given
+     * name as is. Used commonly as placeholder or marker.
+     */
+    public final static NameTransformer NOP = new NameTransformer() {
+        @Override
+        public String transform(String name) {
+            return name;
+        }
+        @Override
+        public String reverse(String transformed) {
+            // identity transformation is always reversible:
+            return transformed;
+        }
+    };
+
+    protected NameTransformer() { }
+    
+    /**
+     * Factory method for constructing a simple transformer based on
+     * prefix and/or suffix.
+     */
+    public static NameTransformer simpleTransformer(final String prefix, final String suffix)
+    {
+        boolean hasPrefix = (prefix != null) && (prefix.length() > 0);
+        boolean hasSuffix = (suffix != null) && (suffix.length() > 0);
+
+        if (hasPrefix) {
+            if (hasSuffix) {
+                return new NameTransformer() {
+                    @Override
+                    public String transform(String name) { return prefix + name + suffix; }
+                    @Override
+                    public String reverse(String transformed) {
+                        if (transformed.startsWith(prefix)) {
+                            String str = transformed.substring(prefix.length());
+                            if (str.endsWith(suffix)) {
+                                return str.substring(0, str.length() - suffix.length());
+                            }
+                        }
+                        return null;
+                    }
+                    @Override
+                    public String toString() { return "[PreAndSuffixTransformer('"+prefix+"','"+suffix+"')]"; }
+                };
+            }
+            return new NameTransformer() {
+                @Override
+                public String transform(String name) { return prefix + name; }
+                @Override
+                public String reverse(String transformed) {
+                    if (transformed.startsWith(prefix)) {
+                        return transformed.substring(prefix.length());
+                    }
+                    return null;
+                }
+                @Override
+                public String toString() { return "[PrefixTransformer('"+prefix+"')]"; }
+            };
+        }
+        if (hasSuffix) {
+            return new NameTransformer() {
+                @Override
+                public String transform(String name) { return name + suffix; }
+                @Override
+                public String reverse(String transformed) {
+                    if (transformed.endsWith(suffix)) {
+                        return transformed.substring(0, transformed.length() - suffix.length());
+                    }
+                    return null;
+                }
+                @Override
+                public String toString() { return "[SuffixTransformer('"+suffix+"')]"; }
+            };
+        }
+        return NOP;
+    }
+
+    /**
+     * Method that constructs transformer that applies given transformers
+     * as a sequence; essentially combines separate transform operations
+     * into one logical transformation.
+     */
+    public static NameTransformer chainedTransformer(NameTransformer t1, NameTransformer t2) {
+        return new Chained(t1, t2);
+    }
+    
+    /**
+     * Method called when (forward) transformation is needed.
+     */
+    public abstract String transform(String name);
+
+    /**
+     * Method called when reversal of transformation is needed; should return
+     * null if this is not possible, that is, given name can not have been
+     * result of calling {@link #transform} of this object.
+     */
+    public abstract String reverse(String transformed);
+
+    public static class Chained extends NameTransformer
+    {
+        protected final NameTransformer _t1, _t2;
+        
+        public Chained(NameTransformer t1, NameTransformer t2) {
+            _t1 = t1;
+            _t2 = t2;
+        }
+
+        @Override
+        public String transform(String name) {
+            return _t1.transform(_t2.transform(name));
+        }
+
+        @Override
+        public String reverse(String transformed) {
+            transformed = _t1.reverse(transformed);
+            if (transformed != null) {
+                transformed = _t2.reverse(transformed);
+            }
+            return transformed;
+        }
+
+        @Override
+        public String toString() { return "[ChainedTransformer("+_t1+", "+_t2+")]"; }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Named.java b/src/main/java/com/fasterxml/jackson/databind/util/Named.java
new file mode 100644
index 0000000..b3f9eb0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Named.java
@@ -0,0 +1,8 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Simple tag interface mostly to allow sorting by name.
+ */
+public interface Named {
+    public String getName();
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java
new file mode 100644
index 0000000..9115881
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ObjectBuffer.java
@@ -0,0 +1,252 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.lang.reflect.Array;
+import java.util.List;
+
+/**
+ * Helper class to use for constructing Object arrays by appending entries
+ * to create arrays of various lengths (length that is not known a priori). 
+ */
+public final class ObjectBuffer
+{
+    // // // Config constants
+
+    /**
+     * Let's start with small chunks; typical usage is for small arrays anyway.
+     */
+    final static int INITIAL_CHUNK_SIZE = 12;
+
+    /**
+     * Also: let's expand by doubling up until 64k chunks (which is 16k entries for
+     * 32-bit machines)
+     */
+    final static int SMALL_CHUNK_SIZE = (1 << 14);
+
+    /**
+     * Let's limit maximum size of chunks we use; helps avoid excessive allocation
+     * overhead for huge data sets.
+     * For now, let's limit to quarter million entries, 1 meg chunks for 32-bit
+     * machines.
+     */
+    final static int MAX_CHUNK_SIZE = (1 << 18);
+
+    // // // Data storage
+
+    private Node _bufferHead;
+
+    private Node _bufferTail;
+
+    /**
+     * Number of total buffered entries in this buffer, counting all instances
+     * within linked list formed by following {@link #_bufferHead}.
+     */
+    private int _bufferedEntryCount;
+
+    // // // Simple reuse
+
+    /**
+     * Reusable Object array, stored here after buffer has been released having
+     * been used previously.
+     */
+    private Object[] _freeBuffer;
+
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    public ObjectBuffer() { }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method called to start buffering process. Will ensure that the buffer
+     * is empty, and then return an object array to start chunking content on
+     */
+    public Object[] resetAndStart()
+    {
+        _reset();
+        if (_freeBuffer == null) {
+            return new Object[INITIAL_CHUNK_SIZE];
+        }
+        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
+     * buffering.
+     *
+     * @param fullChunk Completed chunk that the caller is requesting
+     *   to append to this buffer. It is generally chunk that was
+     *   returned by an earlier call to {@link #resetAndStart} or
+     *   {@link #appendCompletedChunk} (although this is not required or
+     *   enforced)
+     *
+     * @return New chunk buffer for caller to fill
+     */
+    public Object[] appendCompletedChunk(Object[] fullChunk)
+    {
+        Node next = new Node(fullChunk);
+        if (_bufferHead == null) { // first chunk
+            _bufferHead = _bufferTail = next;
+        } else { // have something already
+            _bufferTail.linkNext(next);
+            _bufferTail = next;
+        }
+        int len = fullChunk.length;
+        _bufferedEntryCount += len;
+        // double the size for small chunks
+        if (len < SMALL_CHUNK_SIZE) {
+            len += len;
+        } else { // but by +25% for larger (to limit overhead)
+            len += (len >> 2);
+        }
+        return new Object[len];
+    }
+
+    /**
+     * Method called to indicate that the buffering process is now
+     * complete; and to construct a combined exactly-sized result
+     * array. Additionally the buffer itself will be reset to
+     * reduce memory retention.
+     *<p>
+     * Resulting array will be of generic <code>Object[]</code> type:
+     * if a typed array is needed, use the method with additional
+     * type argument.
+     */
+    public Object[] completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries)
+    {
+        int totalSize = lastChunkEntries + _bufferedEntryCount;
+        Object[] result = new Object[totalSize];
+        _copyTo(result, totalSize, lastChunk, lastChunkEntries);
+        return result;
+    }
+
+    /**
+     * Type-safe alternative to
+     * {@link #completeAndClearBuffer(Object[], int)}, to allow
+     * for constructing explicitly typed result array.
+     *
+     * @param componentType Type of elements included in the buffer. Will be
+     *   used for constructing the result array.
+     */
+    public <T> T[] completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries, Class<T> componentType)
+    {
+       int totalSize = lastChunkEntries + _bufferedEntryCount;
+ 	   @SuppressWarnings("unchecked")
+        T[] result = (T[]) Array.newInstance(componentType, totalSize);
+        _copyTo(result, totalSize, lastChunk, lastChunkEntries);
+        _reset();
+        return result;
+    }
+
+    public void completeAndClearBuffer(Object[] lastChunk, int lastChunkEntries, List<Object> resultList)
+    {
+        for (Node n = _bufferHead; n != null; n = n.next()) {
+            Object[] curr = n.getData();
+            for (int i = 0, len = curr.length; i < len; ++i) {
+                resultList.add(curr[i]);
+            }
+        }
+        // and then the last one
+        for (int i = 0; i < lastChunkEntries; ++i) {
+            resultList.add(lastChunk[i]);
+        }
+    }
+    
+    /**
+     * Helper method that can be used to check how much free capacity
+     * will this instance start with. Can be used to choose the best
+     * instance to reuse, based on size of reusable object chunk
+     * buffer holds reference to.
+     */
+    public int initialCapacity()
+    {
+        return (_freeBuffer == null) ? 0 : _freeBuffer.length;
+    }
+
+    /**
+     * Method that can be used to check how many Objects have been buffered
+     * within this buffer.
+     */
+    public int bufferedSize() { return _bufferedEntryCount; }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected void _reset()
+    {
+        // can we reuse the last (and thereby biggest) array for next time?
+        if (_bufferTail != null) {
+            _freeBuffer = _bufferTail.getData();
+        }
+        // either way, must discard current contents
+        _bufferHead = _bufferTail = null;
+        _bufferedEntryCount = 0;
+    }
+
+    protected final void _copyTo(Object resultArray, int totalSize,
+                                 Object[] lastChunk, int lastChunkEntries)
+    {
+        int ptr = 0;
+
+        for (Node n = _bufferHead; n != null; n = n.next()) {
+            Object[] curr = n.getData();
+            int len = curr.length;
+            System.arraycopy(curr, 0, resultArray, ptr, len);
+            ptr += len;
+        }
+        System.arraycopy(lastChunk, 0, resultArray, ptr, lastChunkEntries);
+        ptr += lastChunkEntries;
+
+        // sanity check (could have failed earlier due to out-of-bounds, too)
+        if (ptr != totalSize) {
+            throw new IllegalStateException("Should have gotten "+totalSize+" entries, got "+ptr);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Helper class used to store actual data, in a linked list.
+     */
+    final static class Node
+    {
+        /**
+         * Data stored in this node. Array is considered to be full.
+         */
+        final Object[] _data;
+
+        Node _next;
+
+        public Node(Object[] data) {
+            _data = data;
+        }
+
+        public Object[] getData() { return _data; }
+
+        public Node next() { return _next; }
+
+        public void linkNext(Node next)
+        {
+            if (_next != null) { // sanity check
+                throw new IllegalStateException();
+            }
+            _next = next;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java b/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java
new file mode 100644
index 0000000..0d558b7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ObjectIdMap.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.IdentityHashMap;
+
+/**
+ * Map used during serialization, to keep track of referable Objects
+ * along with lazily evaluated ids.
+ */
+ at SuppressWarnings("serial")
+public class ObjectIdMap extends IdentityHashMap<Object,Object>
+{
+    public ObjectIdMap()
+    {
+        super(16);
+    }
+
+    /*
+    /**********************************************************
+    /* API
+    /**********************************************************
+     */
+
+    /**
+     * Method that is called to figure out whether we have already
+     * seen given POJO: if yes, we will return its id (first looking
+     * it up as necessary); if not, we will mark down that we have
+     * seen it but return null.
+     */
+    public Object findId(Object pojo)
+    {
+        return get(pojo);
+    }
+    
+    public void insertId(Object pojo, Object id)
+    {
+        put(pojo, id);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java b/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java
new file mode 100644
index 0000000..468f392
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/PrimitiveArrayBuilder.java
@@ -0,0 +1,180 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Base class for specialized primitive array builders.
+ */
+public abstract class PrimitiveArrayBuilder<T>
+{
+    /**
+     * Let's start with small chunks; typical usage is for small arrays anyway.
+     */
+    final static int INITIAL_CHUNK_SIZE = 12;
+
+    /**
+     * Also: let's expand by doubling up until 64k chunks (which is 16k entries for
+     * 32-bit machines)
+     */
+    final static int SMALL_CHUNK_SIZE = (1 << 14);
+
+    /**
+     * Let's limit maximum size of chunks we use; helps avoid excessive allocation
+     * overhead for huge data sets.
+     * For now, let's limit to quarter million entries, 1 meg chunks for 32-bit
+     * machines.
+     */
+    final static int MAX_CHUNK_SIZE = (1 << 18);
+
+    // // // Data storage
+
+    protected T _freeBuffer;
+
+    protected Node<T> _bufferHead;
+
+    protected Node<T> _bufferTail;
+
+    /**
+     * Number of total buffered entries in this buffer, counting all instances
+     * within linked list formed by following {@link #_bufferHead}.
+     */
+    protected int _bufferedEntryCount;
+
+    // // // Recycled instances of sub-classes
+
+    // // // Life-cycle
+
+    protected PrimitiveArrayBuilder() { }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    public T resetAndStart()
+    {
+        _reset();
+        return (_freeBuffer == null) ?
+            _constructArray(INITIAL_CHUNK_SIZE) : _freeBuffer;
+    }
+
+    /**
+     * @return Length of the next chunk to allocate
+     */
+    public final T appendCompletedChunk(T fullChunk, int fullChunkLength)
+    {
+        Node<T> next = new Node<T>(fullChunk, fullChunkLength);
+        if (_bufferHead == null) { // first chunk
+            _bufferHead = _bufferTail = next;
+        } else { // have something already
+            _bufferTail.linkNext(next);
+            _bufferTail = next;
+        }
+        _bufferedEntryCount += fullChunkLength;
+        int nextLen = fullChunkLength; // start with last chunk size
+        // double the size for small chunks
+        if (nextLen < SMALL_CHUNK_SIZE) {
+            nextLen += nextLen;
+        } else { // but by +25% for larger (to limit overhead)
+            nextLen += (nextLen >> 2);
+        }
+        return _constructArray(nextLen);
+    }
+
+    public T completeAndClearBuffer(T lastChunk, int lastChunkEntries)
+    {
+        int totalSize = lastChunkEntries + _bufferedEntryCount;
+        T resultArray = _constructArray(totalSize);
+
+        int ptr = 0;
+
+        for (Node<T> n = _bufferHead; n != null; n = n.next()) {
+            ptr = n.copyData(resultArray, ptr);
+        }
+        System.arraycopy(lastChunk, 0, resultArray, ptr, lastChunkEntries);
+        ptr += lastChunkEntries;
+
+        // sanity check (could have failed earlier due to out-of-bounds, too)
+        if (ptr != totalSize) {
+            throw new IllegalStateException("Should have gotten "+totalSize+" entries, got "+ptr);
+        }
+        return resultArray;
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract methods for sub-classes to implement
+    /**********************************************************
+     */
+
+    protected abstract T _constructArray(int len);
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected void _reset()
+    {
+        // can we reuse the last (and thereby biggest) array for next time?
+        if (_bufferTail != null) {
+            _freeBuffer = _bufferTail.getData();
+        }
+        // either way, must discard current contents
+        _bufferHead = _bufferTail = null;
+        _bufferedEntryCount = 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * For actual buffering beyond the current buffer, we can actually
+     * use shared class which only deals with opaque "untyped" chunks.
+     * This works because {@link java.lang.System#arraycopy} does not
+     * take type; hence we can implement some aspects of primitive data
+     * handling in generic fashion.
+     */
+    final static class Node<T>
+    {
+        /**
+         * Data stored in this node.
+         */
+        final T _data;
+
+        /**
+         * Number entries in the (untyped) array. Offset is assumed to be 0.
+         */
+        final int _dataLength;
+
+        Node<T> _next;
+
+        public Node(T data, int dataLen)
+        {
+            _data = data;
+            _dataLength = dataLen;
+        }
+
+        public T getData() { return _data; }
+
+        public int copyData(T dst, int ptr)
+        {
+            System.arraycopy(_data, 0, dst, ptr, _dataLength);
+            ptr += _dataLength;
+            return ptr;
+        }
+
+        public Node<T> next() { return _next; }
+
+        public void linkNext(Node<T> next)
+        {
+            if (_next != null) { // sanity check
+                throw new IllegalStateException();
+            }
+            _next = next;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/Provider.java b/src/main/java/com/fasterxml/jackson/databind/util/Provider.java
new file mode 100644
index 0000000..075748c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/Provider.java
@@ -0,0 +1,24 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+/**
+* Simple helper class used for decoupling instantiation of
+* optionally loaded handlers, like deserializers and deserializers
+* for libraries that are only present on some platforms.
+ * 
+ * @author tatu
+ *
+ * @param <T> Type of objects provided
+ * 
+ * @since 2.2 Not really needed that much, will be eventually removed
+ */
+ at Deprecated
+public interface Provider<T>
+{
+    /**
+     * Method used to request provider to provide entries it has
+     */
+    public Collection<T> provide();
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java b/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java
new file mode 100644
index 0000000..b1c347e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/RootNameLookup.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.core.io.SerializedString;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.type.ClassKey;
+
+/**
+ * Helper class for caching resolved root names.
+ */
+public class RootNameLookup
+    implements java.io.Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * For efficient operation, let's try to minimize number of times we
+     * need to introspect root element name to use.
+     */
+    protected LRUMap<ClassKey,SerializedString> _rootNames;
+
+    public RootNameLookup() { }
+
+    public SerializedString findRootName(JavaType rootType, MapperConfig<?> config)
+    {
+        return findRootName(rootType.getRawClass(), config);
+    }
+
+    public synchronized SerializedString findRootName(Class<?> rootType, MapperConfig<?> config)
+    {
+        ClassKey key = new ClassKey(rootType);
+
+        if (_rootNames == null) {
+            _rootNames = new LRUMap<ClassKey,SerializedString>(20, 200);
+        } else {
+            SerializedString name = _rootNames.get(key);
+            if (name != null) {
+                return name;
+            }
+        }
+        BeanDescription beanDesc = config.introspectClassAnnotations(rootType);
+        AnnotationIntrospector intr = config.getAnnotationIntrospector();
+        AnnotatedClass ac = beanDesc.getClassInfo();
+        PropertyName pname = intr.findRootName(ac);
+        String nameStr;
+        // No answer so far? Let's just default to using simple class name
+        if (pname == null || !pname.hasSimpleName()) {
+            // Should we strip out enclosing class tho? For now, nope:
+            nameStr = rootType.getSimpleName();
+        } else {
+            nameStr = pname.getSimpleName();
+        }
+        SerializedString name = new SerializedString(nameStr);
+        _rootNames.put(key, name);
+        return name;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
new file mode 100644
index 0000000..d77efa2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/SimpleBeanPropertyDefinition.java
@@ -0,0 +1,201 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.PropertyName;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.*;
+
+/**
+ * Simple immutable {@link BeanPropertyDefinition} implementation that can
+ * be wrapped around a {@link AnnotatedMember} that is a simple
+ * accessor (getter) or mutator (setter, constructor parameter)
+ * (or both, for fields).
+ */
+public class SimpleBeanPropertyDefinition
+    extends BeanPropertyDefinition
+{
+	protected final AnnotationIntrospector _introspector;
+	
+    /**
+     * Member that defines logical property. Assumption is that it
+     * should be a 'simple' accessor; meaning a zero-argument getter,
+     * single-argument setter or constructor parameter.
+     */
+    protected final AnnotatedMember _member;
+
+    protected final String _name;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+
+    /**
+     * @since 2.2 Use {@link #construct} instead
+     */
+    @Deprecated
+    public SimpleBeanPropertyDefinition(AnnotatedMember member) {
+    	this(member, member.getName(), null);
+    }
+
+    /**
+     * @since 2.2 Use {@link #construct} instead
+     */
+    @Deprecated
+    public SimpleBeanPropertyDefinition(AnnotatedMember member, String name) {
+    	this(member, name, null);
+    }
+    
+    private SimpleBeanPropertyDefinition(AnnotatedMember member, String name,
+    		AnnotationIntrospector intr) {
+        _introspector = intr;
+        _member = member;
+        _name = name;
+    }
+
+    /**
+     * @since 2.2
+     */
+    public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
+    		AnnotatedMember member) {
+        return new SimpleBeanPropertyDefinition(member, member.getName(),
+                (config == null) ? null : config.getAnnotationIntrospector());
+    }
+    
+    /**
+     * @since 2.2
+     */
+    public static SimpleBeanPropertyDefinition construct(MapperConfig<?> config,
+    		AnnotatedMember member, String name) {
+        return new SimpleBeanPropertyDefinition(member, name,
+                (config == null) ? null : config.getAnnotationIntrospector());
+    }
+    
+    /*
+    /**********************************************************
+    /* Fluent factories
+    /**********************************************************
+     */
+
+    @Override
+    public SimpleBeanPropertyDefinition withName(String newName) {
+        if (_name.equals(newName)) {
+            return this;
+        }
+        return new SimpleBeanPropertyDefinition(_member, newName, _introspector);
+    }
+    
+    /*
+    /**********************************************************
+    /* Basic property information, name, type
+    /**********************************************************
+     */
+
+    @Override
+    public String getName() { return _name; }
+
+    @Override
+    public String getInternalName() { return getName(); }
+
+    @Override
+    public PropertyName getWrapperName() {
+    	return (_introspector == null) ? null : _introspector.findWrapperName(_member);
+    }
+    
+    // hmmh. what should we claim here?
+    @Override
+    public boolean isExplicitlyIncluded() { return false; }
+    
+    /*
+    /**********************************************************
+    /* Access to accessors (fields, methods etc)
+    /**********************************************************
+     */
+
+    @Override
+    public boolean hasGetter() {
+        return (getGetter() != null);
+    }
+
+    @Override
+    public boolean hasSetter() {
+        return (getSetter() != null);
+    }
+
+    @Override
+    public boolean hasField() {
+        return (_member instanceof AnnotatedField);
+    }
+
+    @Override
+    public boolean hasConstructorParameter() {
+        return (_member instanceof AnnotatedParameter);
+    }
+    
+    @Override
+    public AnnotatedMethod getGetter() {
+        if ((_member instanceof AnnotatedMethod)
+                && ((AnnotatedMethod) _member).getParameterCount() == 0) {
+            return (AnnotatedMethod) _member;
+        }
+        return null;
+    }
+        
+    @Override
+    public AnnotatedMethod getSetter() {
+        if ((_member instanceof AnnotatedMethod)
+                && ((AnnotatedMethod) _member).getParameterCount() == 1) {
+            return (AnnotatedMethod) _member;
+        }
+        return null;
+    }
+
+    @Override
+    public AnnotatedField getField() {
+        return (_member instanceof AnnotatedField) ?
+                (AnnotatedField) _member : null;
+    }
+
+    @Override
+    public AnnotatedParameter getConstructorParameter() {
+        return (_member instanceof AnnotatedParameter) ?
+                (AnnotatedParameter) _member : null;
+    }
+
+    /**
+     * 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 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
new file mode 100644
index 0000000..1e280c3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/StdConverter.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.databind.util;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Standard implementation of {@link Converter} that supports explicit
+ * type access, instead of relying type detection of generic type
+ * parameters. 
+ * 
+ * @since 2.2
+ */
+public abstract class StdConverter<IN,OUT>
+    implements Converter<IN,OUT>
+{
+    /*
+    /**********************************************************
+    /* Partial Converter API implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public abstract OUT convert(IN value);
+
+    @Override
+    public JavaType getInputType(TypeFactory typeFactory)
+    {
+        JavaType[] types = typeFactory.findTypeParameters(getClass(), Converter.class);
+        if (types == null || types.length < 2) {
+            throw new IllegalStateException("Can not find OUT type parameter for Converter of type "+getClass().getName());
+        }
+        return types[0];
+    }
+
+    @Override
+    public JavaType getOutputType(TypeFactory typeFactory)
+    {
+        JavaType[] types = typeFactory.findTypeParameters(getClass(), Converter.class);
+        if (types == null || types.length < 2) {
+            throw new IllegalStateException("Can not find OUT type parameter for Converter of type "+getClass().getName());
+        }
+        return types[1];
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java
new file mode 100644
index 0000000..0fee254
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java
@@ -0,0 +1,400 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.core.io.NumberInput;
+
+/**
+ * Default {@link DateFormat} implementation used by standard Date
+ * serializers and deserializers. For serialization defaults to using
+ * an ISO-8601 compliant format (format String "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ * and for deserialization, both ISO-8601 and RFC-1123.
+ */
+ at SuppressWarnings("serial")
+public class StdDateFormat
+    extends DateFormat
+{
+    /* TODO !!! 24-Nov-2009, tatu: Need to 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.
+     */
+    // Note: [JACKSON-697] is the issue for rewrite
+
+    /**
+     * Defines a commonly used date format that conforms
+     * to ISO-8601 date formatting standard, when it includes basic undecorated
+     * timezone definition
+     */
+    protected 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 "GMT")
+     */
+    protected final static String DATE_FORMAT_STR_ISO8601_Z = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+
+    /**
+     * ISO-8601 with just the Date part, no time
+     */
+    protected final static String DATE_FORMAT_STR_PLAIN = "yyyy-MM-dd";
+
+    /**
+     * This constant defines the date format specified by
+     * RFC 1123 / RFC 822.
+     */
+    protected final static String DATE_FORMAT_STR_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
+
+    /**
+     * For error messages we'll also need a list of all formats.
+     */
+    protected final static String[] ALL_FORMATS = new String[] {
+        DATE_FORMAT_STR_ISO8601,
+        DATE_FORMAT_STR_ISO8601_Z,
+        DATE_FORMAT_STR_RFC1123,
+        DATE_FORMAT_STR_PLAIN
+    };
+
+    /**
+     * By default we use GMT for everything.
+     */
+    private final static TimeZone DEFAULT_TIMEZONE;
+    static {
+        DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT");
+    }
+    
+    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_PLAIN;
+
+    /* Let's construct "blueprint" date format instances: can not 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 GMT for
+         * baseline DataFormat objects
+         */
+        DATE_FORMAT_RFC1123 = new SimpleDateFormat(DATE_FORMAT_STR_RFC1123, Locale.US);
+        DATE_FORMAT_RFC1123.setTimeZone(DEFAULT_TIMEZONE);
+        DATE_FORMAT_ISO8601 = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601);
+        DATE_FORMAT_ISO8601.setTimeZone(DEFAULT_TIMEZONE);
+        DATE_FORMAT_ISO8601_Z = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601_Z);
+        DATE_FORMAT_ISO8601_Z.setTimeZone(DEFAULT_TIMEZONE);
+        DATE_FORMAT_PLAIN = new SimpleDateFormat(DATE_FORMAT_STR_PLAIN);
+        DATE_FORMAT_PLAIN.setTimeZone(DEFAULT_TIMEZONE);
+    }
+    
+    /**
+     * A singleton instance can be used for cloning purposes.
+     */
+    public final static StdDateFormat instance = new StdDateFormat();
+    
+    /**
+     * Caller may want to explicitly override timezone to use; if so,
+     * we will have non-null value here.
+     */
+    protected transient TimeZone _timezone;
+    
+    protected transient DateFormat _formatRFC1123;
+    protected transient DateFormat _formatISO8601;
+    protected transient DateFormat _formatISO8601_z;
+    protected transient DateFormat _formatPlain;
+
+    /*
+    /**********************************************************
+    /* Life cycle, accessing singleton "standard" formats
+    /**********************************************************
+     */
+
+    public StdDateFormat() { }
+    public StdDateFormat(TimeZone tz) {
+        _timezone = tz;
+    }
+
+    public static TimeZone getDefaultTimeZone() {
+        return DEFAULT_TIMEZONE;
+    }
+    
+    /**
+     * Method used for creating a new instance with specified timezone;
+     * if no timezone specified, defaults to the default timezone (UTC).
+     */
+    public StdDateFormat withTimeZone(TimeZone tz) {
+        if (tz == null) {
+            tz = DEFAULT_TIMEZONE;
+        }
+        return new StdDateFormat(tz);
+    }
+    
+    @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();
+    }
+
+    /**
+     * Method for getting the globally shared DateFormat instance
+     * that uses GMT timezone and can handle simple ISO-8601
+     * compliant date format.
+     */
+    public static DateFormat getBlueprintISO8601Format() {
+        return DATE_FORMAT_ISO8601;
+    }
+
+    /**
+     * Method for getting a non-shared DateFormat instance
+     * that uses specified timezone and can handle simple ISO-8601
+     * compliant date format.
+     */
+    public static DateFormat getISO8601Format(TimeZone tz) {
+        return _cloneFormat(DATE_FORMAT_ISO8601, tz);
+    }
+
+    /**
+     * Method for getting the globally shared DateFormat instance
+     * that uses GMT timezone and can handle RFC-1123
+     * compliant date format.
+     */
+    public static DateFormat getBlueprintRFC1123Format() {
+        return DATE_FORMAT_RFC1123;
+    }
+
+
+    /**
+     * Method for getting a non-shared DateFormat instance
+     * that uses specific timezone and can handle RFC-1123
+     * compliant date format.
+     */
+    public static DateFormat getRFC1123Format(TimeZone tz) {
+        return _cloneFormat(DATE_FORMAT_RFC1123, tz);
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    @Override
+    public void setTimeZone(TimeZone tz)
+    {
+        /* DateFormats are timezone-specific (via Calendar contained),
+         * so need to reset instances if timezone changes:
+         */
+        if (tz != _timezone) {
+            _formatRFC1123 = null;
+            _formatISO8601 = null;
+            _formatISO8601_z = null;
+            _formatPlain = null;
+            _timezone = tz;
+        }
+    }
+    
+    @Override
+    public Date parse(String dateStr) throws ParseException
+    {
+        dateStr = dateStr.trim();
+        ParsePosition pos = new ParsePosition(0);
+        Date result = parse(dateStr, pos);
+        if (result != null) {
+            return result;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (String f : ALL_FORMATS) {
+            if (sb.length() > 0) {
+                sb.append("\", \"");
+            } else {
+                sb.append('"');
+            }
+            sb.append(f);
+        }
+        sb.append('"');
+        throw new ParseException
+            (String.format("Can not parse date \"%s\": not compatible with any of standard forms (%s)",
+                           dateStr, sb.toString()), pos.getErrorIndex());
+    }
+
+    @Override
+    public Date parse(String dateStr, ParsePosition pos)
+    {
+        if (looksLikeISO8601(dateStr)) { // also includes "plain"
+            return parseAsISO8601(dateStr, pos);
+        }
+        /* 14-Feb-2010, tatu: As per [JACKSON-236], better also
+         *   consider "stringified" simple time stamp
+         */
+        int i = dateStr.length();
+        while (--i >= 0) {
+            char ch = dateStr.charAt(i);
+            if (ch < '0' || ch > '9') break;
+        }
+        if (i < 0) { // all digits
+            if (NumberInput.inLongRange(dateStr, false)) {
+                return new Date(Long.parseLong(dateStr));
+            }
+        }
+        // Otherwise, fall back to using RFC 1123
+        return parseAsRFC1123(dateStr, pos);
+    }
+
+    @Override
+    public StringBuffer format(Date date, StringBuffer toAppendTo,
+            FieldPosition fieldPosition)
+    {
+        if (_formatISO8601 == null) {
+            _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601);
+        }
+        return _formatISO8601.format(date, toAppendTo, fieldPosition);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Overridable helper method used to figure out which of supported
+     * formats is the likeliest match.
+     */
+    protected boolean looksLikeISO8601(String dateStr)
+    {
+        if (dateStr.length() >= 5
+            && Character.isDigit(dateStr.charAt(0))
+            && Character.isDigit(dateStr.charAt(3))
+            && dateStr.charAt(4) == '-'
+            ) {
+            return true;
+        }
+        return false;
+    }
+
+    protected Date parseAsISO8601(String dateStr, ParsePosition pos)
+    {
+        /* 21-May-2009, tatu: DateFormat has very strict handling of
+         * timezone  modifiers for ISO-8601. So we need to do some scrubbing.
+         */
+
+        /* First: do we have "zulu" format ('Z' == "GMT")? If yes, that's
+         * quite simple because we already set date format timezone to be
+         * GMT, and hence can just strip out 'Z' altogether
+         */
+        int len = dateStr.length();
+        char c = dateStr.charAt(len-1);
+        DateFormat df;
+
+        // [JACKSON-200]: need to support "plain" date...
+        if (len <= 10 && Character.isDigit(c)) {
+           df = _formatPlain;
+            if (df == null) {
+                df = _formatPlain = _cloneFormat(DATE_FORMAT_PLAIN);
+            }
+        } else if (c == 'Z') {
+            df = _formatISO8601_z;
+            if (df == null) {
+                df = _formatISO8601_z = _cloneFormat(DATE_FORMAT_ISO8601_Z);
+            }
+            // [JACKSON-334]: 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";
+                }
+                // [JACKSON-334]: may be missing milliseconds... if so, add
+                len = dateStr.length();
+                // '+0000' (5 chars); should come after '.000' (4 chars) of milliseconds, so:
+                c = dateStr.charAt(len-9);
+                if (Character.isDigit(c)) {
+                    StringBuilder sb = new StringBuilder(dateStr);
+                    sb.insert(len-5, ".000");
+                    dateStr = sb.toString();
+                }
+                
+                df = _formatISO8601;
+                if (_formatISO8601 == null) {
+                    df = _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601);
+                }
+            } else {
+                /* 24-Nov-2009, tatu: Ugh. This is getting pretty
+                 *   ugly. Need to rewrite soon!
+                 */
+
+                // If not, plain date. Easiest to just patch 'Z' in the end?
+                StringBuilder sb = new StringBuilder(dateStr);
+                // And possible also millisecond part if missing
+                int timeLen = len - dateStr.lastIndexOf('T') - 1;
+                if (timeLen <= 8) {
+                    sb.append(".000");
+                }
+                sb.append('Z');
+                dateStr = sb.toString();
+                df = _formatISO8601_z;
+                if (df == null) {
+                    df = _formatISO8601_z = _cloneFormat(DATE_FORMAT_ISO8601_Z);
+                }
+            }
+        }
+        return df.parse(dateStr, pos);
+    }
+
+    protected Date parseAsRFC1123(String dateStr, ParsePosition pos)
+    {
+        if (_formatRFC1123 == null) {
+            _formatRFC1123 = _cloneFormat(DATE_FORMAT_RFC1123);
+        }
+        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;
+    }
+
+    private final DateFormat _cloneFormat(DateFormat df) {
+        return _cloneFormat(df, _timezone);
+    }
+
+    private final static DateFormat _cloneFormat(DateFormat df, TimeZone tz)
+    {
+        df = (DateFormat) df.clone();
+        if (tz != null) {
+            df.setTimeZone(tz);
+        }
+        return df;
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
new file mode 100644
index 0000000..06bc401
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/TokenBuffer.java
@@ -0,0 +1,1368 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+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;
+
+/**
+ * Utility class used for efficient storage of {@link JsonToken}
+ * sequences, needed for temporary buffering.
+ * Space efficient for different sequence lengths (especially so for smaller
+ * ones; but not significantly less efficient for larger), highly efficient
+ * for linear iteration and appending. Implemented as segmented/chunked
+ * linked list of tokens; only modifications are via appends.
+ *<p>
+ * Note that before version 2.0, this class was located in the "core"
+ * bundle, not data-binding; but since it was only used by data binding,
+ * was moved here to reduce size of core package
+ */
+public class TokenBuffer
+/* Won't use JsonGeneratorBase, to minimize overhead for validity
+ * checking
+ */
+    extends JsonGenerator
+{
+    protected final static int DEFAULT_PARSER_FEATURES = JsonParser.Feature.collectDefaults();
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Object codec to use for stream-based object
+     * conversion through parser/generator interfaces. If null,
+     * such methods can not be used.
+     */
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Bit flag composed of bits that indicate which
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
+     * are enabled.
+     *<p>
+     * NOTE: most features have no effect on this class
+     */
+    protected int _generatorFeatures;
+
+    protected boolean _closed;
+    
+    /*
+    /**********************************************************
+    /* Token buffering state
+    /**********************************************************
+     */
+
+    /**
+     * First segment, for contents this buffer has
+     */
+    protected Segment _first;
+
+    /**
+     * Last segment of this buffer, one that is used
+     * for appending more tokens
+     */
+    protected Segment _last;
+    
+    /**
+     * Offset within last segment, 
+     */
+    protected int _appendOffset;
+
+    /*
+    /**********************************************************
+    /* Output state
+    /**********************************************************
+     */
+
+    protected JsonWriteContext _writeContext;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @param codec Object codec to use for stream-based object
+     *   conversion through parser/generator interfaces. If null,
+     *   such methods can not be used.
+     */
+    public TokenBuffer(ObjectCodec codec)
+    {
+        _objectCodec = codec;
+        _generatorFeatures = DEFAULT_PARSER_FEATURES;
+        _writeContext = JsonWriteContext.createRootContext();
+        // at first we have just one segment
+        _first = _last = new Segment();
+        _appendOffset = 0;
+    }
+
+    @Override
+    public Version version() {
+        return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+    }
+
+    /**
+     * Method used to create a {@link JsonParser} that can read contents
+     * stored in this buffer. Will use default <code>_objectCodec</code> for
+     * object conversions.
+     *<p>
+     * Note: instances are not synchronized, that is, they are not thread-safe
+     * if there are concurrent appends to the underlying buffer.
+     * 
+     * @return Parser that can be used for reading contents stored in this buffer
+     */
+    public JsonParser asParser()
+    {
+        return asParser(_objectCodec);
+    }
+
+    /**
+     * Method used to create a {@link JsonParser} that can read contents
+     * stored in this buffer.
+     *<p>
+     * Note: instances are not synchronized, that is, they are not thread-safe
+     * if there are concurrent appends to the underlying buffer.
+     *
+     * @param codec Object codec to use for stream-based object
+     *   conversion through parser/generator interfaces. If null,
+     *   such methods can not 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);
+    }
+
+    /**
+     * @param src Parser to use for accessing source information
+     *    like location, configured codec
+     */
+    public JsonParser asParser(JsonParser src)
+    {
+        Parser p = new Parser(_first, src.getCodec());
+        p.setLocation(src.getTokenLocation());
+        return p;
+    }
+    
+    /*
+    /**********************************************************
+    /* Additional accessors
+    /**********************************************************
+     */
+
+    public JsonToken firstToken() {
+        if (_first != null) {
+            return _first.type(0);
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Other custom methods not needed for implementing interfaces
+    /**********************************************************
+     */
+
+    /**
+     * Helper method that will append contents of given buffer into this
+     * buffer.
+     * Not particularly optimized; can be made faster if there is need.
+     * 
+     * @return This buffer
+     */
+    public TokenBuffer append(TokenBuffer other)
+        throws IOException, JsonGenerationException
+    {
+        JsonParser jp = other.asParser();
+        while (jp.nextToken() != null) {
+            this.copyCurrentEvent(jp);
+        }
+        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
+     * references (from core to mapper package); and as such we also
+     * can not take second argument.
+     */
+    public void serialize(JsonGenerator jgen)
+        throws IOException, JsonGenerationException
+    {
+        Segment segment = _first;
+        int ptr = -1;
+
+        while (true) {
+            if (++ptr >= Segment.TOKENS_PER_SEGMENT) {
+                ptr = 0;
+                segment = segment.next();
+                if (segment == null) break;
+            }
+            JsonToken t = segment.type(ptr);
+            if (t == null) break;
+
+            // Note: copied from 'copyCurrentEvent'...
+            switch (t) {
+            case START_OBJECT:
+                jgen.writeStartObject();
+                break;
+            case END_OBJECT:
+                jgen.writeEndObject();
+                break;
+            case START_ARRAY:
+                jgen.writeStartArray();
+                break;
+            case END_ARRAY:
+                jgen.writeEndArray();
+                break;
+            case FIELD_NAME:
+            {
+                // 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting?
+                Object ob = segment.get(ptr);
+                if (ob instanceof SerializableString) {
+                    jgen.writeFieldName((SerializableString) ob);
+                } else {
+                    jgen.writeFieldName((String) ob);
+                }
+            }
+                break;
+            case VALUE_STRING:
+                {
+                    Object ob = segment.get(ptr);
+                    if (ob instanceof SerializableString) {
+                        jgen.writeString((SerializableString) ob);
+                    } else {
+                        jgen.writeString((String) ob);
+                    }
+                }
+                break;
+            case VALUE_NUMBER_INT:
+                {
+                    Object n = segment.get(ptr);
+                    if (n instanceof Integer) {
+                        jgen.writeNumber((Integer) n);
+                    } else if (n instanceof BigInteger) {
+                        jgen.writeNumber((BigInteger) n);
+                    } else if (n instanceof Long) {
+                        jgen.writeNumber((Long) n);
+                    } else if (n instanceof Short) {
+                        jgen.writeNumber((Short) n);
+                    } else {
+                        jgen.writeNumber(((Number) n).intValue());
+                    }
+                }
+                break;
+            case VALUE_NUMBER_FLOAT:
+                {
+                    Object n = segment.get(ptr);
+                    if (n instanceof Double) {
+                        jgen.writeNumber(((Double) n).doubleValue());
+                    } else if (n instanceof BigDecimal) {
+                        jgen.writeNumber((BigDecimal) n);
+                    } else if (n instanceof Float) {
+                        jgen.writeNumber(((Float) n).floatValue());
+                    } else if (n == null) {
+                        jgen.writeNull();
+                    } else if (n instanceof String) {
+                        jgen.writeNumber((String) n);
+                    } else {
+                        throw new JsonGenerationException("Unrecognized value type for VALUE_NUMBER_FLOAT: "+n.getClass().getName()+", can not serialize");
+                    }
+                }
+                break;
+            case VALUE_TRUE:
+                jgen.writeBoolean(true);
+                break;
+            case VALUE_FALSE:
+                jgen.writeBoolean(false);
+                break;
+            case VALUE_NULL:
+                jgen.writeNull();
+                break;
+            case VALUE_EMBEDDED_OBJECT:
+                jgen.writeObject(segment.get(ptr));
+                break;
+            default:
+                throw new RuntimeException("Internal error: should never end up through this code path");
+            }
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        // Let's print up to 100 first tokens...
+        final int MAX_COUNT = 100;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("[TokenBuffer: ");
+        JsonParser jp = asParser();
+        int count = 0;
+
+        while (true) {
+            JsonToken t;
+            try {
+                t = jp.nextToken();
+                if (t == null) break;
+                if (count < MAX_COUNT) {
+                    if (count > 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(t.toString());
+                    if (t == JsonToken.FIELD_NAME) {
+                        sb.append('(');
+                        sb.append(jp.getCurrentName());
+                        sb.append(')');
+                    }
+                }
+            } catch (IOException ioe) { // should never occur
+                throw new IllegalStateException(ioe);
+            }
+            ++count;
+        }
+
+        if (count >= MAX_COUNT) {
+            sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)");
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+        
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: configuration
+    /**********************************************************
+     */
+
+    @Override
+    public JsonGenerator enable(Feature f) {
+        _generatorFeatures |= f.getMask();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator disable(Feature f) {
+        _generatorFeatures &= ~f.getMask();
+        return this;
+    }
+
+    //public JsonGenerator configure(SerializationFeature f, boolean state) { }
+
+    @Override
+    public boolean isEnabled(Feature f) {
+        return (_generatorFeatures & f.getMask()) != 0;
+    }
+
+    @Override
+    public JsonGenerator useDefaultPrettyPrinter() {
+        // No-op: we don't indent
+        return this;
+    }
+
+    @Override
+    public JsonGenerator setCodec(ObjectCodec oc) {
+        _objectCodec = oc;
+        return this;
+    }
+
+    @Override
+    public ObjectCodec getCodec() { return _objectCodec; }
+
+    @Override
+    public final JsonWriteContext getOutputContext() { return _writeContext; }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: low-level output handling
+    /**********************************************************
+     */
+
+    @Override
+    public void flush() throws IOException { /* NOP */ }
+
+    @Override
+    public void close() throws IOException {
+        _closed = true;
+    }
+
+    @Override
+    public boolean isClosed() { return _closed; }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, structural
+    /**********************************************************
+     */
+
+    @Override
+    public final void writeStartArray()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.START_ARRAY);
+        _writeContext = _writeContext.createChildArrayContext();
+    }
+
+    @Override
+    public final void writeEndArray()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.END_ARRAY);
+        // Let's allow unbalanced tho... i.e. not run out of root level, ever
+        JsonWriteContext c = _writeContext.getParent();
+        if (c != null) {
+            _writeContext = c;
+        }
+    }
+
+    @Override
+    public final void writeStartObject()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.START_OBJECT);
+        _writeContext = _writeContext.createChildObjectContext();
+    }
+
+    @Override
+    public final void writeEndObject()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.END_OBJECT);
+        // Let's allow unbalanced tho... i.e. not run out of root level, ever
+        JsonWriteContext c = _writeContext.getParent();
+        if (c != null) {
+            _writeContext = c;
+        }
+    }
+
+    @Override
+    public final void writeFieldName(String name)
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.FIELD_NAME, name);
+        _writeContext.writeFieldName(name);
+    }
+
+    @Override
+    public void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.FIELD_NAME, name);
+        _writeContext.writeFieldName(name.getValue());
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, textual
+    /**********************************************************
+     */
+
+    @Override
+    public void writeString(String text) throws IOException,JsonGenerationException {
+        if (text == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_STRING, text);
+        }
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        writeString(new String(text, offset, len));
+    }
+
+    @Override
+    public void writeString(SerializableString text) throws IOException, JsonGenerationException {
+        if (text == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_STRING, text);
+        }
+    }
+    
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(SerializableString text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+    
+    @Override
+    public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(char c) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, primitive types
+    /**********************************************************
+     */
+
+    @Override
+    public void writeNumber(short i) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_INT, Short.valueOf(i));
+    }
+
+    @Override
+    public void writeNumber(int i) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
+    }
+
+    @Override
+    public void writeNumber(long l) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
+    }
+
+    @Override
+    public void writeNumber(double d) throws IOException,JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
+    }
+
+    @Override
+    public void writeNumber(float f) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
+    }
+
+    @Override
+    public void writeNumber(BigDecimal dec) throws IOException,JsonGenerationException {
+        if (dec == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_NUMBER_FLOAT, dec);
+        }
+    }
+
+    @Override
+    public void writeNumber(BigInteger v) throws IOException, JsonGenerationException {
+        if (v == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_NUMBER_INT, v);
+        }
+    }
+
+    @Override
+    public void writeNumber(String encodedValue) throws IOException, JsonGenerationException {
+        /* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric
+         *   identity as long as possible
+         */
+        _append(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
+    }
+
+    @Override
+    public void writeBoolean(boolean state) throws IOException,JsonGenerationException {
+        _append(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
+    }
+
+    @Override
+    public void writeNull() throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NULL);
+    }
+
+    /*
+    /***********************************************************
+    /* JsonGenerator implementation: write methods for POJOs/trees
+    /***********************************************************
+     */
+
+    @Override
+    public void writeObject(Object value)
+        throws IOException, JsonProcessingException
+    {
+        // embedded means that no conversions should be done...
+        _append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
+    }
+
+    @Override
+    public void writeTree(TreeNode rootNode)
+        throws IOException, JsonProcessingException
+    {
+        /* 31-Dec-2009, tatu: no need to convert trees either is there?
+         *  (note: may need to re-evaluate at some point)
+         */
+        _append(JsonToken.VALUE_EMBEDDED_OBJECT, rootNode);
+    }
+
+    /*
+    /***********************************************************
+    /* JsonGenerator implementation; binary
+    /***********************************************************
+     */
+
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        /* 31-Dec-2009, tatu: can do this using multiple alternatives; but for
+         *   now, let's try to limit number of conversions.
+         *   The only (?) tricky thing is that of whether to preserve variant,
+         *   seems pointless, so let's not worry about it unless there's some
+         *   compelling reason to.
+         */
+        byte[] copy = new byte[len];
+        System.arraycopy(data, offset, copy, 0, len);
+        writeObject(copy);
+    }
+
+    /**
+     * Although we could support this method, it does not necessarily make
+     * sense: we can not make good use of streaming because buffer must
+     * hold all the data. Because of this, currently this will simply
+     * throw {@link UnsupportedOperationException}
+     */
+    @Override
+    public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) {
+        throw new UnsupportedOperationException();
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation; pass-through copy
+    /**********************************************************
+     */
+
+    @Override
+    public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_OBJECT:
+            writeStartObject();
+            break;
+        case END_OBJECT:
+            writeEndObject();
+            break;
+        case START_ARRAY:
+            writeStartArray();
+            break;
+        case END_ARRAY:
+            writeEndArray();
+            break;
+        case FIELD_NAME:
+            writeFieldName(jp.getCurrentName());
+            break;
+        case VALUE_STRING:
+            if (jp.hasTextCharacters()) {
+                writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
+            } else {
+                writeString(jp.getText());
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            switch (jp.getNumberType()) {
+            case INT:
+                writeNumber(jp.getIntValue());
+                break;
+            case BIG_INTEGER:
+                writeNumber(jp.getBigIntegerValue());
+                break;
+            default:
+                writeNumber(jp.getLongValue());
+            }
+            break;
+        case VALUE_NUMBER_FLOAT:
+            switch (jp.getNumberType()) {
+            case BIG_DECIMAL:
+                writeNumber(jp.getDecimalValue());
+                break;
+            case FLOAT:
+                writeNumber(jp.getFloatValue());
+                break;
+            default:
+                writeNumber(jp.getDoubleValue());
+            }
+            break;
+        case VALUE_TRUE:
+            writeBoolean(true);
+            break;
+        case VALUE_FALSE:
+            writeBoolean(false);
+            break;
+        case VALUE_NULL:
+            writeNull();
+            break;
+        case VALUE_EMBEDDED_OBJECT:
+            writeObject(jp.getEmbeddedObject());
+            break;
+        default:
+            throw new RuntimeException("Internal error: should never end up through this code path");
+        }
+    }
+
+    @Override
+    public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException {
+        JsonToken t = jp.getCurrentToken();
+
+        // Let's handle field-name separately first
+        if (t == JsonToken.FIELD_NAME) {
+            writeFieldName(jp.getCurrentName());
+            t = jp.nextToken();
+            // fall-through to copy the associated value
+        }
+
+        switch (t) {
+        case START_ARRAY:
+            writeStartArray();
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                copyCurrentStructure(jp);
+            }
+            writeEndArray();
+            break;
+        case START_OBJECT:
+            writeStartObject();
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                copyCurrentStructure(jp);
+            }
+            writeEndObject();
+            break;
+        default: // others are simple:
+            copyCurrentEvent(jp);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    protected final void _append(JsonToken type) {
+        Segment next = _last.append(_appendOffset, type);
+        if (next == null) {
+            ++_appendOffset;
+        } else {
+            _last = next;
+            _appendOffset = 1; // since we added first at 0
+        }
+    }
+
+    protected final void _append(JsonToken type, Object value) {
+        Segment next = _last.append(_appendOffset, type, value);
+        if (next == null) {
+            ++_appendOffset;
+        } else {
+            _last = next;
+            _appendOffset = 1;
+        }
+    }
+
+    protected final void _appendRaw(int rawType, Object value) {
+        Segment next = _last.appendRaw(_appendOffset, rawType, value);
+        if (next == null) {
+            ++_appendOffset;
+        } else {
+            _last = next;
+            _appendOffset = 1;
+        }
+    }
+    
+    protected void _reportUnsupportedOperation() {
+        throw new UnsupportedOperationException("Called operation not supported for TokenBuffer");
+    }
+    
+    /*
+    /**********************************************************
+    /* Supporting classes
+    /**********************************************************
+     */
+
+    protected final static class Parser
+        extends ParserMinimalBase
+    {
+        protected ObjectCodec _codec;
+
+        /*
+        /**********************************************************
+        /* Parsing state
+        /**********************************************************
+         */
+
+        /**
+         * Currently active segment
+         */
+        protected Segment _segment;
+
+        /**
+         * Pointer to current token within current segment
+         */
+        protected int _segmentPtr;
+
+        /**
+         * Information about parser context, context in which
+         * the next token is to be parsed (root, array, object).
+         */
+        protected JsonReadContext _parsingContext;
+        
+        protected boolean _closed;
+
+        protected transient ByteArrayBuilder _byteBuilder;
+
+        protected JsonLocation _location = null;
+        
+        /*
+        /**********************************************************
+        /* Construction, init
+        /**********************************************************
+         */
+        
+        public Parser(Segment firstSeg, ObjectCodec codec)
+        {
+            super(0);
+            _segment = firstSeg;
+            _segmentPtr = -1; // not yet read
+            _codec = codec;
+            _parsingContext = JsonReadContext.createRootContext(-1, -1);
+        }
+
+        public void setLocation(JsonLocation l) {
+            _location = l;
+        }
+        
+        @Override
+        public ObjectCodec getCodec() { return _codec; }
+
+        @Override
+        public void setCodec(ObjectCodec c) { _codec = c; }
+
+        @Override
+        public Version version() {
+            return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
+        }
+
+        /*
+        /**********************************************************
+        /* Extended API beyond JsonParser
+        /**********************************************************
+         */
+        
+        public JsonToken peekNextToken()
+            throws IOException, JsonParseException
+        {
+            // closed? nothing more to peek, either
+            if (_closed) return null;
+            Segment seg = _segment;
+            int ptr = _segmentPtr+1;
+            if (ptr >= Segment.TOKENS_PER_SEGMENT) {
+                ptr = 0;
+                seg = (seg == null) ? null : seg.next();
+            }
+            return (seg == null) ? null : seg.type(ptr);
+        }
+        
+        /*
+        /**********************************************************
+        /* Closeable implementation
+        /**********************************************************
+         */
+
+        @Override
+        public void close() throws IOException {
+            if (!_closed) {
+                _closed = true;
+            }
+        }
+
+        /*
+        /**********************************************************
+        /* Public API, traversal
+        /**********************************************************
+         */
+        
+        @Override
+        public JsonToken nextToken() throws IOException, JsonParseException
+        {
+            // If we are closed, nothing more to do
+            if (_closed || (_segment == null)) return null;
+
+            // Ok, then: any more tokens?
+            if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) {
+                _segmentPtr = 0;
+                _segment = _segment.next();
+                if (_segment == null) {
+                    return null;
+                }
+            }
+            _currToken = _segment.type(_segmentPtr);
+            // Field name? Need to update context
+            if (_currToken == JsonToken.FIELD_NAME) {
+                Object ob = _currentObject();
+                String name = (ob instanceof String) ? ((String) ob) : ob.toString();
+                _parsingContext.setCurrentName(name);
+            } else if (_currToken == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(-1, -1);
+            } else if (_currToken == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(-1, -1);
+            } 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(-1, -1);
+                }
+            }
+            return _currToken;
+        }
+
+        @Override
+        public boolean isClosed() { return _closed; }
+
+        /*
+        /**********************************************************
+        /* Public API, token accessors
+        /**********************************************************
+         */
+        
+        @Override
+        public JsonStreamContext getParsingContext() { return _parsingContext; }
+
+        @Override
+        public JsonLocation getTokenLocation() { return getCurrentLocation(); }
+
+        @Override
+        public JsonLocation getCurrentLocation() {
+            return (_location == null) ? JsonLocation.NA : _location;
+        }
+
+        @Override
+        public String getCurrentName() { return _parsingContext.getCurrentName(); }
+
+        @Override
+        public void overrideCurrentName(String name)
+        {
+            // Simple, but need to look for START_OBJECT/ARRAY's "off-by-one" thing:
+            JsonReadContext ctxt = _parsingContext;
+            if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
+                ctxt = ctxt.getParent();
+            }
+            ctxt.setCurrentName(name);
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, text
+        /**********************************************************
+         */
+        
+        @Override
+        public String getText()
+        {
+            // common cases first:
+            if (_currToken == JsonToken.VALUE_STRING
+                    || _currToken == JsonToken.FIELD_NAME) {
+                Object ob = _currentObject();
+                if (ob instanceof String) {
+                    return (String) ob;
+                }
+                return (ob == null) ? null : ob.toString();
+            }
+            if (_currToken == null) {
+                return null;
+            }
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                Object ob = _currentObject();
+                return (ob == null) ? null : ob.toString();
+            default:
+            	return _currToken.asString();
+            }
+        }
+
+        @Override
+        public char[] getTextCharacters() {
+            String str = getText();
+            return (str == null) ? null : str.toCharArray();
+        }
+
+        @Override
+        public int getTextLength() {
+            String str = getText();
+            return (str == null) ? 0 : str.length();
+        }
+
+        @Override
+        public int getTextOffset() { return 0; }
+
+        @Override
+        public boolean hasTextCharacters() {
+            // We never have raw buffer available, so:
+            return false;
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, numeric
+        /**********************************************************
+         */
+
+        @Override
+        public BigInteger getBigIntegerValue() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof BigInteger) {
+                return (BigInteger) n;
+            }
+            if (getNumberType() == NumberType.BIG_DECIMAL) {
+                return ((BigDecimal) n).toBigInteger();
+            }
+            // int/long is simple, but let's also just truncate float/double:
+            return BigInteger.valueOf(n.longValue());
+        }
+
+        @Override
+        public BigDecimal getDecimalValue() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof BigDecimal) {
+                return (BigDecimal) n;
+            }
+            switch (getNumberType()) {
+            case INT:
+            case LONG:
+                return BigDecimal.valueOf(n.longValue());
+            case BIG_INTEGER:
+                return new BigDecimal((BigInteger) n);
+            default:
+            }
+            // float or double
+            return BigDecimal.valueOf(n.doubleValue());
+        }
+
+        @Override
+        public double getDoubleValue() throws IOException, JsonParseException {
+            return getNumberValue().doubleValue();
+        }
+
+        @Override
+        public float getFloatValue() throws IOException, JsonParseException {
+            return getNumberValue().floatValue();
+        }
+
+        @Override
+        public int getIntValue() throws IOException, JsonParseException
+        {
+            // optimize common case:
+            if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+                return ((Number) _currentObject()).intValue();
+            }
+            return getNumberValue().intValue();
+        }
+
+        @Override
+        public long getLongValue() throws IOException, JsonParseException {
+            return getNumberValue().longValue();
+        }
+
+        @Override
+        public NumberType getNumberType() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof Integer) return NumberType.INT;
+            if (n instanceof Long) return NumberType.LONG;
+            if (n instanceof Double) return NumberType.DOUBLE;
+            if (n instanceof BigDecimal) return NumberType.BIG_DECIMAL;
+            if (n instanceof BigInteger) return NumberType.BIG_INTEGER;
+            if (n instanceof Float) return NumberType.FLOAT;
+            if (n instanceof Short) return NumberType.INT;       // should be SHORT
+            return null;
+        }
+
+        @Override
+        public final Number getNumberValue() throws IOException, JsonParseException {
+            _checkIsNumber();
+            Object value = _currentObject();
+            if (value instanceof Number) {
+                return (Number) value;
+            }
+            // Difficult to really support numbers-as-Strings; but let's try.
+            // NOTE: no access to DeserializationConfig, unfortunately, so can not
+            // try to determine Double/BigDecimal preference...
+            if (value instanceof String) {
+                String str = (String) value;
+                if (str.indexOf('.') >= 0) {
+                    return Double.parseDouble(str);
+                }
+                return Long.parseLong(str);
+            }
+            if (value == null) {
+                return null;
+            }
+            throw new IllegalStateException("Internal error: entry should be a Number, but is of type "
+                    +value.getClass().getName());
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, other
+        /**********************************************************
+         */
+
+        @Override
+        public Object getEmbeddedObject()
+        {
+            if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                return _currentObject();
+            }
+            return null;
+        }
+
+        @Override
+        public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException
+        {
+            // First: maybe we some special types?
+            if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                // Embedded byte array would work nicely...
+                Object ob = _currentObject();
+                if (ob instanceof byte[]) {
+                    return (byte[]) ob;
+                }
+                // 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");
+            }
+            final String str = getText();
+            if (str == null) {
+                return null;
+            }
+            ByteArrayBuilder builder = _byteBuilder;
+            if (builder == null) {
+                _byteBuilder = builder = new ByteArrayBuilder(100);
+            } else {
+                _byteBuilder.reset();
+            }
+            _decodeBase64(str, builder, b64variant);
+            return builder.toByteArray();
+        }
+
+        @Override
+        public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws IOException, JsonParseException
+        {
+            byte[] data = getBinaryValue(b64variant);
+            if (data != null) {
+                out.write(data, 0, data.length);
+                return data.length;
+            }
+            return 0;
+        }
+        
+        /*
+        /**********************************************************
+        /* Internal methods
+        /**********************************************************
+         */
+
+        protected final Object _currentObject() {
+            return _segment.get(_segmentPtr);
+        }
+
+        protected final void _checkIsNumber() throws JsonParseException
+        {
+            if (_currToken == null || !_currToken.isNumeric()) {
+                throw _constructError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
+            }
+        }
+
+        @Override
+        protected void _handleEOF() throws JsonParseException {
+            _throwInternal();
+        }
+    }
+    
+    /**
+     * Individual segment of TokenBuffer that can store up to 16 tokens
+     * (limited by 4 bits per token type marker requirement).
+     * Current implementation uses fixed length array; could alternatively
+     * use 16 distinct fields and switch statement (slightly more efficient
+     * storage, slightly slower access)
+     */
+    protected final static class Segment 
+    {
+        public final static int TOKENS_PER_SEGMENT = 16;
+        
+        /**
+         * Static array used for fast conversion between token markers and
+         * matching {@link JsonToken} instances
+         */
+        private final static JsonToken[] TOKEN_TYPES_BY_INDEX;
+        static {
+            // ... here we know that there are <= 16 values in JsonToken enum
+            TOKEN_TYPES_BY_INDEX = new JsonToken[16];
+            JsonToken[] t = JsonToken.values();
+            System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1));
+        }
+
+        // // // Linking
+        
+        protected Segment _next;
+        
+        // // // State
+
+        /**
+         * Bit field used to store types of buffered tokens; 4 bits per token.
+         * Value 0 is reserved for "not in use"
+         */
+        protected long _tokenTypes;
+
+        
+        // Actual tokens
+
+        protected final Object[] _tokens = new Object[TOKENS_PER_SEGMENT];
+
+        public Segment() { }
+
+        // // // Accessors
+
+        public JsonToken type(int index)
+        {
+            long l = _tokenTypes;
+            if (index > 0) {
+                l >>= (index << 2);
+            }
+            int ix = ((int) l) & 0xF;
+            return TOKEN_TYPES_BY_INDEX[ix];
+        }
+
+        public int rawType(int index)
+        {
+            long l = _tokenTypes;
+            if (index > 0) {
+                l >>= (index << 2);
+            }
+            int ix = ((int) l) & 0xF;
+            return ix;
+        }
+        
+        public Object get(int index) {
+            return _tokens[index];
+        }
+
+        public Segment next() { return _next; }
+        
+        // // // Mutators
+
+        public Segment append(int index, JsonToken tokenType)
+        {
+            if (index < TOKENS_PER_SEGMENT) {
+                set(index, tokenType);
+                return null;
+            }
+            _next = new Segment();
+            _next.set(0, tokenType);
+            return _next;
+        }
+
+        public Segment append(int index, JsonToken tokenType, Object value)
+        {
+            if (index < TOKENS_PER_SEGMENT) {
+                set(index, tokenType, value);
+                return null;
+            }
+            _next = new Segment();
+            _next.set(0, tokenType, value);
+            return _next;
+        }
+
+        public Segment appendRaw(int index, int rawTokenType, Object value)
+        {
+            if (index < TOKENS_PER_SEGMENT) {
+                set(index, rawTokenType, value);
+                return null;
+            }
+            _next = new Segment();
+            _next.set(0, rawTokenType, value);
+            return _next;
+        }
+        
+        public void set(int index, JsonToken tokenType)
+        {
+            /* Assumption here is that there are no overwrites, just appends;
+             * and so no masking is needed (nor explicit setting of null)
+             */
+            long typeCode = tokenType.ordinal();
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+
+        public void set(int index, JsonToken tokenType, Object value)
+        {
+            _tokens[index] = value;
+            long typeCode = tokenType.ordinal();
+            /* Assumption here is that there are no overwrites, just appends;
+             * and so no masking is needed
+             */
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+
+        private void set(int index, int rawTokenType, Object value)
+        {
+            _tokens[index] = value;
+            long typeCode = (long) rawTokenType;
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/ViewMatcher.java b/src/main/java/com/fasterxml/jackson/databind/util/ViewMatcher.java
new file mode 100644
index 0000000..89c81b5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/ViewMatcher.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.databind.util;
+
+/**
+ * Helper class used for checking whether a property is visible
+ * in the active view
+ */
+public abstract class ViewMatcher
+{
+    public abstract boolean isVisibleForView(Class<?> activeView);
+
+    public static ViewMatcher construct(Class<?>[] views)
+    {
+        if (views == null) {
+            return Empty.instance;
+        }
+        switch (views.length) {
+        case 0:
+            return Empty.instance;
+        case 1:
+            return new Single(views[0]);
+        }
+        return new Multi(views);
+    } 
+    
+    /*
+    /**********************************************************
+    /* Concrete sub-classes
+    /**********************************************************
+     */
+
+    private final static class Empty extends ViewMatcher
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        final static Empty instance = new Empty();
+        @Override
+        public boolean isVisibleForView(Class<?> activeView) {
+            return false;
+        }
+    }
+
+    private final static class Single extends ViewMatcher
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final Class<?> _view;
+        public Single(Class<?> v) { _view = v; }
+        @Override
+        public boolean isVisibleForView(Class<?> activeView) {
+            return (activeView == _view) || _view.isAssignableFrom(activeView);
+        }
+    }
+
+    private final static class Multi extends ViewMatcher
+        implements java.io.Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final Class<?>[] _views;
+
+        public Multi(Class<?>[] v) { _views = v; }
+
+        @Override
+        public boolean isVisibleForView(Class<?> activeView)
+        {
+            for (int i = 0, len = _views.length; i < len; ++i) {
+                Class<?> view = _views[i];
+                if ((activeView == view) || view.isAssignableFrom(activeView)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/databind/util/package-info.java b/src/main/java/com/fasterxml/jackson/databind/util/package-info.java
new file mode 100644
index 0000000..b965a85
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/databind/util/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Utility classes for Mapper package.
+ */
+package com.fasterxml.jackson.databind.util;
diff --git a/src/main/resources/META-INF/LICENSE b/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..6acf754
--- /dev/null
+++ b/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,8 @@
+This copy of Jackson JSON processor databind module is licensed under the
+Apache (Software) License, version 2.0 ("the License").
+See the License for details about distribution rights, and the
+specific rights regarding derivate works.
+
+You may obtain a copy of the License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
diff --git a/src/main/resources/META-INF/NOTICE b/src/main/resources/META-INF/NOTICE
new file mode 100644
index 0000000..4c976b7
--- /dev/null
+++ b/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,20 @@
+# Jackson JSON processor
+
+Jackson is a high-performance, Free/Open Source JSON processing library.
+It was originally written by Tatu Saloranta (tatu.saloranta at iki.fi), and has
+been in development since 2007.
+It is currently developed by a community of developers, as well as supported
+commercially by FasterXML.com.
+
+## Licensing
+
+Jackson core and extension components may licensed under different licenses.
+To find the details that apply to this artifact see the accompanying LICENSE file.
+For more information, including possible other licensing options, contact
+FasterXML.com (http://fasterxml.com).
+
+## Credits
+
+A list of contributors may be found from CREDITS file, which is included
+in some artifacts (usually source distributions); but is always available
+from the source code management (SCM) system project uses.
diff --git a/src/main/resources/META-INF/services/com.fasterxml.jackson.core.ObjectCodec b/src/main/resources/META-INF/services/com.fasterxml.jackson.core.ObjectCodec
new file mode 100644
index 0000000..f126bb4
--- /dev/null
+++ b/src/main/resources/META-INF/services/com.fasterxml.jackson.core.ObjectCodec
@@ -0,0 +1 @@
+com.fasterxml.jackson.databind.ObjectMapper
diff --git a/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
new file mode 100644
index 0000000..e4daadf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/BaseMapTest.java
@@ -0,0 +1,228 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.fasterxml.jackson.test.BaseTest;
+
+public abstract class BaseMapTest
+    extends BaseTest
+{
+    private final static Object SINGLETON_OBJECT = new Object();
+    
+    /*
+    /**********************************************************
+    /* Shared helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Simple wrapper around boolean types, usually to test value
+     * conversions or wrapping
+     */
+    protected static class BooleanWrapper {
+        public Boolean b;
+
+        @JsonCreator
+        public BooleanWrapper(Boolean value) { b = value; }
+
+        @JsonValue public Boolean value() { return b; }
+    }
+
+    protected static class IntWrapper {
+        public int i;
+
+        public IntWrapper() { }
+        public IntWrapper(int value) { i = value; }
+    }
+    
+    /**
+     * Simple wrapper around String type, usually to test value
+     * conversions or wrapping
+     */
+    protected static class StringWrapper {
+        public String str;
+
+        public StringWrapper() { }
+        public StringWrapper(String value) {
+            str = value;
+        }
+    }
+
+    protected static class ObjectWrapper {
+        private final Object object;
+        protected ObjectWrapper(final Object object) {
+            this.object = object;
+        }
+        public Object getObject() { return object; }
+        @JsonCreator
+        static ObjectWrapper jsonValue(final Object object) {
+            return new ObjectWrapper(object);
+        }
+    }
+
+    protected static class ListWrapper<T>
+    {
+        public List<T> list;
+
+        public ListWrapper(T... values) {
+            list = new ArrayList<T>();
+            for (T value : values) {
+                list.add(value);
+            }
+        }
+    }
+
+    protected static class MapWrapper<K,V>
+    {
+        public Map<K,V> map;
+
+        public MapWrapper(Map<K,V> m) {
+            map = m;
+        }
+    }
+    
+    protected static class ArrayWrapper<T>
+    {
+        public T[] array;
+
+        public ArrayWrapper(T[] v) {
+            array = v;
+        }
+    }
+    
+    /**
+     * Enumeration type with sub-classes per value.
+     */
+    protected enum EnumWithSubClass {
+        A { @Override public void foobar() { } }
+        ,B { @Override public void foobar() { } }
+        ;
+
+        public abstract void foobar();
+    }
+
+    protected BaseMapTest() { super(); }
+
+    /*
+    /**********************************************************
+    /* Additional assert methods
+    /**********************************************************
+     */
+
+    private final static ObjectMapper SHARED_MAPPER = new ObjectMapper();
+
+    protected ObjectMapper objectMapper() {
+        return SHARED_MAPPER;
+    }
+    
+    protected ObjectWriter objectWriter() {
+        return SHARED_MAPPER.writer();
+    }
+
+    protected ObjectReader objectReader() {
+        return SHARED_MAPPER.reader();
+    }
+    
+    protected ObjectReader objectReader(Class<?> cls) {
+        return SHARED_MAPPER.reader(cls);
+    }
+
+    /*
+    /**********************************************************
+    /* Additional assert methods
+    /**********************************************************
+     */
+
+    protected void assertEquals(int[] exp, int[] act)
+    {
+        assertArrayEquals(exp, act);
+    }
+
+    /**
+     * Helper method for verifying 3 basic cookie cutter cases;
+     * identity comparison (true), and against null (false),
+     * or object of different type (false)
+     */
+    protected void assertStandardEquals(Object o)
+    {
+        assertTrue(o.equals(o));
+        assertFalse(o.equals(null));
+        assertFalse(o.equals(SINGLETON_OBJECT));
+        // just for fun, let's also call hash code...
+        o.hashCode();
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    protected Map<String,Object> writeAndMap(ObjectMapper m, Object value)
+        throws IOException
+    {
+        String str = m.writeValueAsString(value);
+        return (Map<String,Object>) m.readValue(str, Map.class);
+    }
+
+    protected <T> T readAndMapFromString(String input, Class<T> cls)
+        throws IOException
+    {
+        return readAndMapFromString(SHARED_MAPPER, input, cls);
+    }
+    
+    protected <T> T readAndMapFromString(ObjectMapper m, String input, Class<T> cls)
+        throws IOException
+    {
+        return (T) m.readValue("\""+input+"\"", cls);
+    }
+    
+    protected String serializeAsString(ObjectMapper m, Object value)
+        throws IOException
+    {
+        return m.writeValueAsString(value);
+    }
+
+    protected String serializeAsString(Object value)
+        throws IOException
+    {
+        return serializeAsString(SHARED_MAPPER, value);
+    }
+
+    protected String asJSONObjectValueString(Object... args)
+        throws IOException
+    {
+        return asJSONObjectValueString(SHARED_MAPPER, args);
+    }
+
+    protected String asJSONObjectValueString(ObjectMapper m, Object... args)
+        throws IOException
+    {
+        LinkedHashMap<Object,Object> map = new LinkedHashMap<Object,Object>();
+        for (int i = 0, len = args.length; i < len; i += 2) {
+            map.put(args[i], args[i+1]);
+        }
+        return m.writeValueAsString(map);
+    }
+
+    protected TimeZone getUTCTimeZone() {
+        return TimeZone.getTimeZone("GMT");
+    }
+
+    protected byte[] utf8Bytes(String str) {
+        try {
+            return str.getBytes("UTF-8");
+        } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
new file mode 100644
index 0000000..a5e6665
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestFormatSchema.java
@@ -0,0 +1,274 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.base.ParserBase;
+import com.fasterxml.jackson.core.base.GeneratorBase;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Basic tests to ensure that {@link FormatSchema} instances are properly
+ * passed to {@link JsonGenerator} and {@link JsonParser} instances if
+ * mapper, reader or writer is configured with one.
+ */
+public class TestFormatSchema extends BaseMapTest
+{
+    /*
+    /**********************************************************************
+    /* Helper classes
+    /**********************************************************************
+     */
+
+    static class MySchema implements FormatSchema {
+        @Override
+        public String getSchemaType() { return "test"; }
+    }
+    
+    static class FactoryWithSchema extends JsonFactory
+    {
+        @Override
+        public String getFormatName() { return "test"; }
+
+        @Override
+        public boolean canUseSchema(FormatSchema schema) {
+            return (schema instanceof MySchema);
+        }
+        
+        private static final long serialVersionUID = 1L;
+        @Override
+        protected JsonParser _createParser(Reader r, IOContext ctxt)
+            throws IOException, JsonParseException
+        {
+            return new ParserWithSchema(ctxt, _parserFeatures);
+        }
+
+        @Override
+        protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException
+        {
+            return new GeneratorWithSchema(_generatorFeatures, _objectCodec);
+        }
+    }
+
+    // Ugly, but easiest way to get schema back is to throw exception...
+    @SuppressWarnings("serial")
+    static class SchemaException extends RuntimeException
+    {
+        public final FormatSchema _schema;
+        
+        public SchemaException(FormatSchema s) {
+            _schema = s;
+        }
+    }
+    
+    static class ParserWithSchema extends ParserBase
+    {
+        public ParserWithSchema(IOContext ioCtxt, int features)
+        {
+            super(ioCtxt, features);
+        }
+
+        @Override
+        public void setSchema(FormatSchema schema) {
+            throw new SchemaException(schema);
+        }
+
+        @Override
+        protected void _finishString() throws IOException, JsonParseException { }
+
+        @Override
+        public byte[] getBinaryValue(Base64Variant b64variant) {
+            return null;
+        }
+
+        @Override
+        public byte[] getEmbeddedObject() {
+            return null;
+        }
+
+        @Override
+        public String getText() throws IOException, JsonParseException {
+            return null;
+        }
+
+        @Override
+        public char[] getTextCharacters() throws IOException {
+            return null;
+        }
+
+        @Override
+        public int getTextLength() throws IOException, JsonParseException {
+            return 0;
+        }
+
+        @Override
+        public int getTextOffset() throws IOException, JsonParseException {
+            return 0;
+        }
+
+        @Override
+        public JsonToken nextToken() throws IOException, JsonParseException {
+            return null;
+        }
+
+        @Override
+        public ObjectCodec getCodec() {
+            return null;
+        }
+
+        @Override
+        public void setCodec(ObjectCodec c) { }
+
+        @Override
+        protected boolean loadMore() throws IOException {
+            return false;
+        }
+
+        @Override
+        protected void _closeInput() throws IOException {
+        }
+
+        @Override
+        public int readBinaryValue(Base64Variant b64variant, OutputStream out) {
+            return 0;
+        }
+    }
+
+    static class GeneratorWithSchema extends GeneratorBase
+    {
+        public GeneratorWithSchema(int features, ObjectCodec codec)
+        {
+            super(features, codec);
+        }
+
+        @Override
+        public void setSchema(FormatSchema schema) {
+            throw new SchemaException(schema);
+        }
+
+        @Override
+        protected void _releaseBuffers() { }
+
+        @Override
+        protected void _verifyValueWrite(String typeMsg) throws IOException { }
+
+        @Override
+        public void flush() throws IOException { }
+
+        @Override
+        public void writeBinary(Base64Variant b64variant, byte[] data,
+                int offset, int len) throws IOException { }
+
+        @Override
+        public void writeBoolean(boolean state) throws IOException { }
+
+        @Override
+        public void writeFieldName(String name) throws IOException { }
+
+        @Override
+        public void writeNull() throws IOException, JsonGenerationException { }
+
+        @Override
+        public void writeNumber(short v) throws IOException { }
+
+        @Override
+        public void writeNumber(int v) throws IOException { }
+
+        @Override
+        public void writeNumber(long v) throws IOException { }
+
+        @Override
+        public void writeNumber(BigInteger v) throws IOException { }
+
+        @Override
+        public void writeNumber(double d) throws IOException { }
+
+        @Override
+        public void writeNumber(float f) throws IOException { }
+
+        @Override
+        public void writeNumber(BigDecimal dec) throws IOException { }
+
+        @Override
+        public void writeNumber(String encodedValue) throws IOException { }
+
+        @Override
+        public void writeRaw(String text) throws IOException { }
+
+        @Override
+        public void writeRaw(String text, int offset, int len) { }
+
+        @Override
+        public void writeRaw(char[] text, int offset, int len) { }
+
+        @Override
+        public void writeRaw(char c) throws IOException { }
+
+        @Override
+        public void writeRawUTF8String(byte[] text, int offset, int length) { }
+
+        @Override
+        public void writeString(String text) throws IOException { }
+
+        @Override
+        public void writeString(char[] text, int offset, int len) { }
+
+        @Override
+        public void writeUTF8String(byte[] text, int offset, int length) { }
+
+        @Override
+        public void writeStartArray() { }
+
+        @Override
+        public void writeEndArray() throws IOException, JsonGenerationException { }
+
+        @Override
+        public void writeStartObject() { }
+
+        @Override
+        public void writeEndObject() { }
+
+        @Override
+        public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) {
+            return -1;
+        }
+    }
+    
+    /*
+    /**********************************************************************
+    /* Unit tests
+    /**********************************************************************
+     */
+    
+    public void testFormatForParsers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper(new FactoryWithSchema());
+        MySchema s = new MySchema();
+        StringReader r = new StringReader("{}");
+        //  bit ugly, but can't think of cleaner simple way to check this...
+        try {
+            mapper.reader(s).withType(Object.class).readValue(r);
+            fail("Excpected exception");
+        } catch (SchemaException e) {
+            assertSame(s, e._schema);
+        }
+    }
+
+    public void testFormatForGenerators() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper(new FactoryWithSchema());
+        MySchema s = new MySchema();
+        StringWriter sw = new StringWriter();
+        //  bit ugly, but can't think of cleaner simple way to check this...
+        try {
+            mapper.writer(s).writeValue(sw, "Foobar");
+            fail("Excpected exception");
+        } catch (SchemaException e) {
+            assertSame(s, e._schema);
+        }
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java
new file mode 100644
index 0000000..78955c8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestGeneratorUsingMapper.java
@@ -0,0 +1,84 @@
+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/TestHandlerInstantiation.java b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java
new file mode 100644
index 0000000..d694c5a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestHandlerInstantiation.java
@@ -0,0 +1,267 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.*;
+import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestHandlerInstantiation extends BaseMapTest
+{
+    /*
+    /**********************************************************************
+    /* Helper classes, beans
+    /**********************************************************************
+     */
+
+    @JsonDeserialize(using=MyBeanDeserializer.class)
+    @JsonSerialize(using=MyBeanSerializer.class)
+    static class MyBean
+    {
+        public String value;
+
+        public MyBean() { this(null); }
+        public MyBean(String s) { value = s; }
+    }
+
+    @SuppressWarnings("serial")
+    @JsonDeserialize(keyUsing=MyKeyDeserializer.class)
+    static class MyMap extends HashMap<String,String> { }
+
+    @JsonTypeInfo(use=Id.CUSTOM, include=As.WRAPPER_ARRAY)
+    @JsonTypeIdResolver(CustomIdResolver.class)
+    static class TypeIdBean {
+        public int x;
+        
+        public TypeIdBean() { }
+        public TypeIdBean(int x) { this.x = x; }
+    }
+
+    static class TypeIdBeanWrapper {
+        public TypeIdBean bean;
+        
+        public TypeIdBeanWrapper() { this(null); }
+        public TypeIdBeanWrapper(TypeIdBean b) { bean = b; }
+    }
+    
+    /*
+    /**********************************************************************
+    /* Helper classes, serializers/deserializers/resolvers
+    /**********************************************************************
+     */
+    
+    static class MyBeanDeserializer extends JsonDeserializer<MyBean>
+    {
+        public String _prefix = "";
+
+        public MyBeanDeserializer(String p) {
+            _prefix  = p;
+        }
+        
+        @Override
+        public MyBean deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            return new MyBean(_prefix+jp.getText());
+        }
+    }
+
+    static class MyKeyDeserializer extends KeyDeserializer
+    {
+        public MyKeyDeserializer() { }
+        
+        @Override
+        public Object deserializeKey(String key, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            return "KEY";
+        }
+    }
+    
+    static class MyBeanSerializer extends JsonSerializer<MyBean>
+    {
+        public String _prefix = "";
+
+        public MyBeanSerializer(String p) {
+            _prefix  = p;
+        }
+        
+        @Override
+        public void serialize(MyBean value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+        {
+            jgen.writeString(_prefix + value.value);
+        }
+    }
+    
+    // copied from "TestCustomTypeIdResolver"
+    static class CustomIdResolver implements TypeIdResolver
+    {
+        static List<JavaType> initTypes;
+
+        final String _id;
+        
+        public CustomIdResolver(String idForBean) {
+            _id = idForBean;
+        }
+        
+        @Override
+        public Id getMechanism() {
+            return Id.CUSTOM;
+        }
+        @Override
+        public String idFromValue(Object value)
+        {
+            if (value.getClass() == TypeIdBean.class) {
+                return _id;
+            }
+            return "unknown";
+        }
+        @Override
+        public String idFromValueAndType(Object value, Class<?> type) {
+            return idFromValue(value);
+        }
+        @Override
+        public void init(JavaType baseType) {
+            if (initTypes != null) {
+                initTypes.add(baseType);
+            }
+        }
+        @Override
+        public JavaType typeFromId(String id)
+        {
+            if (id.equals(_id)) {
+                return TypeFactory.defaultInstance().constructType(TypeIdBean.class);
+            }
+            return null;
+        }
+        @Override
+        public String idFromBaseType() {
+            return "xxx";
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Helper classes, handler instantiator
+    /**********************************************************************
+     */
+    
+    static class MyInstantiator extends HandlerInstantiator
+    {
+        private final String _prefix;
+        
+        public MyInstantiator(String p) {
+            _prefix = p;
+        }
+        
+        @Override
+        public JsonDeserializer<?> deserializerInstance(DeserializationConfig config,
+                Annotated annotated,
+                Class<?> deserClass)
+        {
+            if (deserClass == MyBeanDeserializer.class) {
+                return new MyBeanDeserializer(_prefix);
+            }
+            return null;
+        }
+
+        @Override
+        public KeyDeserializer keyDeserializerInstance(DeserializationConfig config,
+                Annotated annotated, Class<?> keyDeserClass)
+        {
+            if (keyDeserClass == MyKeyDeserializer.class) {
+                return new MyKeyDeserializer();
+            }
+            return null;
+            
+        }
+        
+        @Override
+        public JsonSerializer<?> serializerInstance(SerializationConfig config,
+                Annotated annotated, Class<?> serClass)
+        {
+            if (serClass == MyBeanSerializer.class) {
+                return new MyBeanSerializer(_prefix);
+            }
+            return null;
+        }
+
+        @Override
+        public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config,
+                Annotated annotated, Class<?> resolverClass)
+        {
+            if (resolverClass == CustomIdResolver.class) {
+                return new CustomIdResolver("!!!");
+            }
+            return null;
+        }
+
+        @Override
+        public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated,
+                Class<?> builderClass)
+        {
+            return null;
+        }
+    }
+    
+    /*
+    /**********************************************************************
+    /* Unit tests
+    /**********************************************************************
+     */
+
+    public void testDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setHandlerInstantiator(new MyInstantiator("abc:"));
+        MyBean result = mapper.readValue(quote("123"), MyBean.class);
+        assertEquals("abc:123", result.value);
+    }
+
+    public void testKeyDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setHandlerInstantiator(new MyInstantiator("abc:"));
+        MyMap map = mapper.readValue("{\"a\":\"b\"}", MyMap.class);
+        // easiest to test by just serializing...
+        assertEquals("{\"KEY\":\"b\"}", mapper.writeValueAsString(map));
+    }
+    
+    public void testSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setHandlerInstantiator(new MyInstantiator("xyz:"));
+        assertEquals(quote("xyz:456"), mapper.writeValueAsString(new MyBean("456")));
+    }
+
+    public void testTypeIdResolver() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setHandlerInstantiator(new MyInstantiator("foobar"));
+        String json = mapper.writeValueAsString(new TypeIdBeanWrapper(new TypeIdBean(123)));
+        // should now use our custom id scheme:
+        assertEquals("{\"bean\":[\"!!!\",{\"x\":123}]}", json);
+        // and bring it back too:
+        TypeIdBeanWrapper result = mapper.readValue(json, TypeIdBeanWrapper.class);
+        TypeIdBean bean = result.bean;
+        assertEquals(123, bean.x);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
new file mode 100644
index 0000000..314298f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestJDKSerialization.java
@@ -0,0 +1,125 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Tests to verify that most core Jackson components can be serialized
+ * using default JDK serialization: this feature is useful for some
+ * platforms, such as Android, where memory management is handled
+ * much more aggressively.
+ */
+public class TestJDKSerialization extends BaseMapTest
+{
+    static class MyPojo {
+        public int x;
+        private int y;
+        
+        public MyPojo() { }
+        public MyPojo(int x0, int y0) {
+            x = x0;
+            y = y0;
+        }
+        
+        public int getY() { return y; }
+        public void setY(int y) { this.y = y; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests for individual objects
+    /**********************************************************
+     */
+
+    public void testConfigs() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        byte[] base = jdkSerialize(mapper.getDeserializationConfig().getBaseSettings());
+        assertNotNull(jdkDeserialize(base));
+
+        // first things first: underlying BaseSettings
+        
+        DeserializationConfig origDC = mapper.getDeserializationConfig();
+        SerializationConfig origSC = mapper.getSerializationConfig();
+        byte[] dcBytes = jdkSerialize(origDC);
+        byte[] scBytes = jdkSerialize(origSC);
+
+        DeserializationConfig dc = jdkDeserialize(dcBytes);
+        assertNotNull(dc);
+        assertEquals(dc._deserFeatures, origDC._deserFeatures);
+        SerializationConfig sc = jdkDeserialize(scBytes);
+        assertNotNull(sc);
+        assertEquals(sc._serFeatures, origSC._serFeatures);
+    }
+
+    public void testObjectWriter() throws IOException
+    {
+        ObjectWriter origWriter = new ObjectMapper().writer();
+        final String EXP_JSON = "{\"x\":2,\"y\":3}";
+        final MyPojo p = new MyPojo(2, 3);
+        assertEquals(EXP_JSON, origWriter.writeValueAsString(p));
+        byte[] bytes = jdkSerialize(origWriter);
+        ObjectWriter writer2 = jdkDeserialize(bytes);
+        assertEquals(EXP_JSON, writer2.writeValueAsString(p));
+    }
+    
+    public void testObjectReader() throws IOException
+    {
+        ObjectReader origReader = new ObjectMapper().reader(MyPojo.class);
+        final String JSON = "{\"x\":1,\"y\":2}";
+        MyPojo p1 = origReader.readValue(JSON);
+        assertEquals(2, p1.y);
+        byte[] bytes = jdkSerialize(origReader);
+        ObjectReader reader2 = jdkDeserialize(bytes);
+        MyPojo p2 = reader2.readValue(JSON);
+        assertEquals(2, p2.y);
+    }
+
+    public void testObjectMapper() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        final String EXP_JSON = "{\"x\":2,\"y\":3}";
+        final MyPojo p = new MyPojo(2, 3);
+        assertEquals(EXP_JSON, mapper.writeValueAsString(p));
+
+        byte[] bytes = jdkSerialize(mapper);
+        ObjectMapper mapper2 = jdkDeserialize(bytes);
+        assertEquals(EXP_JSON, mapper2.writeValueAsString(p));
+        MyPojo p2 = mapper2.readValue(EXP_JSON, MyPojo.class);
+        assertEquals(p.x, p2.x);
+        assertEquals(p.y, p2.y);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected byte[] jdkSerialize(Object o) throws IOException
+    {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream(1000);
+        ObjectOutputStream obOut = new ObjectOutputStream(bytes);
+        obOut.writeObject(o);
+        obOut.close();
+        return bytes.toByteArray();
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <T> T jdkDeserialize(byte[] raw) throws IOException
+    {
+        ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(raw));
+        try {
+            return (T) objIn.readObject();
+        } catch (ClassNotFoundException e) {
+            fail("Missing class: "+e.getMessage());
+            return null;
+        } finally {
+            objIn.close();
+        }
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestNamingStrategy.java b/src/test/java/com/fasterxml/jackson/databind/TestNamingStrategy.java
new file mode 100644
index 0000000..e14067a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestNamingStrategy.java
@@ -0,0 +1,257 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import com.fasterxml.jackson.databind.cfg.MapperConfig;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
+
+/**
+ * Unit tests to verify functioning of {@link PropertyNamingStrategy} which
+ * was added in Jackson 1.8, as per [JACKSON-178].
+ */
+ at SuppressWarnings("serial")
+public class TestNamingStrategy extends BaseMapTest
+{
+    /*
+    /**********************************************************************
+    /* Helper classes
+    /**********************************************************************
+     */
+
+	static class PrefixStrategy extends PropertyNamingStrategy
+    {
+        @Override
+        public String nameForField(MapperConfig<?> config,
+                AnnotatedField field, String defaultName)
+        {
+            return "Field-"+defaultName;
+        }
+
+        @Override
+        public String nameForGetterMethod(MapperConfig<?> config,
+                AnnotatedMethod method, String defaultName)
+        {
+            return "Get-"+defaultName;
+        }
+
+        @Override
+        public String nameForSetterMethod(MapperConfig<?> config,
+                AnnotatedMethod method, String defaultName)
+        {
+            return "Set-"+defaultName;
+        }
+    }
+    
+    static class CStyleStrategy extends PropertyNamingStrategy
+    {
+        @Override
+        public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
+        {
+            return convert(defaultName);
+        }
+
+        @Override
+        public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
+        {
+            return convert(defaultName);
+        }
+
+        @Override
+        public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
+        {
+            return convert(defaultName);
+        }
+
+        private String convert(String input)
+        {
+            // easy: replace capital letters with underscore, lower-cases equivalent
+            StringBuilder result = new StringBuilder();
+            for (int i = 0, len = input.length(); i < len; ++i) {
+                char c = input.charAt(i);
+                if (Character.isUpperCase(c)) {
+                    result.append('_');
+                    c = Character.toLowerCase(c);
+                }
+                result.append(c);
+            }
+            return result.toString();
+        }
+    }
+    
+    static class GetterBean {
+        public int getKey() { return 123; }
+    }
+
+    static class SetterBean {
+        protected int value;
+        
+        public void setKey(int v) {
+            value = v;
+        }
+    }
+
+    static class FieldBean {
+        public int key;
+
+        public FieldBean() { this(0); }
+        public FieldBean(int v) { key = v; }
+    }
+
+    @JsonPropertyOrder({"first_name", "last_name"})
+    static class PersonBean {
+        public String firstName;
+        public String lastName;
+        public int age;
+
+        public PersonBean() { this(null, null, 0); }
+        public PersonBean(String f, String l, int a)
+        {
+            firstName = f;
+            lastName = l;
+            age = a;
+        }
+    }
+
+    static class Value {
+        public int intValue;
+        
+        public Value() { this(0); }
+        public Value(int v) { intValue = v; }
+    }
+
+    static class SetterlessWithValue
+    {
+        protected ArrayList<Value> values = new ArrayList<Value>();
+
+        public List<Value> getValueList() { return values; }
+
+        public SetterlessWithValue add(int v) {
+            values.add(new Value(v));
+            return this;
+        }
+    }
+
+    // [JACKSON-687]
+    static class LcStrategy extends PropertyNamingStrategy.PropertyNamingStrategyBase
+    {
+        @Override
+        public String translate(String propertyName) {
+            return propertyName.toLowerCase();
+        }
+    }
+    
+    static class RenamedCollectionBean
+    {
+        @JsonDeserialize
+        private List<String> THEvalues = Collections.emptyList();
+        
+        // intentionally odd name, to be renamed by naming strategy
+        public List<String> getTheVALUEs() { return THEvalues; }
+    }
+
+    // [Issue#45]: Support @JsonNaming
+    @JsonNaming(PrefixStrategy.class)
+    static class BeanWithPrefixNames
+    {
+        protected int a = 3;
+        
+        public int getA() { return a; }
+        public void setA(int value) { a = value; }
+    }
+    
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+    
+    public void testSimpleGetters() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new PrefixStrategy());
+        assertEquals("{\"Get-key\":123}", mapper.writeValueAsString(new GetterBean()));
+    }
+
+    public void testSimpleSetters() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new PrefixStrategy());
+        SetterBean bean = mapper.readValue("{\"Set-key\":13}", SetterBean.class);
+        assertEquals(13, bean.value);
+    }
+
+    public void testSimpleFields() throws Exception
+    {
+        // First serialize
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new PrefixStrategy());
+        String json = mapper.writeValueAsString(new FieldBean(999));
+        assertEquals("{\"Field-key\":999}", json);
+
+        // then deserialize
+        FieldBean result = mapper.readValue(json, FieldBean.class);
+        assertEquals(999, result.key);
+    }
+
+    public void testCStyleNaming() throws Exception
+    {
+        // First serialize
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new CStyleStrategy());
+        String json = mapper.writeValueAsString(new PersonBean("Joe", "Sixpack", 42));
+        assertEquals("{\"first_name\":\"Joe\",\"last_name\":\"Sixpack\",\"age\":42}", json);
+        
+        // then deserialize
+        PersonBean result = mapper.readValue(json, PersonBean.class);
+        assertEquals("Joe", result.firstName);
+        assertEquals("Sixpack", result.lastName);
+        assertEquals(42, result.age);
+    }
+
+    public void testWithGetterAsSetter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new CStyleStrategy());
+        SetterlessWithValue input = new SetterlessWithValue().add(3);
+        String json = mapper.writeValueAsString(input);
+        assertEquals("{\"value_list\":[{\"int_value\":3}]}", json);
+
+        SetterlessWithValue result = mapper.readValue(json, SetterlessWithValue.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.size());
+        assertEquals(3, result.values.get(0).intValue);
+    }
+
+    // For [JACKSON-687]
+    public void testJson() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new LcStrategy());
+//        mapper.disable(DeserializationConfig.DeserializationFeature.USE_GETTERS_AS_SETTERS);
+        RenamedCollectionBean foo = mapper.readValue("{\"thevalues\":[\"a\"]}", RenamedCollectionBean.class);
+        assertNotNull(foo.getTheVALUEs());
+        assertEquals(1, foo.getTheVALUEs().size());
+        assertEquals("a", foo.getTheVALUEs().get(0));
+    }
+
+    // @JsonNaming / [Issue#45]
+    public void testPerClassAnnotation() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(new LcStrategy());
+        BeanWithPrefixNames input = new BeanWithPrefixNames();
+        String json = mapper.writeValueAsString(input);
+        assertEquals("{\"Get-a\":3}", json);
+
+        BeanWithPrefixNames output = mapper.readValue("{\"Set-a\":7}",
+                BeanWithPrefixNames.class);
+        assertEquals(7, output.a);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
new file mode 100644
index 0000000..6a6ab67
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapper.java
@@ -0,0 +1,161 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
+import com.fasterxml.jackson.databind.node.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestObjectMapper extends BaseMapTest
+{
+    static class Bean {
+        int value = 3;
+        
+        public void setX(int v) { value = v; }
+    }
+
+    // for [Issue#206]
+    @SuppressWarnings("serial")
+    static class CustomMapper extends ObjectMapper {
+        @Override
+        protected DefaultDeserializationContext createDeserializationContext(JsonParser jp,
+                DeserializationConfig cfg) {
+            return super.createDeserializationContext(jp, cfg);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    final static ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testProps()
+    {
+        ObjectMapper m = new ObjectMapper();
+        // should have default factory
+        assertNotNull(m.getNodeFactory());
+        JsonNodeFactory nf = JsonNodeFactory.instance;
+        m.setNodeFactory(nf);
+        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
+    {
+        ObjectMapper m = new ObjectMapper();
+        
+        // sort-alphabetically is disabled by default:
+        assertFalse(m.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
+        SerializationConfig sc = m.getSerializationConfig();
+        assertFalse(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
+        assertFalse(sc.shouldSortPropertiesAlphabetically());
+        DeserializationConfig dc = m.getDeserializationConfig();
+        assertFalse(dc.shouldSortPropertiesAlphabetically());
+
+        // but when enabled, should be visible:
+        m.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
+        sc = m.getSerializationConfig();
+        assertTrue(sc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
+        assertTrue(sc.shouldSortPropertiesAlphabetically());
+        dc = m.getDeserializationConfig();
+        // and not just via SerializationConfig, but also via DeserializationConfig
+        assertTrue(dc.isEnabled(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY));
+        assertTrue(dc.shouldSortPropertiesAlphabetically());
+    }
+
+
+    public void testJsonFactoryLinkage()
+    {
+        // first, implicit factory, giving implicit linkage
+        assertSame(MAPPER, MAPPER.getFactory().getCodec());
+
+        // and then explicit factory, which should also be implicitly linked
+        JsonFactory f = new JsonFactory();
+        ObjectMapper m = new ObjectMapper(f);
+        assertSame(f, m.getFactory());
+        assertSame(m, f.getCodec());
+    }
+    
+    /**
+     * Test for verifying working of [JACKSON-191]
+     */
+    public void testProviderConfig() throws Exception   
+    {
+        ObjectMapper m = new ObjectMapper();
+
+        assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
+        // and then should get one constructed for:
+        Bean bean = m.readValue("{ \"x\" : 3 }", Bean.class);
+        assertNotNull(bean);
+        assertEquals(1, m._deserializationContext._cache.cachedDeserializersCount());
+        m._deserializationContext._cache.flushCachedDeserializers();
+        assertEquals(0, m._deserializationContext._cache.cachedDeserializersCount());
+    }
+    
+    // [Issue#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));
+
+        // // 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));
+        // 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.addMixInAnnotations(String.class, Integer.class);
+        assertEquals(1, m.getSerializationConfig().mixInCount());
+        assertEquals(0, m2.getSerializationConfig().mixInCount());
+        assertEquals(1, m.getDeserializationConfig().mixInCount());
+        assertEquals(0, m2.getDeserializationConfig().mixInCount());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
new file mode 100644
index 0000000..6afdf87
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanDeserializer.java
@@ -0,0 +1,286 @@
+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;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * 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); }
+
+        @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; }
+    }
+
+    /**
+     * 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());
+    }
+
+    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());
+    }
+
+    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
new file mode 100644
index 0000000..d303f2c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestObjectMapperBeanSerializer.java
@@ -0,0 +1,237 @@
+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;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * 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")) {
+                assertToken(JsonToken.VALUE_STRING, t);
+                assertEquals(FixtureObject.VALUE_URSTR, getAndVerifyText(jp));
+            } else if (name.equals("url")) {
+                assertToken(JsonToken.VALUE_STRING, t);
+                assertEquals(FixtureObject.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(FixtureObject.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(FixtureObject.VALUE_ENUM.toString(),getAndVerifyText(jp));
+            } else if (name.equals("testInteger")) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, t);
+                assertEquals(jp.getIntValue(),FixtureObject.VALUE_INT);
+            } else if (name.equals("testLong")) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, t);
+                assertEquals(jp.getLongValue(),FixtureObject.VALUE_LONG);
+            } else if (name.equals("testBigInteger")) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, t);
+                assertEquals(jp.getLongValue(),FixtureObject.VALUE_BIGINT.longValue());
+            } else if (name.equals("testBigDecimal")) {
+                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
+                assertEquals(jp.getText(), FixtureObject.VALUE_BIGDEC.toString());
+            } else if (name.equals("testCharacter")) {
+                assertToken(JsonToken.VALUE_STRING, t);
+                assertEquals(String.valueOf(FixtureObject.VALUE_CHAR), getAndVerifyText(jp));
+            } else if (name.equals("testShort")) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, t);
+                assertEquals(jp.getIntValue(),FixtureObject.VALUE_SHORT);
+            } else if (name.equals("testByte")) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, t);
+                assertEquals(jp.getIntValue(),FixtureObject.VALUE_BYTE);
+            } else if (name.equals("testFloat")) {
+                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
+                assertEquals(jp.getDecimalValue().floatValue(),FixtureObject.VALUE_FLOAT);
+            } else if (name.equals("testDouble")) {
+                assertToken(JsonToken.VALUE_NUMBER_FLOAT, t);
+                assertEquals(jp.getDoubleValue(),FixtureObject.VALUE_DBL);
+            } else if (name.equals("testStringBuffer")) {
+                assertToken(JsonToken.VALUE_STRING, t);
+                assertEquals(FixtureObject.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(FixtureObject.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());
+    }
+
+    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,
+    }
+
+    @SuppressWarnings("unused")
+    private 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);
+        }
+    }
+
+    @SuppressWarnings("unused")
+    private static class FixtureObject extends FixtureObjectBase
+    {
+        public Exception getTestError()
+        {
+            return new Exception(VALUE_ERRTXT);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java b/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
new file mode 100644
index 0000000..92bd390
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestParserUsingMapper.java
@@ -0,0 +1,174 @@
+package com.fasterxml.jackson.databind;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestParserUsingMapper  extends com.fasterxml.jackson.test.BaseTest
+{
+    final static int TWO_BYTE_ESCAPED = 0x111;
+    final static int THREE_BYTE_ESCAPED = 0x1111;
+
+    final static SerializedString TWO_BYTE_ESCAPED_STRING = new SerializedString("&111;");
+    final static SerializedString THREE_BYTE_ESCAPED_STRING = new SerializedString("&1111;");
+    
+    final static class Pojo
+    {
+        int _x;
+
+        public void setX(int x) { _x = x; }
+    }
+    
+    /*
+    /********************************************************
+    /* Helper types
+    /********************************************************
+     */
+
+    /**
+     * Trivial simple custom escape definition set.
+     */
+    static class MyEscapes extends CharacterEscapes
+    {
+        private static final long serialVersionUID = 1L;
+
+        private final int[] _asciiEscapes;
+
+        public MyEscapes() {
+            _asciiEscapes = standardAsciiEscapesForJSON();
+            _asciiEscapes['a'] = 'A'; // to basically give us "\A"
+            _asciiEscapes['b'] = CharacterEscapes.ESCAPE_STANDARD; // too force "\u0062"
+            _asciiEscapes['d'] = CharacterEscapes.ESCAPE_CUSTOM;
+        }
+        
+        @Override
+        public int[] getEscapeCodesForAscii() {
+            return _asciiEscapes;
+        }
+
+        @Override
+        public SerializableString getEscapeSequence(int ch)
+        {
+            if (ch == 'd') {
+                return new SerializedString("[D]");
+            }
+            if (ch == TWO_BYTE_ESCAPED) {
+                return TWO_BYTE_ESCAPED_STRING;
+            }
+            if (ch == THREE_BYTE_ESCAPED) {
+                return THREE_BYTE_ESCAPED_STRING;
+            }
+            return null;
+        }
+    }
+    
+    /*
+    /********************************************************
+    /* Unit tests
+    /********************************************************
+     */
+
+    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();
+        final String JSON = "{ \"x\" : 9 }";
+        JsonParser jp = jf.createParser(new StringReader(JSON));
+
+        // let's try first by advancing:
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        Pojo p = jp.readValueAs(Pojo.class);
+        assertEquals(9, p._x);
+        jp.close();
+
+        // and without
+        jp = jf.createParser(new StringReader(JSON));
+        p = jp.readValueAs(Pojo.class);
+        assertEquals(9, p._x);
+        jp.close();
+    }
+
+    /**
+     * Test similar to above, but instead reads a sequence of values
+     */
+    public void testIncrementalPojoReading()
+        throws IOException
+    {
+        JsonFactory jf = new MappingJsonFactory();
+        final String JSON = "[ 1, true, null, \"abc\" ]";
+        JsonParser jp = jf.createParser(new StringReader(JSON));
+
+        // let's advance past array start to prevent full binding
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Integer.valueOf(1), jp.readValueAs(Integer.class));
+        assertEquals(Boolean.TRUE, jp.readValueAs(Boolean.class));
+        /* note: null can be returned both when there is no more
+         * data in current scope, AND when Json null literal is
+         * bound!
+         */
+        assertNull(jp.readValueAs(Object.class));
+        // but we can verify that it was Json null by:
+        assertEquals(JsonToken.VALUE_NULL, jp.getLastClearedToken());
+
+        assertEquals("abc", jp.readValueAs(String.class));
+
+        // this null is for actually hitting the END_ARRAY
+        assertNull(jp.readValueAs(Object.class));
+        assertEquals(JsonToken.END_ARRAY, jp.getLastClearedToken());
+
+        // afrer which there should be nothing to advance to:
+        assertNull(jp.nextToken());
+
+        jp.close();
+    }
+
+    public void testPojoReadingFailing()
+        throws IOException
+    {
+        // regular factory can't do it, without a call to setCodec()
+        JsonFactory jf = new JsonFactory();
+        try {
+            final String JSON = "{ \"x\" : 9 }";
+            JsonParser jp = jf.createParser(new StringReader(JSON));
+            Pojo p = jp.readValueAs(Pojo.class);
+            fail("Expected an exception: got "+p);
+        } catch (IllegalStateException e) {
+            verifyException(e, "No ObjectCodec defined");
+        }
+    }
+    
+    // for [JACKSON-672]
+    public void testEscapingUsingMapper() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
+        assertEquals(quote("\\u0101"), mapper.writeValueAsString(String.valueOf((char) 257)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java b/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
new file mode 100644
index 0000000..554cbd0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestReadValues.java
@@ -0,0 +1,237 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestReadValues extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static class Bean {
+        public int a;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests; root-level value sequences via Mapper
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testRootBeans() throws Exception
+    {
+        final String JSON = "{\"a\":3}{\"a\":27}  ";
+        Iterator<Bean> it = MAPPER.reader(Bean.class).readValues(JSON);
+
+        assertNotNull(((MappingIterator<?>) it).getCurrentLocation());
+        assertTrue(it.hasNext());
+        Bean b = it.next();
+        assertEquals(3, b.a);
+        assertTrue(it.hasNext());
+        b = it.next();
+        assertEquals(27, b.a);
+        assertFalse(it.hasNext());
+    }
+
+    public void testRootMaps() throws Exception
+    {
+        final String JSON = "{\"a\":3}{\"a\":27}  ";
+        Iterator<Map<?,?>> it = MAPPER.reader(Map.class).readValues(JSON);
+
+        assertNotNull(((MappingIterator<?>) it).getCurrentLocation());
+        assertTrue(it.hasNext());
+        Map<?,?> map = it.next();
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(3), map.get("a"));
+        assertTrue(it.hasNext());
+        assertNotNull(((MappingIterator<?>) it).getCurrentLocation());
+        map = it.next();
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(27), map.get("a"));
+        assertFalse(it.hasNext());
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests; root-level value sequences via JsonParser
+    /**********************************************************
+     */
+
+    public void testRootBeansWithParser() throws Exception
+    {
+        final String JSON = "{\"a\":3}{\"a\":27}  ";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+        
+        Iterator<Bean> it = jp.readValuesAs(Bean.class);
+
+        assertTrue(it.hasNext());
+        Bean b = it.next();
+        assertEquals(3, b.a);
+        assertTrue(it.hasNext());
+        b = it.next();
+        assertEquals(27, b.a);
+        assertFalse(it.hasNext());
+    }
+
+    public void testRootArraysWithParser() throws Exception
+    {
+        final String JSON = "[1][3]";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+
+        // NOTE: We must point JsonParser to the first element; if we tried to
+        // use "managed" accessor, it would try to advance past START_ARRAY.
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        
+        Iterator<int[]> it = MAPPER.reader(int[].class).readValues(jp);
+        assertTrue(it.hasNext());
+        int[] array = it.next();
+        assertEquals(1, array.length);
+        assertEquals(1, array[0]);
+        assertTrue(it.hasNext());
+        array = it.next();
+        assertEquals(1, array.length);
+        assertEquals(3, array[0]);
+        assertFalse(it.hasNext());
+    }
+    
+    public void testHasNextWithEndArray() throws Exception {
+        final String JSON = "[1,3]";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+
+        // NOTE: We must point JsonParser to the first element; if we tried to
+        // use "managed" accessor, it would try to advance past START_ARRAY.
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        jp.nextToken();
+        
+        Iterator<Integer> it = MAPPER.reader(Integer.class).readValues(jp);
+        assertTrue(it.hasNext());
+        int value = it.next();
+        assertEquals(1, value);
+        assertTrue(it.hasNext());
+        value = it.next();
+        assertEquals(3, value);
+        assertFalse(it.hasNext());
+        assertFalse(it.hasNext());
+    }
+    
+    public void testHasNextWithEndArrayManagedParser() throws Exception {
+        final String JSON = "[1,3]";
+
+        Iterator<Integer> it = MAPPER.reader(Integer.class).readValues(JSON);
+        assertTrue(it.hasNext());
+        int value = it.next();
+        assertEquals(1, value);
+        assertTrue(it.hasNext());
+        value = it.next();
+        assertEquals(3, value);
+        assertFalse(it.hasNext());
+        assertFalse(it.hasNext());
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests; non-root arrays
+    /**********************************************************
+     */
+
+    public void testNonRootBeans() throws Exception
+    {
+        final String JSON = "{\"leaf\":[{\"a\":3},{\"a\":27}]}";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        // can either advance to first START_OBJECT, or clear current token;
+        // explicitly passed JsonParser MUST point to the first token of
+        // the first element
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        
+        Iterator<Bean> it = MAPPER.reader(Bean.class).readValues(jp);
+
+        assertTrue(it.hasNext());
+        Bean b = it.next();
+        assertEquals(3, b.a);
+        assertTrue(it.hasNext());
+        b = it.next();
+        assertEquals(27, b.a);
+        assertFalse(it.hasNext());
+        jp.close();
+    }
+
+    public void testNonRootMapsWithParser() throws Exception
+    {
+        final String JSON = "[{\"a\":3},{\"a\":27}]";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+        // can either advance to first START_OBJECT, or clear current token;
+        // explicitly passed JsonParser MUST point to the first token of
+        // the first element
+        jp.clearCurrentToken();
+        
+        Iterator<Map<?,?>> it = MAPPER.reader(Map.class).readValues(jp);
+
+        assertTrue(it.hasNext());
+        Map<?,?> map = it.next();
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(3), map.get("a"));
+        assertTrue(it.hasNext());
+        map = it.next();
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(27), map.get("a"));
+        assertFalse(it.hasNext());
+        jp.close();
+    }
+
+    public void testNonRootMapsWithObjectReader() throws Exception
+    {
+        String JSON = "[{ \"hi\": \"ho\", \"neighbor\": \"Joe\" },\n"
+            +"{\"boy\": \"howdy\", \"huh\": \"what\"}]";
+        final MappingIterator<Map<String, Object>> iterator = MAPPER
+                .reader()
+                .withType(new TypeReference<Map<String, Object>>(){})
+                .readValues(JSON);
+
+        Map<String,Object> map;
+        assertTrue(iterator.hasNext());
+        map = iterator.nextValue();
+        assertEquals(2, map.size());
+        assertTrue(iterator.hasNext());
+        map = iterator.nextValue();
+        assertEquals(2, map.size());
+        assertFalse(iterator.hasNext());
+    }
+    
+    public void testNonRootArraysUsingParser() throws Exception
+    {
+        final String JSON = "[[1],[3]]";
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        
+        // Important: as of 2.1, START_ARRAY can only be skipped if the
+        // target type is NOT a Collection or array Java type.
+        // So we have to explicitly skip it in this particular case.
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        
+        Iterator<int[]> it = MAPPER.readValues(jp, int[].class);
+
+        assertTrue(it.hasNext());
+        int[] array = it.next();
+        assertEquals(1, array.length);
+        assertEquals(1, array[0]);
+        assertTrue(it.hasNext());
+        array = it.next();
+        assertEquals(1, array.length);
+        assertEquals(3, array[0]);
+        assertFalse(it.hasNext());
+        jp.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestRootName.java b/src/test/java/com/fasterxml/jackson/databind/TestRootName.java
new file mode 100644
index 0000000..38b0f00
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestRootName.java
@@ -0,0 +1,118 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Unit tests dealing with handling of "root element wrapping",
+ * including configuration of root name to use.
+ */
+public class TestRootName extends BaseMapTest
+{
+    @JsonRootName("rudy")
+    static class Bean {
+        public int a = 3;
+    }
+    
+    @JsonRootName("")
+    static class RootBeanWithEmpty {
+        public int a = 2;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testRootViaMapper() throws Exception
+    {
+        ObjectMapper mapper = rootMapper();
+        String json = mapper.writeValueAsString(new Bean());
+        assertEquals("{\"rudy\":{\"a\":3}}", json);
+        Bean bean = mapper.readValue(json, Bean.class);
+        assertNotNull(bean);
+
+        // also same with explicitly "not defined"...
+        json = mapper.writeValueAsString(new RootBeanWithEmpty());
+        assertEquals("{\"RootBeanWithEmpty\":{\"a\":2}}", json);
+        RootBeanWithEmpty bean2 = mapper.readValue(json, RootBeanWithEmpty.class);
+        assertNotNull(bean2);
+        assertEquals(2, bean2.a);
+    }
+
+    public void testRootViaWriterAndReader() throws Exception
+    {
+        ObjectMapper mapper = rootMapper();
+        String json = mapper.writer().writeValueAsString(new Bean());
+        assertEquals("{\"rudy\":{\"a\":3}}", json);
+        Bean bean = mapper.reader(Bean.class).readValue(json);
+        assertNotNull(bean);
+    }
+
+    public void testReconfiguringOfWrapping() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // default: no wrapping
+        final Bean input = new Bean();
+        String jsonUnwrapped = mapper.writeValueAsString(input);
+        assertEquals("{\"a\":3}", jsonUnwrapped);
+        // secondary: wrapping
+        String jsonWrapped = mapper.writer(SerializationFeature.WRAP_ROOT_VALUE)
+            .writeValueAsString(input);
+        assertEquals("{\"rudy\":{\"a\":3}}", jsonWrapped);
+
+        // and then similarly for readers:
+        Bean result = mapper.readValue(jsonUnwrapped, Bean.class);
+        assertNotNull(result);
+        try { // must not have extra wrapping
+            result = mapper.reader(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE)
+                .readValue(jsonUnwrapped);
+            fail("Should have failed");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Root name 'a'");
+        }
+        // except wrapping may be expected:
+        result = mapper.reader(Bean.class).with(DeserializationFeature.UNWRAP_ROOT_VALUE)
+            .readValue(jsonWrapped);
+        assertNotNull(result);
+    }
+    
+    // [JACKSON-764]
+    public void testRootUsingExplicitConfig() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectWriter writer = mapper.writer().withRootName("wrapper");
+        String json = writer.writeValueAsString(new Bean());
+        assertEquals("{\"wrapper\":{\"a\":3}}", json);
+
+        ObjectReader reader = mapper.reader(Bean.class).withRootName("wrapper");
+        Bean bean = reader.readValue(json);
+        assertNotNull(bean);
+
+        // also: verify that we can override SerializationFeature as well:
+        ObjectMapper wrapping = rootMapper();
+        json = wrapping.writer().withRootName("something").writeValueAsString(new Bean());
+        assertEquals("{\"something\":{\"a\":3}}", json);
+        json = wrapping.writer().withRootName("").writeValueAsString(new Bean());
+        assertEquals("{\"a\":3}", json);
+
+        bean = wrapping.reader(Bean.class).withRootName("").readValue(json);
+        assertNotNull(bean);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private ObjectMapper rootMapper()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
+        mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
+        return mapper;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java b/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java
new file mode 100644
index 0000000..5d0d2fb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestStdDateFormat.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+
+public class TestStdDateFormat
+    extends BaseMapTest
+{
+    public void testFactories() {
+        assertNotNull(StdDateFormat.getBlueprintISO8601Format());
+        assertNotNull(StdDateFormat.getBlueprintRFC1123Format());
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        assertNotNull(StdDateFormat.getISO8601Format(tz));
+        assertNotNull(StdDateFormat.getRFC1123Format(tz));
+    }
+
+    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/TestStdNamingStrategies.java b/src/test/java/com/fasterxml/jackson/databind/TestStdNamingStrategies.java
new file mode 100644
index 0000000..7795178
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestStdNamingStrategies.java
@@ -0,0 +1,269 @@
+package com.fasterxml.jackson.databind;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.TestNamingStrategy.PersonBean;
+
+/**
+ * Unit tests to verify functioning of 
+ * {@link PropertyNamingStrategy#CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES} 
+ * inside the context of an ObjectMapper.
+ * CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES was added in Jackson 1.9, 
+ * as per [JACKSON-598].
+ * 
+ * and
+ * 
+ * Unit test to verify translations of 
+ * {@link PropertyNamingStrategy#PASCAL_CASE_TO_CAMEL_CASE } 
+ * outside the context of an ObjectMapper.
+ * PASCAL_CASE_TO_CAMEL_CASE was added in Jackson 2.1.0, 
+ * as per [JACKSON-63].
+ * 
+ * @since 2.1.0
+ */
+public class TestStdNamingStrategies extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    @JsonPropertyOrder({"www", "some_url", "some_uris"})
+    static class Acronyms
+    {
+        public String WWW;
+        public String someURL;
+        public String someURIs;
+        
+        public Acronyms() {this(null, null, null);}
+        public Acronyms(String WWW, String someURL, String someURIs)
+        {
+            this.WWW = WWW;
+            this.someURL = someURL;
+            this.someURIs = someURIs;
+        }
+    }
+    
+    @JsonPropertyOrder({"from_user", "user", "from$user", "from7user", "_"})
+    static class UnchangedNames
+    {
+        public String from_user;
+        public String _user;
+        public String from$user;
+        public String from7user;
+        public String _;
+        
+        public UnchangedNames() {this(null, null, null, null, null);}
+        public UnchangedNames(String from_user, String _user, String from$user, String from7user, String _)
+        {
+            this.from_user = from_user;
+            this._user = _user;
+            this.from$user = from$user;
+            this.from7user = from7user;
+            this._ = _;
+        }
+    }
+    
+    @JsonPropertyOrder({"results", "user", "__", "$_user"})
+    static class OtherNonStandardNames
+    {
+        public String Results;
+        public String _User;
+        public String ___;
+        public String $User;
+        
+        public OtherNonStandardNames() {this(null, null, null, null);}
+        public OtherNonStandardNames(String Results, String _User, String ___, String $User)
+        {
+            this.Results = Results;
+            this._User = _User;
+            this.___ = ___;
+            this.$User = $User;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Set up
+    /**********************************************************
+     */
+
+    public static List<Object[]> NAME_TRANSLATIONS = Arrays.asList(new Object[][] {
+                {null, null},
+                {"", ""},
+                {"a", "a"},
+                {"abc", "abc"},
+                {"1", "1"},
+                {"123", "123"},
+                {"1a", "1a"},
+                {"a1", "a1"},
+                {"$", "$"},
+                {"$a", "$a"},
+                {"a$", "a$"},
+                {"$_a", "$_a"},
+                {"a_$", "a_$"},
+                {"a$a", "a$a"},
+                {"$A", "$_a"},
+                {"$_A", "$_a"},
+                {"_", "_"},
+                {"__", "_"},
+                {"___", "__"},
+                {"A", "a"},
+                {"A1", "a1"},
+                {"1A", "1_a"},
+                {"_a", "a"},
+                {"_A", "a"},
+                {"a_a", "a_a"},
+                {"a_A", "a_a"},
+                {"A_A", "a_a"},
+                {"A_a", "a_a"},
+                {"WWW", "www"},
+                {"someURI", "some_uri"},
+                {"someURIs", "some_uris"},
+                {"Results", "results"},
+                {"_Results", "results"},
+                {"_results", "results"},
+                {"__results", "_results"},
+                {"__Results", "_results"},
+                {"___results", "__results"},
+                {"___Results", "__results"},
+                {"userName", "user_name"},
+                {"user_name", "user_name"},
+                {"user__name", "user__name"},
+                {"UserName", "user_name"},
+                {"User_Name", "user_name"},
+                {"User__Name", "user__name"},
+                {"_user_name", "user_name"},
+                {"_UserName", "user_name"},
+                {"_User_Name", "user_name"},
+                {"UGLY_NAME", "ugly_name"},
+                {"_Bars", "bars" }
+    });
+    
+    private ObjectMapper mapper;
+    
+    @Override
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        mapper = new ObjectMapper();
+        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods for CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
+    /**********************************************************
+     */
+
+    /**
+     * Unit test to verify translations of 
+     * {@link PropertyNamingStrategy#CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES} 
+     * outside the context of an ObjectMapper.
+     * CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES was added in Jackson 1.9, 
+     * as per [JACKSON-598].
+     */
+    @Test
+    public void testLowerCaseStrategyStandAlone()
+    {
+        for (Object[] pair : NAME_TRANSLATIONS) {
+            String translatedJavaName = PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES.nameForField(null, null,
+                    (String) pair[0]);
+            assertEquals((String) pair[1], translatedJavaName);
+        }
+    }
+    
+    public void testLowerCaseTranslations() throws Exception
+    {
+        // First serialize
+        String json = mapper.writeValueAsString(new PersonBean("Joe", "Sixpack", 42));
+        assertEquals("{\"first_name\":\"Joe\",\"last_name\":\"Sixpack\",\"age\":42}", json);
+        
+        // then deserialize
+        PersonBean result = mapper.readValue(json, PersonBean.class);
+        assertEquals("Joe", result.firstName);
+        assertEquals("Sixpack", result.lastName);
+        assertEquals(42, result.age);
+    }
+    
+    public void testLowerCaseAcronymsTranslations() throws Exception
+    {
+        // First serialize
+        String json = mapper.writeValueAsString(new Acronyms("world wide web", "http://jackson.codehaus.org", "/path1/,/path2/"));
+        assertEquals("{\"www\":\"world wide web\",\"some_url\":\"http://jackson.codehaus.org\",\"some_uris\":\"/path1/,/path2/\"}", json);
+        
+        // then deserialize
+        Acronyms result = mapper.readValue(json, Acronyms.class);
+        assertEquals("world wide web", result.WWW);
+        assertEquals("http://jackson.codehaus.org", result.someURL);
+        assertEquals("/path1/,/path2/", result.someURIs);
+    }
+
+    public void testLowerCaseOtherNonStandardNamesTranslations() throws Exception
+    {
+        // First serialize
+        String json = mapper.writeValueAsString(new OtherNonStandardNames("Results", "_User", "___", "$User"));
+        assertEquals("{\"results\":\"Results\",\"user\":\"_User\",\"__\":\"___\",\"$_user\":\"$User\"}", json);
+        
+        // then deserialize
+        OtherNonStandardNames result = mapper.readValue(json, OtherNonStandardNames.class);
+        assertEquals("Results", result.Results);
+        assertEquals("_User", result._User);
+        assertEquals("___", result.___);
+        assertEquals("$User", result.$User);
+    }
+
+    public void testLowerCaseUnchangedNames() throws Exception
+    {
+        // First serialize
+        String json = mapper.writeValueAsString(new UnchangedNames("from_user", "_user", "from$user", "from7user", "_"));
+        assertEquals("{\"from_user\":\"from_user\",\"user\":\"_user\",\"from$user\":\"from$user\",\"from7user\":\"from7user\",\"_\":\"_\"}", json);
+        
+        // then deserialize
+        UnchangedNames result = mapper.readValue(json, UnchangedNames.class);
+        assertEquals("from_user", result.from_user);
+        assertEquals("_user", result._user);
+        assertEquals("from$user", result.from$user);
+        assertEquals("from7user", result.from7user);
+        assertEquals("_", result._);
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods for PASCAL_CASE_TO_CAMEL_CASE (added in 2.1)
+    /**********************************************************
+     */
+
+    /**
+     * Unit test to verify translations of 
+     * {@link PropertyNamingStrategy#PASCAL_CASE_TO_CAMEL_CASE } 
+     * outside the context of an ObjectMapper.
+     * PASCAL_CASE_TO_CAMEL_CASE was added in Jackson 2.1.0, 
+     * as per [JACKSON-63].
+     */
+    @Test
+    public void testPascalCaseStandAlone()
+    {
+    	String translatedJavaName = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE.nameForField
+    	        (null, null, "userName");
+        assertEquals("UserName", translatedJavaName);
+
+        translatedJavaName = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE.nameForField
+                (null, null, "User");
+        assertEquals("User", translatedJavaName);
+
+        translatedJavaName = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE.nameForField
+                (null, null, "user");
+        assertEquals("User", translatedJavaName);
+        translatedJavaName = PropertyNamingStrategy.PASCAL_CASE_TO_CAMEL_CASE.nameForField
+                (null, null, "x");
+        assertEquals("X", translatedJavaName);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/TestVersions.java b/src/test/java/com/fasterxml/jackson/databind/TestVersions.java
new file mode 100644
index 0000000..5b5df44
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/TestVersions.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.databind;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.Versioned;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.cfg.PackageVersion;
+
+/**
+ * Tests to ensure that we get proper Version information via
+ * things defined as Versioned.
+ */
+public class TestVersions extends com.fasterxml.jackson.test.BaseTest
+{
+    public void testMapperVersions()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertVersion(mapper);
+        assertVersion(mapper.reader());
+        assertVersion(mapper.writer());
+        assertVersion(new JacksonAnnotationIntrospector());
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private void assertVersion(Versioned vers)
+    {
+        Version v = vers.version();
+        assertFalse("Should find version information (got "+v+")", v.isUknownVersion());
+        Version exp = PackageVersion.VERSION;
+        assertEquals(exp.toFullString(), v.toFullString());
+        assertEquals(exp, v);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java b/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
new file mode 100644
index 0000000..66b5b86
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/access/TestSerAnyGetter.java
@@ -0,0 +1,73 @@
+package com.fasterxml.jackson.databind.access;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Separate tests located in different package than code being
+ * exercised; needed to trigger some access-related failures.
+ */
+public class TestSerAnyGetter
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class DynaBean {
+        public int id;
+
+        protected HashMap<String,String> other = new HashMap<String,String>();
+        
+        @JsonAnyGetter
+        public Map<String,String> any() {
+            return other;
+        }
+
+        @JsonAnySetter
+        public void set(String name, String value) {
+            other.put(name, value);
+        }
+    }
+
+    private static class PrivateThing
+    {
+        @JsonAnyGetter
+        public Map<?,?> getProperties()
+        {
+            HashMap<String,String> map = new HashMap<String,String>();
+            map.put("a", "A");
+            return map;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test cases
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testDynaBean() throws Exception
+    {
+        DynaBean b = new DynaBean();
+        b.id = 123;
+        b.set("name", "Billy");
+        assertEquals("{\"id\":123,\"name\":\"Billy\"}", MAPPER.writeValueAsString(b));
+
+        DynaBean result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\"}", DynaBean.class);
+        assertEquals(2, result.id);
+        assertEquals("Joe", result.other.get("name"));
+    }
+
+    public void testPrivate() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new PrivateThing());
+        assertEquals("{\"a\":\"A\"}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java
new file mode 100644
index 0000000..9d3a4d7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualDeserialization.java
@@ -0,0 +1,277 @@
+package com.fasterxml.jackson.databind.contextual;
+
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * Test cases to verify that it is possible to define deserializers
+ * that can use contextual information (like field/method
+ * annotations) for configuration.
+ */
+public class TestContextualDeserialization extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /* NOTE: important; MUST be considered a 'Jackson' annotation to be seen
+     * (or recognized otherwise via AnnotationIntrospect.isHandled())
+     */
+    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotation
+    public @interface Name {
+        public String value();
+    }
+    
+    static class StringValue {
+        protected String value;
+        
+        public StringValue(String v) { value = v; }
+    }
+    
+    static class ContextualBean
+    {
+        @Name("NameA")
+        public StringValue a;
+        @Name("NameB")
+        public StringValue b;
+    }
+    
+    static class ContextualCtorBean
+    {
+        protected String a, b;
+
+        @JsonCreator
+        public ContextualCtorBean(
+                @Name("CtorA") @JsonProperty("a") StringValue a,
+                @Name("CtorB") @JsonProperty("b") StringValue b)
+        {
+            this.a = a.value;
+            this.b = b.value;
+        }
+    }
+
+    @Name("Class")
+    static class ContextualClassBean
+    {
+        public StringValue a;
+
+        @Name("NameB")
+        public StringValue b;
+    }
+    
+    static class ContextualArrayBean
+    {
+        @Name("array")
+        public StringValue[] beans;
+    }
+    
+    static class ContextualListBean
+    {
+        @Name("list")
+        public List<StringValue> beans;
+    }
+    
+    static class ContextualMapBean
+    {
+        @Name("map")
+        public Map<String, StringValue> beans;
+    }
+    
+    static class MyContextualDeserializer
+        extends JsonDeserializer<StringValue>
+        implements ContextualDeserializer
+    {
+        protected final String _fieldName;
+        
+        public MyContextualDeserializer() { this(""); }
+        public MyContextualDeserializer(String fieldName) {
+            _fieldName = fieldName;
+        }
+
+        @Override
+        public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            return new StringValue(""+_fieldName+"="+jp.getText());
+        }
+
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property)
+            throws JsonMappingException
+        {
+            String name = (property == null) ? "NULL" : property.getName();
+            return new MyContextualDeserializer(name);
+        }
+    }
+
+    /**
+     * Alternative that uses annotation for choosing name to use
+     */
+    static class AnnotatedContextualDeserializer
+        extends JsonDeserializer<StringValue>
+        implements ContextualDeserializer
+    {
+        protected final String _fieldName;
+        
+        public AnnotatedContextualDeserializer() { this(""); }
+        public AnnotatedContextualDeserializer(String fieldName) {
+            _fieldName = fieldName;
+        }
+    
+        @Override
+        public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            return new StringValue(""+_fieldName+"="+jp.getText());
+        }
+    
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property)
+            throws JsonMappingException
+        {
+            Name ann = property.getAnnotation(Name.class);
+            if (ann == null) {
+                ann = property.getContextAnnotation(Name.class);
+            }
+            String propertyName = (ann == null) ?  "UNKNOWN" : ann.value();
+            return new MyContextualDeserializer(propertyName);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimple() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addDeserializer(StringValue.class, new MyContextualDeserializer());
+        mapper.registerModule(module);
+        ContextualBean bean = mapper.readValue("{\"a\":\"1\",\"b\":\"2\"}", ContextualBean.class);
+        assertEquals("a=1", bean.a.value);
+        assertEquals("b=2", bean.b.value);
+
+        // try again, to ensure caching etc works
+        bean = mapper.readValue("{\"a\":\"3\",\"b\":\"4\"}", ContextualBean.class);
+        assertEquals("a=3", bean.a.value);
+        assertEquals("b=4", bean.b.value);
+    }
+
+    public void testSimpleWithAnnotations() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualBean bean = mapper.readValue("{\"a\":\"1\",\"b\":\"2\"}", ContextualBean.class);
+        assertEquals("NameA=1", bean.a.value);
+        assertEquals("NameB=2", bean.b.value);
+
+        // try again, to ensure caching etc works
+        bean = mapper.readValue("{\"a\":\"x\",\"b\":\"y\"}", ContextualBean.class);
+        assertEquals("NameA=x", bean.a.value);
+        assertEquals("NameB=y", bean.b.value);
+    }
+
+    public void testSimpleWithClassAnnotations() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualClassBean bean = mapper.readValue("{\"a\":\"1\",\"b\":\"2\"}", ContextualClassBean.class);
+        assertEquals("Class=1", bean.a.value);
+        assertEquals("NameB=2", bean.b.value);
+        // and again
+        bean = mapper.readValue("{\"a\":\"123\",\"b\":\"345\"}", ContextualClassBean.class);
+        assertEquals("Class=123", bean.a.value);
+        assertEquals("NameB=345", bean.b.value);
+    }
+    
+    public void testAnnotatedCtor() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualCtorBean bean = mapper.readValue("{\"a\":\"foo\",\"b\":\"bar\"}", ContextualCtorBean.class);
+        assertEquals("CtorA=foo", bean.a);
+        assertEquals("CtorB=bar", bean.b);
+
+        bean = mapper.readValue("{\"a\":\"1\",\"b\":\"0\"}", ContextualCtorBean.class);
+        assertEquals("CtorA=1", bean.a);
+        assertEquals("CtorB=0", bean.b);
+    }
+
+    public void testAnnotatedArray() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualArrayBean bean = mapper.readValue("{\"beans\":[\"x\"]}", ContextualArrayBean.class);
+        assertEquals(1, bean.beans.length);
+        assertEquals("array=x", bean.beans[0].value);
+
+        bean = mapper.readValue("{\"beans\":[\"a\",\"b\"]}", ContextualArrayBean.class);
+        assertEquals(2, bean.beans.length);
+        assertEquals("array=a", bean.beans[0].value);
+        assertEquals("array=b", bean.beans[1].value);
+    }
+
+    public void testAnnotatedList() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualListBean bean = mapper.readValue("{\"beans\":[\"x\"]}", ContextualListBean.class);
+        assertEquals(1, bean.beans.size());
+        assertEquals("list=x", bean.beans.get(0).value);
+
+        bean = mapper.readValue("{\"beans\":[\"x\",\"y\",\"z\"]}", ContextualListBean.class);
+        assertEquals(3, bean.beans.size());
+        assertEquals("list=x", bean.beans.get(0).value);
+        assertEquals("list=y", bean.beans.get(1).value);
+        assertEquals("list=z", bean.beans.get(2).value);
+    }
+
+    public void testAnnotatedMap() throws Exception
+    {
+        ObjectMapper mapper = _mapperWithAnnotatedContextual();
+        ContextualMapBean bean = mapper.readValue("{\"beans\":{\"a\":\"b\"}}", ContextualMapBean.class);
+        assertEquals(1, bean.beans.size());
+        Map.Entry<String,StringValue> entry = bean.beans.entrySet().iterator().next();
+        assertEquals("a", entry.getKey());
+        assertEquals("map=b", entry.getValue().value);
+
+        bean = mapper.readValue("{\"beans\":{\"x\":\"y\",\"1\":\"2\"}}", ContextualMapBean.class);
+        assertEquals(2, bean.beans.size());
+        Iterator<Map.Entry<String,StringValue>> it = bean.beans.entrySet().iterator();
+        entry = it.next();
+        assertEquals("x", entry.getKey());
+        assertEquals("map=y", entry.getValue().value);
+        entry = it.next();
+        assertEquals("1", entry.getKey());
+        assertEquals("map=2", entry.getValue().value);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private ObjectMapper _mapperWithAnnotatedContextual()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addDeserializer(StringValue.class, new AnnotatedContextualDeserializer());
+        mapper.registerModule(module);
+        return mapper;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java
new file mode 100644
index 0000000..3d56412
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualKeyTypes.java
@@ -0,0 +1,124 @@
+package com.fasterxml.jackson.databind.contextual;
+
+import java.io.IOException;
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualKeyDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Tests to ensure that we can do contextual key serializers and
+ * deserializers as well as value ser/deser.
+ */
+public class TestContextualKeyTypes extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class ContextualKeySerializer
+        extends JsonSerializer<String>
+        implements ContextualSerializer
+    {
+        protected final String _prefix;
+    
+        public ContextualKeySerializer() { this(""); }
+        public ContextualKeySerializer(String p) {
+            _prefix = p;
+        }
+
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+        {
+            if (_prefix != null) {
+                value = _prefix + value;
+            }
+            jgen.writeFieldName(value);
+        }
+
+        @Override
+        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
+            throws JsonMappingException
+        {
+            return new ContextualKeySerializer(_prefix+":");
+        }
+    }
+
+    static class ContextualDeser
+        extends KeyDeserializer
+        implements ContextualKeyDeserializer
+    {
+        protected final String _prefix;
+        
+        protected ContextualDeser(String p) {
+            _prefix = p;
+        }        
+
+        @Override
+        public Object deserializeKey(String key, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            return _prefix + ":" + key;
+        }
+
+        @Override
+        public KeyDeserializer createContextual(DeserializationContext ctxt,
+                BeanProperty property) throws JsonMappingException
+        {
+            return new ContextualDeser((property == null) ? "ROOT" : property.getName());
+        }
+    }
+
+    static class MapBean {
+        public Map<String, Integer> map;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests, serialization
+    /**********************************************************
+     */
+
+    public void testSimpleKeySer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addKeySerializer(String.class, new ContextualKeySerializer("prefix"));
+        mapper.registerModule(module);
+        Map<String,Object> input = new HashMap<String,Object>();
+        input.put("a", Integer.valueOf(3));
+        String json = mapper.writerWithType(TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, Object.class))
+            .writeValueAsString(input);
+        assertEquals("{\"prefix:a\":3}", json);
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests, deserialization
+    /**********************************************************
+     */
+
+    public void testSimpleKeyDeser() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addKeyDeserializer(String.class, new ContextualDeser("???"));
+        mapper.registerModule(module);
+        MapBean result = mapper.readValue("{\"map\":{\"a\":3}}", MapBean.class);
+        Map<String,Integer> map = result.map;
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        Map.Entry<String,Integer> entry = map.entrySet().iterator().next();
+        assertEquals(Integer.valueOf(3), entry.getValue());
+        assertEquals("map:a", entry.getKey());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java
new file mode 100644
index 0000000..b1fd92b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualSerialization.java
@@ -0,0 +1,291 @@
+package com.fasterxml.jackson.databind.contextual;
+
+import java.io.IOException;
+import java.lang.annotation.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
+
+/**
+ * Test cases to verify that it is possible to define serializers
+ * that can use contextual information (like field/method
+ * annotations) for configuration.
+ */
+public class TestContextualSerialization extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /* NOTE: important; MUST be considered a 'Jackson' annotation to be seen
+     * (or recognized otherwise via AnnotationIntrospect.isHandled())
+     */
+    @Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotation
+    public @interface Prefix {
+        public String value();
+    }
+
+    static class ContextualBean
+    {
+        protected final String _value;
+
+        public ContextualBean(String s) { _value = s; }
+
+        @Prefix("see:")
+        public String getValue() { return _value; }
+    }
+
+    // For [JACKSON-569]
+    static class AnnotatedContextualBean
+    {
+        @Prefix("prefix->")
+        @JsonSerialize(using=AnnotatedContextualSerializer.class)
+        protected final String value;
+
+        public AnnotatedContextualBean(String s) { value = s; }
+    }
+
+    
+    @Prefix("wrappedBean:")
+    static class ContextualBeanWrapper
+    {
+        @Prefix("wrapped:")
+        public ContextualBean wrapped;
+        
+        public ContextualBeanWrapper(String s) {
+            wrapped = new ContextualBean(s);
+        }
+    }
+    
+    static class ContextualArrayBean
+    {
+        @Prefix("array->")
+        public final String[] beans;
+        
+        public ContextualArrayBean(String... strings) {
+            beans = strings;
+        }
+    }
+
+    static class ContextualArrayElementBean
+    {
+        @Prefix("elem->")
+        @JsonSerialize(contentUsing=AnnotatedContextualSerializer.class)
+        public final String[] beans;
+        
+        public ContextualArrayElementBean(String... strings) {
+            beans = strings;
+        }
+    }
+    
+    static class ContextualListBean
+    {
+        @Prefix("list->")
+        public final List<String> beans = new ArrayList<String>();
+
+        public ContextualListBean(String... strings) {
+            for (String string : strings) {
+                beans.add(string);
+            }
+        }
+    }
+    
+    static class ContextualMapBean
+    {
+        @Prefix("map->")
+        public final Map<String, String> beans = new HashMap<String, String>();
+    }
+    
+    /**
+     * Another bean that has class annotations that should be visible for
+     * contextualizer, too
+     */
+    @Prefix("Voila->")
+    static class BeanWithClassConfig
+    {
+        public String value;
+
+        public BeanWithClassConfig(String v) { value = v; }
+    }
+    
+    /**
+     * Annotation-based contextual serializer that simply prepends piece of text.
+     */
+    static class AnnotatedContextualSerializer
+        extends JsonSerializer<String>
+        implements ContextualSerializer
+    {
+        protected final String _prefix;
+        
+        public AnnotatedContextualSerializer() { this(""); }
+        public AnnotatedContextualSerializer(String p) {
+            _prefix = p;
+        }
+
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+        {
+            jgen.writeString(_prefix + value);
+        }
+
+        @Override
+        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
+                throws JsonMappingException
+        {
+            String prefix = "UNKNOWN";
+            Prefix ann = null;
+            if (property != null) {
+                ann = property.getAnnotation(Prefix.class);
+                if (ann == null) {
+                    ann = property.getContextAnnotation(Prefix.class);
+                }
+            }
+            if (ann != null) {
+                prefix = ann.value();
+            }
+            return new AnnotatedContextualSerializer(prefix);
+        }
+    }
+
+    static class ContextualAndResolvable
+        extends JsonSerializer<String>
+        implements ContextualSerializer, ResolvableSerializer
+    {
+        protected int isContextual;
+        protected int isResolved;
+
+        public ContextualAndResolvable() { this(0, 0); }
+        
+        public ContextualAndResolvable(int resolved, int contextual)
+        {
+            isContextual = contextual;
+            isResolved = resolved;
+        }
+        
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+        {
+            jgen.writeString("contextual="+isContextual+",resolved="+isResolved);
+        }
+
+        @Override
+        public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
+                throws JsonMappingException
+        {
+            return new ContextualAndResolvable(isResolved, isContextual+1);
+        }
+
+        @Override
+        public void resolve(SerializerProvider provider) {
+            ++isResolved;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    // Test to verify that contextual serializer can make use of property
+    // (method, field) annotations.
+    public void testMethodAnnotations() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        assertEquals("{\"value\":\"see:foobar\"}", mapper.writeValueAsString(new ContextualBean("foobar")));
+    }
+
+    // Test to verify that contextual serializer can also use annotations
+    // for enclosing class.
+    public void testClassAnnotations() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        assertEquals("{\"value\":\"Voila->xyz\"}", mapper.writeValueAsString(new BeanWithClassConfig("xyz")));
+    }
+
+    public void testWrappedBean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        assertEquals("{\"wrapped\":{\"value\":\"see:xyz\"}}", mapper.writeValueAsString(new ContextualBeanWrapper("xyz")));
+    }
+    
+    // Serializer should get passed property context even if contained in an array.
+    public void testMethodAnnotationInArray() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        ContextualArrayBean beans = new ContextualArrayBean("123");
+        assertEquals("{\"beans\":[\"array->123\"]}", mapper.writeValueAsString(beans));
+    }
+
+    // Serializer should get passed property context even if contained in a Collection.
+    public void testMethodAnnotationInList() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        ContextualListBean beans = new ContextualListBean("abc");
+        assertEquals("{\"beans\":[\"list->abc\"]}", mapper.writeValueAsString(beans));
+    }
+
+    // Serializer should get passed property context even if contained in a Collection.
+    public void testMethodAnnotationInMap() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new AnnotatedContextualSerializer());
+        mapper.registerModule(module);
+        ContextualMapBean map = new ContextualMapBean();
+        map.beans.put("first", "In Map");
+        assertEquals("{\"beans\":{\"first\":\"map->In Map\"}}", mapper.writeValueAsString(map));
+    }
+
+    public void testContextualViaAnnotation() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotatedContextualBean bean = new AnnotatedContextualBean("abc");
+        assertEquals("{\"value\":\"prefix->abc\"}", mapper.writeValueAsString(bean));
+    }
+
+    /*
+    // [JACKSON-647]: is resolve() called for contextual instances?
+    public void testResolveOnContextual() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(String.class, new ContextualAndResolvable());
+        mapper.registerModule(module);
+        assertEquals(quote("contextual=1,resolved=1"), mapper.writeValueAsString("abc"));
+    }
+
+    public void testContextualArrayElement() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ContextualArrayElementBean beans = new ContextualArrayElementBean("456");
+        assertEquals("{\"beans\":[\"elem->456\"]}", mapper.writeValueAsString(beans));
+    }
+    */
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java
new file mode 100644
index 0000000..d20d37d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/contextual/TestContextualWithAnnDeserializer.java
@@ -0,0 +1,107 @@
+package com.fasterxml.jackson.databind.contextual;
+
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.fasterxml.jackson.annotation.JacksonAnnotation;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+
+public class TestContextualWithAnnDeserializer extends BaseMapTest
+{
+    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotation
+    public @interface Name {
+        public String value();
+    }
+    
+    static class StringValue {
+        protected String value;
+        
+        public StringValue(String v) { value = v; }
+    }
+
+    static class AnnotatedContextualClassBean
+    {
+        @Name("xyz")
+        @JsonDeserialize(using=AnnotatedContextualDeserializer.class)
+        public StringValue value;
+    }
+    
+    static class AnnotatedContextualDeserializer
+        extends JsonDeserializer<StringValue>
+        implements ContextualDeserializer
+    {
+        protected final String _fieldName;
+        
+        public AnnotatedContextualDeserializer() { this(""); }
+        public AnnotatedContextualDeserializer(String fieldName) {
+            _fieldName = fieldName;
+        }
+
+        @Override
+        public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            return new StringValue(""+_fieldName+"="+jp.getText());
+        }
+
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property)
+            throws JsonMappingException
+        {
+            Name ann = property.getAnnotation(Name.class);
+            if (ann == null) {
+                ann = property.getContextAnnotation(Name.class);
+            }
+            String propertyName = (ann == null) ?  "UNKNOWN" : ann.value();
+            return new MyContextualDeserializer(propertyName);
+        }
+    }
+
+    static class MyContextualDeserializer
+        extends JsonDeserializer<StringValue>
+        implements ContextualDeserializer
+    {
+        protected final String _fieldName;
+        
+        public MyContextualDeserializer() { this(""); }
+        public MyContextualDeserializer(String fieldName) {
+            _fieldName = fieldName;
+        }
+    
+        @Override
+        public StringValue deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            return new StringValue(""+_fieldName+"="+jp.getText());
+        }
+
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property)
+            throws JsonMappingException
+        {
+            String name = (property == null) ? "NULL" : property.getName();
+            return new MyContextualDeserializer(name);
+        }
+    }
+
+    
+    // ensure that direct associations also work
+    public void testAnnotatedContextual() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotatedContextualClassBean bean = mapper.readValue(
+                "{\"value\":\"a\"}",
+              AnnotatedContextualClassBean.class);
+        assertNotNull(bean);
+        assertEquals("xyz=a", bean.value.value);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
new file mode 100644
index 0000000..ab7f50a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestArrayConversions.java
@@ -0,0 +1,216 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.math.*;
+import java.util.*;
+import java.lang.reflect.Array;
+
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+public class TestArrayConversions
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final static String OVERFLOW_MSG_BYTE = "out of range of Java byte";
+    final static String OVERFLOW_MSG = "overflow";
+
+    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));
+    }
+
+    /**
+     * Tests to verify that primitive number arrays round-trip
+     * correctly, i.e. type -> type gives equal (although
+     * not necessarily same) output
+     */
+    public void testArrayIdentityTransforms() throws Exception
+    {
+        // first integral types
+        // (note: byte[] is ok, even if it goes to base64 and back)
+        verifyByteArrayConversion(bytes(), byte[].class);
+        verifyShortArrayConversion(shorts(), short[].class);
+        verifyIntArrayConversion(ints(), int[].class);
+        verifyLongArrayConversion(longs(), long[].class);
+        // then primitive decimal types
+        verifyFloatArrayConversion(floats(), float[].class);
+        verifyDoubleArrayConversion(doubles(), float[].class);
+    }
+
+    public void testByteArrayFrom() throws Exception
+    {
+        /* Note: byte arrays are tricky, since they are considered
+         * binary data primarily, not as array of numbers. Hence
+         * output will be base64 encoded...
+         */
+        byte[] data = _convert("c3VyZS4=", byte[].class);
+        byte[] exp = "sure.".getBytes("Ascii");
+        verifyIntegralArrays(exp, data, exp.length);
+    }
+    
+    public void testShortArrayToX() throws Exception
+    {
+        short[] data = shorts();
+        verifyShortArrayConversion(data, byte[].class);
+        verifyShortArrayConversion(data, int[].class);
+        verifyShortArrayConversion(data, long[].class);
+    }
+
+    public void testIntArrayToX() throws Exception
+    {
+        int[] data = ints();
+        verifyIntArrayConversion(data, byte[].class);
+        verifyIntArrayConversion(data, short[].class);
+        verifyIntArrayConversion(data, long[].class);
+
+        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>>() {});
+        assertEquals(expNums, actNums);
+    }
+
+    public void testLongArrayToX() throws Exception
+    {
+        long[] data = longs();
+        verifyLongArrayConversion(data, byte[].class);
+        verifyLongArrayConversion(data, short[].class);
+        verifyLongArrayConversion(data, int[].class);
+ 
+        List<Number> expNums = _numberList(data, data.length);
+        List<Long> actNums = mapper.convertValue(data, new TypeReference<List<Long>>() {});
+        assertEquals(expNums, actNums);        
+    }
+
+    public void testOverflows()
+    {
+        // Byte overflow
+        try {
+            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);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, OVERFLOW_MSG);
+        }
+        // Int overflow
+        try {
+            mapper.convertValue(new long[] { Long.MAX_VALUE }, int[].class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, OVERFLOW_MSG);
+        }
+        // Longs need help of BigInteger...
+        BigInteger biggie = BigInteger.valueOf(Long.MAX_VALUE);
+        biggie.add(BigInteger.ONE);
+        List<BigInteger> l = new ArrayList<BigInteger>();
+        l.add(biggie);
+        try {
+            mapper.convertValue(l, int[].class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, OVERFLOW_MSG);
+        }
+        
+    }
+    
+    /*
+    /********************************************************
+    /* Helper methods
+    /********************************************************
+     */
+
+    // note: all value need to be within byte range
+    
+    private byte[] bytes() { return new byte[] { 1, -1, 0, 98, 127 }; }
+    private short[] shorts() { return new short[] { 1, -1, 0, 98, 127 }; }
+    private int[] ints() { return new int[] { 1, -1, 0, 98, 127 }; }
+    private long[] longs() { return new long[] { 1, -1, 0, 98, 127 }; }
+
+    // note: use values that are exact in binary
+
+    private double[] doubles() { return new double[] { 0.0, 0.25, -0.125, 10.5, 9875.0 }; }
+    private float[] floats() { return new float[] {
+            0.0f, 0.25f, -0.125f, 10.5f, 9875.0f };
+    }
+
+    private <T> void verifyByteArrayConversion(byte[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyIntegralArrays(data, result, data.length);
+    }
+    private <T> void verifyShortArrayConversion(short[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyIntegralArrays(data, result, data.length);
+    }
+    private <T> void verifyIntArrayConversion(int[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyIntegralArrays(data, result, data.length);
+    }
+    private <T> void verifyLongArrayConversion(long[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyIntegralArrays(data, result, data.length);
+    }
+    private <T> void verifyFloatArrayConversion(float[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyDoubleArrays(data, result, data.length);
+    }
+    private <T> void verifyDoubleArrayConversion(double[] data, Class<T> arrayType) {
+        T result = _convert(data, arrayType);
+        verifyDoubleArrays(data, result, data.length);
+    }
+    
+    private <T> T _convert(Object input, Class<T> outputType)
+    {
+        // 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);
+        // sanity check first:
+        assertNotNull(result);
+        assertEquals(outputType, result.getClass());
+        return result;
+    }
+
+    private List<Number> _numberList(Object numberArray, int size)
+    {
+        ArrayList<Number> result = new ArrayList<Number>(size);
+        for (int i = 0; i < size; ++i) {
+            result.add((Number) Array.get(numberArray, i));
+        }
+        return result;
+    }
+    
+    /**
+     * Helper method for checking that given collections contain integral Numbers
+     * that essentially contain same values in same order
+     */
+    private void verifyIntegralArrays(Object inputArray, Object outputArray, int size)
+    {
+        for (int i = 0; i < size; ++i) {
+            Number n1 = (Number) Array.get(inputArray, i);
+            Number n2 = (Number) Array.get(outputArray, i);
+            double value1 = ((Number) n1).longValue();
+            double value2 = ((Number) n2).longValue();
+            assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
+        }        
+    }
+
+    private void verifyDoubleArrays(Object inputArray, Object outputArray, int size)
+    {
+        for (int i = 0; i < size; ++i) {
+            Number n1 = (Number) Array.get(inputArray, i);
+            Number n2 = (Number) Array.get(outputArray, i);
+            double value1 = ((Number) n1).doubleValue();
+            double value2 = ((Number) n2).doubleValue();
+            assertEquals("Entry #"+i+"/"+size+" not equal", value1, value2);
+        }        
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
new file mode 100644
index 0000000..4722cff
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestBeanConversions.java
@@ -0,0 +1,220 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.TreeNode;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class TestBeanConversions
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final ObjectMapper MAPPER = new ObjectMapper();
+
+    static class Point {
+        public int x, y;
+
+        public int z = -13;
+
+        public Point() { }
+        public Point(int a, int b, int c)
+        {
+            x = a;
+            y = b;
+            z = c;
+        }
+    }
+
+    static class PointStrings {
+        public final String x, y;
+
+        public PointStrings(String x, String y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    public static class BooleanBean {
+        public boolean boolProp;
+    }
+
+    static class WrapperBean {
+        public BooleanBean x;
+    }
+
+    static class ObjectWrapper
+    {
+        private Object data;
+
+        public ObjectWrapper() { }
+        public ObjectWrapper(Object o) { data = o; }
+
+        public Object getData() { return data; }
+        public void setData(Object data) { this.data = data; }
+    }
+
+    static class Leaf {
+        public int value;
+
+        public Leaf() { }
+        public Leaf(int v) { value = v; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testBeanConvert()
+    {
+        // should have no problems convert between compatible beans...
+        PointStrings input = new PointStrings("37", "-9");
+        Point point = MAPPER.convertValue(input, Point.class);
+        assertEquals(37, point.x);
+        assertEquals(-9, point.y);
+        // z not included in input, will be whatever default constructor provides
+        assertEquals(-13, point.z);
+    }
+    
+    // For [JACKSON-371]; verify that we know property that caused issue...
+    // (note: not optimal place for test, but will have to do for now)
+    public void testErrorReporting() throws Exception
+    {
+        //String json = "{\"boolProp\":\"oops\"}";
+        // First: unknown property
+        try {
+            MAPPER.readValue("{\"unknownProp\":true}", BooleanBean.class);
+        } catch (JsonProcessingException e) {
+            verifyException(e, "unknownProp");
+        }
+
+        // then bad conversion
+        try {
+            MAPPER.readValue("{\"boolProp\":\"foobar\"}", BooleanBean.class);
+        } catch (JsonMappingException e) {
+            verifyException(e, "from String value 'foobar'");
+        }
+    }
+
+    public void testIssue458() throws Exception
+    {
+        ObjectWrapper a = new ObjectWrapper("foo");
+        ObjectWrapper b = new ObjectWrapper(a);
+        ObjectWrapper b2 = MAPPER.convertValue(b, ObjectWrapper.class);
+        ObjectWrapper a2 = MAPPER.convertValue(b2.getData(), ObjectWrapper.class);
+        assertEquals("foo", a2.getData());
+    }
+
+    // [JACKSON-710]: should work regardless of wrapping...
+    public void testWrapping() throws Exception
+    {
+        ObjectMapper wrappingMapper = new ObjectMapper();
+        wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
+        wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
+
+        // conversion is ok, even if it's bogus one
+        _convertAndVerifyPoint(wrappingMapper);
+
+        // also: ok to have mismatched settings, since as per [JACKSON-710], should
+        // not actually use wrapping internally in these cases
+        wrappingMapper = new ObjectMapper();
+        wrappingMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
+        wrappingMapper.disable(SerializationFeature.WRAP_ROOT_VALUE);
+        _convertAndVerifyPoint(wrappingMapper);
+
+        wrappingMapper = new ObjectMapper();
+        wrappingMapper.disable(DeserializationFeature.UNWRAP_ROOT_VALUE);
+        wrappingMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
+        _convertAndVerifyPoint(wrappingMapper);
+    }
+
+    // [Issue-11]: simple cast, for POJOs etc
+    public void testConvertUsingCast() throws Exception
+    {
+        String str = new String("foo");
+        CharSequence seq = str;
+        String result = MAPPER.convertValue(seq, String.class);
+        // should just cast...
+        assertSame(str, result);
+    }
+    
+    // [Issue-11]: simple cast, for Tree
+    public void testNodeConvert() throws Exception
+    {
+        ObjectNode src = (ObjectNode) MAPPER.readTree("{}");
+        TreeNode node = src;
+        ObjectNode result = MAPPER.treeToValue(node, ObjectNode.class);
+        // should just cast...
+        assertSame(src, result);
+    }
+    
+    private void _convertAndVerifyPoint(ObjectMapper m)
+    {
+        final Point input = new Point(1, 2, 3);
+        Point output = m.convertValue(input, Point.class);
+        assertEquals(1, output.x);
+        assertEquals(2, output.y);
+        assertEquals(3, output.z);
+    }
+
+    /**
+     * Need to test "shortcuts" introduced by [Issue-11]
+     */
+    public void testIssue11() throws Exception
+    {
+        // First the expected use case, Node specification
+        ObjectNode root = MAPPER.createObjectNode();
+        JsonNode n = root;
+        ObjectNode ob2 = MAPPER.convertValue(n, ObjectNode.class);
+        assertSame(root, ob2);
+
+        JsonNode n2 = MAPPER.convertValue(n, JsonNode.class);
+        assertSame(root, n2);
+        
+        // then some other no-op conversions
+        String STR = "test";
+        CharSequence seq = MAPPER.convertValue(STR, CharSequence.class);
+        assertSame(STR, seq);
+
+        // and then something that should NOT use short-cut
+        Leaf l = new Leaf(13);
+        Map<?,?> m = MAPPER.convertValue(l, Map.class);
+        assertNotNull(m);
+        assertEquals(1, m.size());
+        assertEquals(Integer.valueOf(13), m.get("value"));
+
+        // and reverse too
+        Leaf l2 = MAPPER.convertValue(m, Leaf.class);
+        assertEquals(13, l2.value);
+
+        // also; ok to use "untyped" (Object):
+        Object ob = MAPPER.convertValue(l, Object.class);
+        assertNotNull(ob);
+        assertEquals(LinkedHashMap.class, ob.getClass());
+
+        // And one more: this time with a minor twist
+        final Object plaino = new Object();
+        // first, a failed attempt:
+        try {
+            m = MAPPER.convertValue(plaino, Map.class);
+            fail("Conversion should have failed");
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "no properties discovered");
+        }
+        
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+        try {
+            assertEquals("{}", mapper.writeValueAsString(plaino));
+        } catch (Exception e) {
+            throw (Exception) e.getCause();
+        }
+        // should now work, via serialization/deserialization:
+        m = mapper.convertValue(plaino, Map.class);
+        assertNotNull(m);
+        assertEquals(0, m.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java
new file mode 100644
index 0000000..77df83f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingDeserializer.java
@@ -0,0 +1,183 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.util.StdConverter;
+
+public class TestConvertingDeserializer
+extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    @JsonDeserialize(converter=ConvertingBeanConverter.class)
+    static class ConvertingBean
+    {
+        protected int x, y;
+
+        protected ConvertingBean(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+    
+    static class Point
+    {
+        protected int x, y;
+    
+        public Point(int v1, int v2) {
+            x = v1;
+            y = v2;
+        }
+    }
+
+    static class ConvertingBeanContainer
+    {
+        public List<ConvertingBean> values;
+
+        public ConvertingBeanContainer() { }
+        public ConvertingBeanContainer(ConvertingBean... beans) {
+            values = Arrays.asList(beans);
+        }
+    }
+
+    static class ConvertingBeanConverter extends StdConverter<int[],ConvertingBean>
+    {
+        @Override
+        public ConvertingBean convert(int[] values) {
+            return new ConvertingBean(values[0], values[1]);
+        }
+    }
+    
+    static class PointConverter extends StdConverter<int[], Point>
+    {
+        @Override public Point convert(int[] value) {
+            return new Point(value[0], value[1]);
+        }
+    }
+
+    static class PointWrapper {
+        @JsonDeserialize(converter=PointConverter.class)
+        public Point value;
+
+        protected PointWrapper() { }
+        public PointWrapper(int x, int y) {
+            value = new Point(x, y);
+        }
+    }
+    
+    static class PointListWrapperArray {
+        @JsonDeserialize(contentConverter=PointConverter.class)
+        public Point[] values;
+    }
+
+    static class PointListWrapperList {
+        @JsonDeserialize(contentConverter=PointConverter.class)
+        public List<Point> values;
+    }
+
+    static class PointListWrapperMap {
+        @JsonDeserialize(contentConverter=PointConverter.class)
+        public Map<String,Point> values;
+    }
+
+    static class LowerCaser extends StdConverter<String, String>
+    {
+        @Override
+        public String convert(String value) {
+            return value.toLowerCase();
+        }
+        
+    }
+
+    static class LowerCaseText {
+        @JsonDeserialize(converter=LowerCaser.class)
+        public String text;
+    }
+
+    static class LowerCaseTextArray {
+        @JsonDeserialize(contentConverter=LowerCaser.class)
+        public String[] texts;
+    }
+     
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testClassAnnotationSimple() throws Exception
+    {
+        ConvertingBean bean = objectReader(ConvertingBean.class).readValue("[1,2]");
+        assertNotNull(bean);
+        assertEquals(1, bean.x);
+        assertEquals(2, bean.y);
+    }
+
+    public void testClassAnnotationForLists() throws Exception
+    {
+        ConvertingBeanContainer container = objectReader(ConvertingBeanContainer.class)
+                .readValue("{\"values\":[[1,2],[3,4]]}");
+        assertNotNull(container);
+        assertNotNull(container.values);
+        assertEquals(2, container.values.size());
+        assertEquals(4, container.values.get(1).y);
+    }
+
+    public void testPropertyAnnotationSimple() throws Exception
+    {
+        PointWrapper wrapper = objectReader(PointWrapper.class).readValue("{\"value\":[3,4]}");
+        assertNotNull(wrapper);
+        assertNotNull(wrapper.value);
+        assertEquals(3, wrapper.value.x);
+        assertEquals(4, wrapper.value.y);
+    }
+
+    public void testPropertyAnnotationLowerCasing() throws Exception
+    {
+        LowerCaseText text = objectReader(LowerCaseText.class).readValue("{\"text\":\"Yay!\"}");
+        assertNotNull(text);
+        assertNotNull(text.text);
+        assertEquals("yay!", text.text);
+    }
+
+    public void testPropertyAnnotationArrayLC() throws Exception
+    {
+        LowerCaseTextArray texts = objectReader(LowerCaseTextArray.class).readValue("{\"texts\":[\"ABC\"]}");
+        assertNotNull(texts);
+        assertNotNull(texts.texts);
+        assertEquals(1, texts.texts.length);
+        assertEquals("abc", texts.texts[0]);
+    }
+
+    public void testPropertyAnnotationForArrays() throws Exception
+    {
+        PointListWrapperArray array = objectReader(PointListWrapperArray.class)
+                .readValue("{\"values\":[[4,5],[5,4]]}");
+        assertNotNull(array);
+        assertNotNull(array.values);
+        assertEquals(2, array.values.length);
+        assertEquals(5, array.values[1].x);
+    }
+
+    public void testPropertyAnnotationForLists() throws Exception
+    {
+        PointListWrapperList array = objectReader(PointListWrapperList.class)
+                .readValue("{\"values\":[[7,8],[8,7]]}");
+        assertNotNull(array);
+        assertNotNull(array.values);
+        assertEquals(2, array.values.size());
+        assertEquals(7, array.values.get(0).x);
+    }
+
+    public void testPropertyAnnotationForMaps() throws Exception
+    {
+        PointListWrapperMap map = objectReader(PointListWrapperMap.class)
+                .readValue("{\"values\":{\"a\":[1,2]}}");
+        assertNotNull(map);
+        assertNotNull(map.values);
+        assertEquals(1, map.values.size());
+        Point p = map.values.get("a");
+        assertNotNull(p);
+        assertEquals(1, p.x);
+        assertEquals(2, p.y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java
new file mode 100644
index 0000000..4a9e7cb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestConvertingSerializer.java
@@ -0,0 +1,132 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.util.StdConverter;
+
+public class TestConvertingSerializer
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    @JsonSerialize(converter=ConvertingBeanConverter.class)
+    static class ConvertingBean
+    {
+        public int x, y;
+
+        public ConvertingBean(int v1, int v2) {
+            x = v1;
+            y = v2;
+        }
+    }
+
+    static class Point
+    {
+        public int x, y;
+
+        public Point(int v1, int v2) {
+            x = v1;
+            y = v2;
+        }
+    }
+    
+    static class ConvertingBeanContainer
+    {
+        public List<ConvertingBean> values;
+        
+        public ConvertingBeanContainer(ConvertingBean... beans) {
+            values = Arrays.asList(beans);
+        }
+    }
+
+    static class ConvertingBeanConverter extends StdConverter<ConvertingBean, int[]>
+    {
+        @Override
+        public int[] convert(ConvertingBean value) {
+            return new int[] { value.x, value.y };
+        }
+    }
+
+    static class PointConverter extends StdConverter<Point, int[]>
+    {
+        @Override public int[] convert(Point value) {
+            return new int[] { value.x, value.y };
+        }
+    }
+    
+    static class PointWrapper {
+        @JsonSerialize(converter=PointConverter.class)
+        public Point value;
+
+        public PointWrapper(int x, int y) {
+            value = new Point(x, y);
+        }
+    }
+
+    static class PointListWrapperArray {
+        @JsonSerialize(contentConverter=PointConverter.class)
+        public Point[] values;
+
+        public PointListWrapperArray(int x, int y) {
+            values = new Point[] { new Point(x, y), new Point(y, x) };
+        }
+    }
+
+    static class PointListWrapperList {
+        @JsonSerialize(contentConverter=PointConverter.class)
+        public List<Point> values;
+
+        public PointListWrapperList(int x, int y) {
+            values = Arrays.asList(new Point[] { new Point(x, y), new Point(y, x) });
+        }
+    }
+    
+    static class PointListWrapperMap {
+        @JsonSerialize(contentConverter=PointConverter.class)
+        public Map<String,Point> values;
+
+        public PointListWrapperMap(String key, int x, int y) {
+            values = new HashMap<String,Point>();
+            values.put(key, new Point(x, y));
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testClassAnnotationSimple() throws Exception
+    {
+        String json = objectWriter().writeValueAsString(new ConvertingBean(1, 2));
+        assertEquals("[1,2]", json);
+    }
+
+    public void testClassAnnotationForLists() throws Exception
+    {
+        String json = objectWriter().writeValueAsString(new ConvertingBeanContainer(
+                new ConvertingBean(1, 2), new ConvertingBean(3, 4)));
+        assertEquals("{\"values\":[[1,2],[3,4]]}", json);
+    }
+
+    public void testPropertyAnnotationSimple() throws Exception
+    {
+        String json = objectWriter().writeValueAsString(new PointWrapper(3, 4));
+        assertEquals("{\"value\":[3,4]}", json);
+    }
+
+    public void testPropertyAnnotationForArrays() throws Exception {
+        String json = objectWriter().writeValueAsString(new PointListWrapperArray(4, 5));
+        assertEquals("{\"values\":[[4,5],[5,4]]}", json);
+    }
+
+    public void testPropertyAnnotationForLists() throws Exception {
+        String json = objectWriter().writeValueAsString(new PointListWrapperList(7, 8));
+        assertEquals("{\"values\":[[7,8],[8,7]]}", json);
+    }
+
+    public void testPropertyAnnotationForMaps() throws Exception {
+        String json = objectWriter().writeValueAsString(new PointListWrapperMap("a", 1, 2));
+        assertEquals("{\"values\":{\"a\":[1,2]}}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestMapConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestMapConversions.java
new file mode 100644
index 0000000..ba5c094
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestMapConversions.java
@@ -0,0 +1,63 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+public class TestMapConversions
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final ObjectMapper mapper = new ObjectMapper();
+
+    enum AB { A, B; }
+
+    static class Bean {
+        public Integer A;
+        public String B;
+    }
+    
+    /**
+     * Test that verifies that we can go between couple of types of Maps...
+     */
+    public void testMapToMap()
+    {
+        Map<String,Integer> input = new LinkedHashMap<String,Integer>();
+        input.put("A", Integer.valueOf(3));
+        input.put("B", Integer.valueOf(-4));
+        Map<AB,String> output = mapper.convertValue(input,
+                new TypeReference<Map<AB,String>>() { });
+        assertEquals(2, output.size());
+        assertEquals("3", output.get(AB.A));
+        assertEquals("-4", output.get(AB.B));
+
+        // Let's try the other way too... and mix up types a bit
+        Map<String,Integer> roundtrip = mapper.convertValue(input,
+                new TypeReference<TreeMap<String,Integer>>() { });
+        assertEquals(2, roundtrip.size());
+        assertEquals(Integer.valueOf(3), roundtrip.get("A"));
+        assertEquals(Integer.valueOf(-4), roundtrip.get("B"));
+    }
+
+    public void testMapToBean()
+    {
+        EnumMap<AB,String> map = new EnumMap<AB,String>(AB.class);
+        map.put(AB.A, "   17");
+        map.put(AB.B, " -1");
+        Bean bean = mapper.convertValue(map, Bean.class);
+        assertEquals(Integer.valueOf(17), bean.A);
+        assertEquals(" -1", bean.B);
+    }
+
+    public void testBeanToMap()
+    {
+        Bean bean = new Bean();
+        bean.A = 129;
+        bean.B = "13";
+        EnumMap<AB,String> result = mapper.convertValue(bean,
+                new TypeReference<EnumMap<AB,String>>() { });
+        assertEquals("129", result.get(AB.A));
+        assertEquals("13", result.get(AB.B));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestPolymorphicUpdateValue.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestPolymorphicUpdateValue.java
new file mode 100644
index 0000000..cee3b8f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestPolymorphicUpdateValue.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.convert;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying handling of update value on polymorphic
+ * objects.
+ */
+public class TestPolymorphicUpdateValue extends BaseMapTest
+{
+    @JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_ARRAY //PROPERTY
+            ,use=JsonTypeInfo.Id.NAME, property="type")
+    @JsonSubTypes(value={ @JsonSubTypes.Type(value=Child.class)})
+    abstract static class Parent {
+        public int x;
+        public int y;
+    }
+
+    @JsonTypeName("child")
+    public static class Child extends Parent {
+        public int w;
+        public int h;
+    }    
+    
+    /*
+    /********************************************************
+    /* Unit tests
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testPolymorphicTest() throws Exception
+    {
+         Child c = new Child();
+         c.w = 10;
+         c.h = 11;
+         MAPPER.readerForUpdating(c).readValue("{\"x\":3,\"y\":4,\"w\":111}");
+         assertEquals(3, c.x);
+         assertEquals(4, c.y);
+         assertEquals(111, c.w);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java
new file mode 100644
index 0000000..c76e83c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestStringConversions.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestStringConversions
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimple()
+    {
+        assertEquals(Boolean.TRUE, MAPPER.convertValue("true", Boolean.class));
+        assertEquals(Integer.valueOf(-3), MAPPER.convertValue("  -3 ", Integer.class));
+        assertEquals(Long.valueOf(77), MAPPER.convertValue("77", Long.class));
+
+        int[] ints = { 1, 2, 3 };
+        List<Integer> Ints = new ArrayList<Integer>();
+        Ints.add(1);
+        Ints.add(2);
+        Ints.add(3);
+        
+        assertArrayEquals(ints, MAPPER.convertValue(Ints, int[].class));
+    }
+
+    public void testStringsToInts()
+    {
+        // let's verify our "neat trick" actually works...
+        assertArrayEquals(new int[] { 1, 2, 3, 4, -1, 0 },
+                          MAPPER.convertValue("1  2 3    4  -1 0".split("\\s+"), int[].class));
+    }
+
+    public void testBytesToBase64AndBack() throws Exception
+    {
+        byte[] input = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
+        String encoded = MAPPER.convertValue(input, String.class);
+        assertNotNull(encoded);
+        byte[] result = MAPPER.convertValue(encoded, byte[].class);
+        assertArrayEquals(input, result);
+    }
+    
+    public void testBytestoCharArray() throws Exception
+    {
+        byte[] input = new byte[] { 1, 2, 3, 4, 5, 6, 7 };
+        // first, do baseline encoding
+        char[] expEncoded = MAPPER.convertValue(input, String.class).toCharArray();
+        // then compare
+        char[] actEncoded = MAPPER.convertValue(input, char[].class);
+        assertArrayEquals(expEncoded, actEncoded);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
new file mode 100644
index 0000000..a51f769
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/convert/TestUpdateValue.java
@@ -0,0 +1,148 @@
+package com.fasterxml.jackson.databind.convert;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonView;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static org.junit.Assert.assertArrayEquals;
+
+/**
+ * Unit tests for verifying that "updating reader" works as
+ * expected.
+ */
+public class TestUpdateValue extends BaseMapTest
+{
+    /*
+    /********************************************************
+    /* Helper types
+    /********************************************************
+     */
+
+    static class Bean {
+        public String a = "a";
+        public String b = "b";
+
+        public int[] c = new int[] { 1, 2, 3 };
+
+        public Bean child = null;
+    }
+
+    static class XYBean {
+        public int x, y;
+    }
+
+    // [JACKSON-824]
+    public class TextView {}
+    public class NumView {}
+
+    public class Updateable {
+        @JsonView(NumView.class)
+        public int num;
+
+        @JsonView(TextView.class)
+        public String str;
+    }
+        
+    /*
+    /********************************************************
+    /* Unit tests
+    /********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testBeanUpdate() throws Exception
+    {
+        Bean bean = new Bean();
+        assertEquals("b", bean.b);
+        assertEquals(3, bean.c.length);
+        assertNull(bean.child);
+
+        Object ob = MAPPER.readerForUpdating(bean).readValue("{ \"b\":\"x\", \"c\":[4,5], \"child\":{ \"a\":\"y\"} }");
+        assertSame(ob, bean);
+
+        assertEquals("a", bean.a);
+        assertEquals("x", bean.b);
+        assertArrayEquals(new int[] { 4, 5 }, bean.c);
+
+        Bean child = bean.child;
+        assertNotNull(child);
+        assertEquals("y", child.a);
+        assertEquals("b", child.b);
+        assertArrayEquals(new int[] { 1, 2, 3 }, child.c);
+        assertNull(child.child);
+    }
+
+    public void testListUpdate() throws Exception
+    {
+        List<String> strs = new ArrayList<String>();
+        strs.add("a");
+        // for lists, we will be appending entries
+        Object ob = MAPPER.readerForUpdating(strs).readValue("[ \"b\", \"c\", \"d\" ]");
+        assertSame(strs, ob);
+        assertEquals(4, strs.size());
+        assertEquals("a", strs.get(0));
+        assertEquals("b", strs.get(1));
+        assertEquals("c", strs.get(2));
+        assertEquals("d", strs.get(3));
+    }
+
+    public void testMapUpdate() throws Exception
+    {
+        Map<String,String> strs = new HashMap<String,String>();
+        strs.put("a", "a");
+        strs.put("b", "b");
+        // for maps, we will be adding and/or overwriting entries
+        Object ob = MAPPER.readerForUpdating(strs).readValue("{ \"c\" : \"c\", \"a\" : \"z\" }");
+        assertSame(strs, ob);
+        assertEquals(3, strs.size());
+        assertEquals("z", strs.get("a"));
+        assertEquals("b", strs.get("b"));
+        assertEquals("c", strs.get("c"));
+    }
+
+    // Test for [JACKSON-717] -- ensure 'readValues' also does update
+    public void testUpdateSequence() throws Exception
+    {
+        XYBean toUpdate = new XYBean();
+        Iterator<XYBean> it = MAPPER.readerForUpdating(toUpdate).readValues(
+                "{\"x\":1,\"y\":2}\n{\"x\":16}{\"y\":37}");
+
+        assertTrue(it.hasNext());
+        XYBean value = it.next();
+        assertSame(toUpdate, value);
+        assertEquals(1, value.x);
+        assertEquals(2, value.y);
+
+        assertTrue(it.hasNext());
+        value = it.next();
+        assertSame(toUpdate, value);
+        assertEquals(16, value.x);
+        assertEquals(2, value.y); // unchanged
+
+        assertTrue(it.hasNext());
+        value = it.next();
+        assertSame(toUpdate, value);
+        assertEquals(16, value.x); // unchanged
+        assertEquals(37, value.y);
+        
+        assertFalse(it.hasNext());
+    }
+
+    // [JACKSON-824]
+    public void testUpdatingWithViews() throws Exception
+    {
+        Updateable bean = new Updateable();
+        bean.num = 100;
+        bean.str = "test";
+        Updateable result = MAPPER.readerForUpdating(bean)
+                .withView(TextView.class)
+                .readValue("{\"num\": 10, \"str\":\"foobar\"}");    
+        assertSame(bean, result);
+
+        assertEquals(100, bean.num);
+        assertEquals("foobar", bean.str);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java
new file mode 100644
index 0000000..9b5f1c6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestBuilderSimple.java
@@ -0,0 +1,217 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSetter;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+public class TestBuilderSimple extends BaseMapTest
+{
+    // // Simple 2-property value class, builder with standard naming
+	
+    @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 x) {
+    		this.x = x;
+    		return this;
+    	}
+
+    	public SimpleBuilderXY withY(int y) {
+    		this.y = y;
+    		return this;
+    	}
+
+    	public ValueClassXY build() {
+    		return new ValueClassXY(x, y);
+    	}
+    }
+
+    // // 3-property value, with more varied builder
+	
+    @JsonDeserialize(builder=BuildABC.class)
+    static class ValueClassABC
+    {
+        final int a, b, c;
+
+        protected ValueClassABC(int a, int b, int c) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+        }
+    }
+
+    static class BuildABC
+    {
+    	public int a; // to be used as is
+    	private int b, c;
+    	
+    	@JsonProperty("b")
+    	public BuildABC assignB(int b) {
+    		this.b = b;
+    		return this;
+    	}
+
+    	// Also ok NOT to return 'this'
+    	@JsonSetter("c")
+    	public void c(int c) {
+    		this.c = c;
+    	}
+
+    	public ValueClassABC build() {
+    		return new ValueClassABC(a, b, c);
+    	}
+    }
+
+    // // Then Builder that is itself immutable
+    
+    @JsonDeserialize(builder=BuildImmutable.class)
+    static class ValueImmutable
+    {
+        final int value;
+        protected ValueImmutable(int v) { value = v; }
+    }
+    
+    static class BuildImmutable {
+        private final int value;
+        
+        private BuildImmutable() { this(0); }
+        private BuildImmutable(int v) {
+            value = v;
+        }
+        public BuildImmutable withValue(int v) {
+            return new BuildImmutable(v);
+        }
+        public ValueImmutable build() {
+            return new ValueImmutable(value);
+        }
+    }
+    
+    // And then with custom naming:
+
+    @JsonDeserialize(builder=BuildFoo.class)
+    static class ValueFoo
+    {
+        final int value;
+        protected ValueFoo(int v) { value = v; }
+    }
+
+    @JsonPOJOBuilder(withPrefix="foo", buildMethodName="construct")
+    static class BuildFoo {
+        private int value;
+        
+        public BuildFoo fooValue(int v) {
+            value = v;
+            return this;
+        }
+        public ValueFoo construct() {
+            return new ValueFoo(value);
+        }
+    }
+
+    // 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);
+        }
+    }
+        
+	/*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimple() throws Exception
+    {
+    	String json = "{\"x\":1,\"y\":2}";
+    	Object o = mapper.readValue(json, ValueClassXY.class);
+    	assertNotNull(o);
+    	assertSame(ValueClassXY.class, o.getClass());
+    	ValueClassXY value = (ValueClassXY) o;
+    	// note: ctor adds one to both values
+    	assertEquals(value._x, 2);
+    	assertEquals(value._y, 3);
+    }
+
+    public void testMultiAccess() throws Exception
+    {
+    	String json = "{\"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);
+    }
+
+    // test for Immutable builder, to ensure return value is used
+    public void testImmutable() throws Exception
+    {
+        final String json = "{\"value\":13}";
+        ValueImmutable value = mapper.readValue(json, ValueImmutable.class);        
+        assertEquals(13, value.value);
+    }
+
+    // test with custom 'with-prefix'
+    public void testCustomWith() throws Exception
+    {
+        final String json = "{\"value\":1}";
+        ValueFoo value = mapper.readValue(json, ValueFoo.class);        
+        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);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
new file mode 100644
index 0000000..873a556
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestConstructFromMap.java
@@ -0,0 +1,96 @@
+package com.fasterxml.jackson.databind.creators;
+
+import java.awt.Point; // just for convenience
+import java.math.BigDecimal;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite that tests use of {@link JsonCreator}
+ * with "delegate" constructors and factory methods: ones that
+ * take a deserializable type that is bound from JSON content.
+ * This is usually done to get two-phase data binding, often using
+ * {@link java.util.Map} as the intermediate form.
+ */
+public class TestConstructFromMap
+    extends BaseMapTest
+{
+    static class ConstructorFromMap
+    {
+        int _x;
+        String _y;
+
+        @JsonCreator
+        ConstructorFromMap(Map<?,?> arg)
+        {
+            _x = ((Number) arg.get("x")).intValue();
+            _y = (String) arg.get("y");
+        }
+    }
+
+    static class FactoryFromPoint
+    {
+        int _x, _y;
+
+        private FactoryFromPoint(Point p) {
+            _x = p.x;
+            _y = p.y;
+        }
+
+        @JsonCreator
+        static FactoryFromPoint createIt(Point p)
+        {
+            return new FactoryFromPoint(p);
+        }
+    }
+
+    // Also: let's test BigDecimal-from-JSON-String factory
+    static class FactoryFromDecimalString
+    {
+	int _value;
+
+        private FactoryFromDecimalString(BigDecimal d) {
+	    _value = d.intValue();
+        }
+
+        @JsonCreator
+        static FactoryFromDecimalString whateverNameWontMatter(BigDecimal d)
+        {
+            return new FactoryFromDecimalString(d);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testViaConstructor() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ConstructorFromMap result = m.readValue
+            ("{ \"x\":1, \"y\" : \"abc\" }", ConstructorFromMap.class);
+        assertEquals(1, result._x);
+        assertEquals("abc", result._y);
+    }
+
+    public void testViaFactory() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        FactoryFromPoint result = m.readValue("{ \"x\" : 3, \"y\" : 4 }", FactoryFromPoint.class);
+        assertEquals(3, result._x);
+        assertEquals(4, result._y);
+    }
+
+    public void testViaFactoryUsingString() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        FactoryFromDecimalString result = m.readValue("\"12.57\"", FactoryFromDecimalString.class);
+        assertNotNull(result);
+        assertEquals(12, result._value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
new file mode 100644
index 0000000..34e084d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorNullValue.java
@@ -0,0 +1,84 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.*;
+
+// Mostly for [JACSON-774]
+public class TestCreatorNullValue extends BaseMapTest
+{
+    protected static class Container {
+        Contained<String> contained;
+
+        @JsonCreator
+        public Container(@JsonProperty("contained") Contained<String> contained) {
+            this.contained = contained;
+        }
+    }
+
+    private static interface Contained<T> {}
+
+    private static class NullContained implements Contained<Object> {}
+
+    private static final NullContained NULL_CONTAINED = new NullContained();
+
+    private static class ContainedDeserializer extends JsonDeserializer<Contained<?>> {
+        @Override
+        public Contained<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws JsonProcessingException {
+            return null;
+        }
+
+        @Override
+        public Contained<?> getNullValue() {
+            return NULL_CONTAINED;
+        }
+    }
+
+    private static class ContainerDeserializerResolver extends Deserializers.Base {
+        @Override
+        public JsonDeserializer<?> findBeanDeserializer(JavaType type,
+                DeserializationConfig config, BeanDescription beanDesc)
+            throws JsonMappingException
+        {
+            if (!Contained.class.isAssignableFrom(type.getRawClass())) {
+                return null;
+            } else {
+                return new ContainedDeserializer();
+            }
+        }
+    }
+
+    private static class TestModule extends Module
+    {
+        @Override
+        public String getModuleName() {
+            return "ContainedModule";
+        }
+
+        @Override
+        public Version version() {
+            return Version.unknownVersion();
+        }
+
+        @Override
+        public void setupModule(SetupContext setupContext) {
+            setupContext.addDeserializers(new ContainerDeserializerResolver());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testUsesDeserializersNullValue() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new TestModule());
+        Container container = mapper.readValue("{}", Container.class);
+        assertEquals(NULL_CONTAINED, container.contained);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
new file mode 100644
index 0000000..753f417
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators.java
@@ -0,0 +1,479 @@
+package com.fasterxml.jackson.databind.creators;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that it is possible to annotate
+ * various kinds of things with {@link JsonCreator} annotation.
+ */
+public class TestCreators
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes, simple
+    /**********************************************************
+     */
+
+    /**
+     * Simple(st) possible demonstration of using annotated
+     * constructors
+     */
+    static class ConstructorBean {
+        int x;
+
+        @JsonCreator protected ConstructorBean(@JsonProperty("x") int x) {
+            this.x = x;
+        }
+    }
+
+    /**
+     * Another simple constructor, but with bit more unusual argument
+     * type
+     */
+    static class BooleanConstructorBean {
+        Boolean b;
+        @JsonCreator protected BooleanConstructorBean(Boolean b) {
+            this.b = b;
+        }
+    }
+
+    static class BooleanConstructorBean2 {
+        boolean b;
+        @JsonCreator protected BooleanConstructorBean2(boolean b) {
+            this.b = b;
+        }
+    }
+    
+    static class DoubleConstructorBean {
+        Double d; // cup?
+        @JsonCreator protected DoubleConstructorBean(Double d) {
+            this.d = d;
+        }
+    }
+
+    static class FactoryBean {
+        double d; // teehee
+
+        private FactoryBean(double value, boolean dummy) { d = value; }
+
+        @JsonCreator protected static FactoryBean createIt(@JsonProperty("f") double value) {
+            return new FactoryBean(value, true);
+        }
+    }
+
+    static class LongFactoryBean {
+        long value;
+
+        private LongFactoryBean(long v) { value = v; }
+
+        @JsonCreator static protected LongFactoryBean valueOf(long v) {
+            return new LongFactoryBean(v);
+        }
+    }
+
+    static class StringFactoryBean {
+        String value;
+
+        private StringFactoryBean(String v, boolean dummy) { value = v; }
+
+        @JsonCreator static protected StringFactoryBean valueOf(String v) {
+            return new StringFactoryBean(v, true);
+        }
+    }
+
+    static class FactoryBeanMixIn { // static just to be able to use static methods
+        /**
+         * Note: signature (name and parameter types) must match; but
+         * only annotations will be used, not code or such. And use
+         * is by augmentation, so we only need to add things to add
+         * or override.
+         */
+        static FactoryBean createIt(@JsonProperty("mixed") double xyz) {
+            return null;
+        }
+    }
+
+    /**
+     * Simple demonstration of INVALID construtor annotation (only
+     * defining name for first arg)
+     */
+    static class BrokenBean {
+        @JsonCreator protected BrokenBean(@JsonProperty("a") int a,
+                                          int b) {
+        }
+    }
+
+    /**
+     * Bean that defines both creator and factory methor as
+     * creators. Constructors have priority; but it is possible
+     * to hide it using mix-in annotations.
+     */
+    static class CreatorBean
+    {
+        String a;
+        int x;
+
+        @JsonCreator
+        protected CreatorBean(@JsonProperty("a") String paramA,
+                              @JsonProperty("x") int paramX)
+        {
+            a = "ctor:"+paramA;
+            x = 1+paramX;
+        }
+
+        private CreatorBean(String a, int x, boolean dummy) {
+            this.a = a;
+            this.x = x;
+        }
+
+        @JsonCreator
+        public static CreatorBean buildMeUpButterCup(@JsonProperty("a") String paramA,
+                                                     @JsonProperty("x") int paramX)
+        {
+            return new CreatorBean("factory:"+paramA, paramX-1, false);
+        }
+    }
+
+    /**
+     * Class for sole purpose of hosting mix-in annotations.
+     * Couple of things to note: (a) MUST be static class (non-static
+     * get implicit pseudo-arg, 'this';
+     * (b) for factory methods, must have static to match (part of signature)
+     */
+    abstract static class MixIn {
+        @JsonIgnore private MixIn(String a, int x) { }
+    }
+
+    static class MultiBean {
+        Object value;
+
+        @JsonCreator public MultiBean(int v) { value = v; }
+        @JsonCreator public MultiBean(double v) { value = v; }
+        @JsonCreator public MultiBean(String v) { value = v; }
+        @JsonCreator public MultiBean(boolean v) { value = v; }
+    }
+
+    // for [JACKSON-850]
+    static class NoArgFactoryBean {
+        public int x;
+        public int y;
+        
+        public NoArgFactoryBean(int value) { x = value; }
+        
+        @JsonCreator
+        public static NoArgFactoryBean create() { return new NoArgFactoryBean(123); }
+    }
+    
+    /*
+    /**********************************************************
+    /* Annotated helper classes, mixed (creator and props)
+    /**********************************************************
+     */
+
+    /**
+     * Test bean for ensuring that constructors can be mixed with setters
+     */
+    static class ConstructorAndPropsBean
+    {
+        final int a, b;
+        boolean c;
+
+        @JsonCreator protected ConstructorAndPropsBean(@JsonProperty("a") int a,
+                                                       @JsonProperty("b") int b)
+        {
+            this.a = a;
+            this.b = b;
+        }
+
+        public void setC(boolean value) { c = value; }
+    }
+
+    /**
+     * Test bean for ensuring that factory methods can be mixed with setters
+     */
+    static class FactoryAndPropsBean
+    {
+        boolean[] arg1;
+        int arg2, arg3;
+
+        @JsonCreator protected FactoryAndPropsBean(@JsonProperty("a") boolean[] arg)
+        {
+            arg1 = arg;
+        }
+
+        public void setB(int value) { arg2 = value; }
+        public void setC(int value) { arg3 = value; }
+    }
+
+    static class DeferredConstructorAndPropsBean
+    {
+        final int[] createA;
+        String propA = "xyz";
+        String propB;
+
+        @JsonCreator
+        public DeferredConstructorAndPropsBean(@JsonProperty("createA") int[] a)
+        {
+            createA = a;
+        }
+        public void setPropA(String a) { propA = a; }
+        public void setPropB(String b) { propB = b; }
+    }
+
+    static class DeferredFactoryAndPropsBean
+    {
+        String prop, ctor;
+
+        @JsonCreator DeferredFactoryAndPropsBean(@JsonProperty("ctor") String str)
+        {
+            ctor = str;
+        }
+
+        public void setProp(String str) { prop = str; }
+    }
+
+    /*
+    /**********************************************************
+    /* Annotated helper classes for Maps
+    /**********************************************************
+     */
+
+    @SuppressWarnings("serial")
+    static class MapWithCtor extends HashMap<Object,Object>
+    {
+        final int _number;
+        String _text = "initial";
+
+        MapWithCtor() { this(-1, "default"); }
+
+        @JsonCreator
+            public MapWithCtor(@JsonProperty("number") int nr,
+                               @JsonProperty("text") String t)
+        {
+            _number = nr;
+            _text = t;
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class MapWithFactory extends TreeMap<Object,Object>
+    {
+        Boolean _b;
+
+        private MapWithFactory(Boolean b) {
+            _b = b;
+        }
+
+        @JsonCreator
+            static MapWithFactory createIt(@JsonProperty("b") Boolean b)
+        {
+            return new MapWithFactory(b);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, valid cases, non-deferred, no-mixins
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleConstructor() throws Exception
+    {
+        ConstructorBean bean = MAPPER.readValue("{ \"x\" : 42 }", ConstructorBean.class);
+        assertEquals(42, bean.x);
+    }
+
+    // [JACKSON-850]
+    public void testNoArgsFactory() throws Exception
+    {
+        NoArgFactoryBean value = MAPPER.readValue("{\"y\":13}", NoArgFactoryBean.class);
+        assertEquals(13, value.y);
+        assertEquals(123, value.x);
+    }
+    
+    public void testSimpleDoubleConstructor() throws Exception
+    {
+        Double exp = new Double("0.25");
+        DoubleConstructorBean bean = MAPPER.readValue(exp.toString(), DoubleConstructorBean.class);
+        assertEquals(exp, bean.d);
+    }
+
+    public void testSimpleBooleanConstructor() throws Exception
+    {
+        BooleanConstructorBean bean = MAPPER.readValue(" true ", BooleanConstructorBean.class);
+        assertEquals(Boolean.TRUE, bean.b);
+
+        BooleanConstructorBean2 bean2 = MAPPER.readValue(" true ", BooleanConstructorBean2.class);
+        assertTrue(bean2.b);
+    }
+
+    public void testSimpleFactory() throws Exception
+    {
+        FactoryBean bean = MAPPER.readValue("{ \"f\" : 0.25 }", FactoryBean.class);
+        assertEquals(0.25, bean.d);
+    }
+
+    public void testLongFactory() throws Exception
+    {
+        long VALUE = 123456789000L;
+        LongFactoryBean bean = MAPPER.readValue(String.valueOf(VALUE), LongFactoryBean.class);
+        assertEquals(VALUE, bean.value);
+    }
+
+    public void testStringFactory() throws Exception
+    {
+        String str = "abc";
+        StringFactoryBean bean = MAPPER.readValue(quote(str), StringFactoryBean.class);
+        assertEquals(str, bean.value);
+    }
+
+    public void testConstructorCreator() throws Exception
+    {
+        CreatorBean bean = MAPPER.readValue
+            ("{ \"a\" : \"xyz\", \"x\" : 12 }", CreatorBean.class);
+        assertEquals(13, bean.x);
+        assertEquals("ctor:xyz", bean.a);
+    }
+
+    public void testConstructorAndProps() throws Exception
+    {
+        ConstructorAndPropsBean bean = MAPPER.readValue
+            ("{ \"a\" : \"1\", \"b\": 2, \"c\" : true }", ConstructorAndPropsBean.class);
+        assertEquals(1, bean.a);
+        assertEquals(2, bean.b);
+        assertEquals(true, bean.c);
+    }
+
+    public void testFactoryAndProps() throws Exception
+    {
+        FactoryAndPropsBean bean = MAPPER.readValue
+            ("{ \"a\" : [ false, true, false ], \"b\": 2, \"c\" : -1 }", FactoryAndPropsBean.class);
+        assertEquals(2, bean.arg2);
+        assertEquals(-1, bean.arg3);
+        boolean[] arg1 = bean.arg1;
+        assertNotNull(arg1);
+        assertEquals(3, arg1.length);
+        assertFalse(arg1[0]);
+        assertTrue(arg1[1]);
+        assertFalse(arg1[2]);
+    }
+
+    /**
+     * Test to verify that multiple creators may co-exist, iff
+     * they use different JSON type as input
+     */
+    public void testMultipleCreators() throws Exception
+    {
+        MultiBean bean = MAPPER.readValue("123", MultiBean.class);
+        assertEquals(Integer.valueOf(123), bean.value);
+        bean = MAPPER.readValue(quote("abc"), MultiBean.class);
+        assertEquals("abc", bean.value);
+        bean = MAPPER.readValue("0.25", MultiBean.class);
+        assertEquals(Double.valueOf(0.25), bean.value);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, valid cases, deferred, no mixins
+    /**********************************************************
+     */
+
+    public void testDeferredConstructorAndProps() throws Exception
+    {
+        DeferredConstructorAndPropsBean bean = MAPPER.readValue
+            ("{ \"propB\" : \"...\", \"createA\" : [ 1 ], \"propA\" : null }",
+             DeferredConstructorAndPropsBean.class);
+
+        assertEquals("...", bean.propB);
+        assertNull(bean.propA);
+        assertNotNull(bean.createA);
+        assertEquals(1, bean.createA.length);
+        assertEquals(1, bean.createA[0]);
+    }
+
+    public void testDeferredFactoryAndProps() throws Exception
+    {
+        DeferredFactoryAndPropsBean bean = MAPPER.readValue
+            ("{ \"prop\" : \"1\", \"ctor\" : \"2\" }", DeferredFactoryAndPropsBean.class);
+        assertEquals("1", bean.prop);
+        assertEquals("2", bean.ctor);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, valid cases, mixins
+    /**********************************************************
+     */
+
+    public void testFactoryCreatorWithMixin() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(CreatorBean.class, MixIn.class);
+        CreatorBean bean = m.readValue
+            ("{ \"a\" : \"xyz\", \"x\" : 12 }", CreatorBean.class);
+        assertEquals(11, bean.x);
+        assertEquals("factory:xyz", bean.a);
+    }
+
+    public void testFactoryCreatorWithRenamingMixin() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(FactoryBean.class, FactoryBeanMixIn.class);
+        // override changes property name from "f" to "mixed"
+        FactoryBean bean = m.readValue("{ \"mixed\" :  20.5 }", FactoryBean.class);
+        assertEquals(20.5, bean.d);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, valid cases, Map with creator
+    /* (to test [JACKSON-153])
+    /**********************************************************
+     */
+
+    public void testMapWithConstructor() throws Exception
+    {
+        MapWithCtor result = MAPPER.readValue
+            ("{\"text\":\"abc\", \"entry\":true, \"number\":123, \"xy\":\"yx\"}",
+             MapWithCtor.class);
+        // regular Map entries:
+        assertEquals(Boolean.TRUE, result.get("entry"));
+        assertEquals("yx", result.get("xy"));
+        assertEquals(2, result.size());
+        // then ones passed via constructor
+        assertEquals("abc", result._text);
+        assertEquals(123, result._number);
+    }
+
+    public void testMapWithFactory() throws Exception
+    {
+        MapWithFactory result = MAPPER.readValue
+            ("{\"x\":\"...\",\"b\":true  }",
+             MapWithFactory.class);
+        assertEquals("...", result.get("x"));
+        assertEquals(1, result.size());
+        assertEquals(Boolean.TRUE, result._b);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, invalid/broken cases
+    /**********************************************************
+     */
+
+    public void testBrokenConstructor() throws Exception
+    {
+        try {
+            /*BrokenBean bean =*/ MAPPER.readValue("{ \"x\" : 42 }", BrokenBean.class);
+        } catch (JsonMappingException 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/creators/TestCreators2.java
new file mode 100644
index 0000000..7d3e0d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreators2.java
@@ -0,0 +1,308 @@
+
+package com.fasterxml.jackson.databind.creators;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class TestCreators2
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class HashTest
+    {
+        final byte[] bytes;
+        final String type;
+
+        @JsonCreator
+        public HashTest(@JsonProperty("bytes") @JsonDeserialize(using = BytesDeserializer.class) final byte[] bytes,
+                @JsonProperty("type") final String type)
+        {
+            this.bytes = bytes;
+            this.type = type;
+        }
+    }
+
+    static class BytesDeserializer extends JsonDeserializer<byte[]>
+    {
+        @Override
+        public byte[] deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+            String str = jp.getText();
+            return str.getBytes("UTF-8");
+        }
+    }
+
+    static class Primitives
+    {
+        protected int x = 3;
+        protected double d = -0.5;
+        protected boolean b = true;
+        
+        @JsonCreator
+        public Primitives(@JsonProperty("x") int x,
+                @JsonProperty("d") double d,
+                @JsonProperty("b") boolean b)
+        {
+            this.x = x;
+            this.d = d;
+            this.b = b;
+        }
+    }
+    
+    protected static class Test431Container {
+        protected final List<Item431> items;
+
+        @JsonCreator
+        public Test431Container(@JsonProperty("items") final List<Item431> i) {
+            items = i;
+        }
+}    
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    protected static class Item431 {
+        protected final String id;
+
+        @JsonCreator
+        public Item431(@JsonProperty("id") String id) {
+            this.id = id;
+        }
+    }
+
+    // Test class for verifying that creator-call failures are reported as checked exceptions
+    static class BeanFor438 {
+        @JsonCreator
+        public BeanFor438(@JsonProperty("name") String s) {
+            throw new IllegalArgumentException("I don't like that name!");
+        }
+    }
+
+    // For [JACKSON-465]
+    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
+    {
+        protected String bar;
+        
+        @JsonCreator
+        public BrokenCreatorBean(@JsonProperty("bar") String bar1, @JsonProperty("bar") String bar2) {
+            bar = ""+bar1+"/"+bar2;
+        }
+    }
+    
+    // For [JACKSON-541]: should not need @JsonCreator if SerializationFeature.AUTO_DETECT_CREATORS is on.
+    static class AutoDetectConstructorBean
+    {
+    	protected final String foo;
+    	protected final String bar;
+
+    	public AutoDetectConstructorBean(@JsonProperty("bar") String bar, @JsonProperty("foo") String foo){
+    	    this.bar = bar;
+    	    this.foo = foo;
+    	}
+    }
+
+    static class BustedCtor {
+        @JsonCreator
+        BustedCtor(@JsonProperty("a") String value) {
+            throw new IllegalArgumentException("foobar");
+        }
+    }
+
+    // As per [JACKSON-575]
+    static class IgnoredCtor
+    {
+        @JsonIgnore
+        public IgnoredCtor(String arg) {
+            throw new RuntimeException("Should never use this constructor");
+        }
+
+        public IgnoredCtor() { }
+    }
+
+    abstract static class AbstractBase {
+        @JsonCreator
+        public static AbstractBase create(Map<String,Object> props)
+        {
+            return new AbstractBaseImpl(props);
+        }
+    }
+
+    static class AbstractBaseImpl extends AbstractBase
+    {
+        protected Map<String,Object> props;
+        
+        public AbstractBaseImpl(Map<String,Object> props) {
+            this.props = props;
+        }
+    }
+    
+    static interface Issue700Set extends java.util.Set<Object> { }
+
+    static class Issue700Bean
+    {
+        protected Issue700Set item;
+
+        @JsonCreator
+        public Issue700Bean(@JsonProperty("item") String item) { }
+
+        public String getItem() { return null; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    // for [JACKSON-547]
+    public void testExceptionFromConstructor() throws Exception
+    {
+        try {
+            MAPPER.readValue("{}", BustedCtor.class);
+            fail("Expected exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, ": foobar");
+            // also: should have nested exception
+            Throwable t = e.getCause();
+            assertNotNull(t);
+            assertEquals(IllegalArgumentException.class, t.getClass());
+            assertEquals("foobar", t.getMessage());
+        }
+    }
+    
+    public void testSimpleConstructor() throws Exception
+    {
+        HashTest test = MAPPER.readValue("{\"type\":\"custom\",\"bytes\":\"abc\" }", HashTest.class);
+        assertEquals("custom", test.type);
+        assertEquals("abc", new String(test.bytes, "UTF-8"));
+    }    
+
+    // Test for [JACKSON-372]
+    public void testMissingPrimitives() throws Exception
+    {
+        Primitives p = MAPPER.readValue("{}", Primitives.class);
+        assertFalse(p.b);
+        assertEquals(0, p.x);
+        assertEquals(0.0, p.d);
+    }
+
+    public void testJackson431() throws Exception
+    {
+        final Test431Container foo = MAPPER.readValue(
+                "{\"items\":\n"
+                +"[{\"bar\": 0,\n"
+                +"\"id\": \"id123\",\n"
+                +"\"foo\": 1\n" 
+                +"}]}",
+                Test431Container.class);
+        assertNotNull(foo);
+    }
+
+    // [JACKSON-438]: Catch and rethrow exceptions that Creator methods throw
+    public void testJackson438() throws Exception
+    {
+        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");
+        }
+    }
+
+    @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());
+    }
+
+    public void testCreatorWithDupNames() throws Exception
+    {
+        try {
+            MAPPER.readValue("{\"bar\":\"x\"}", BrokenCreatorBean.class);
+            fail("Should have caught duplicate creator parameters");
+        } catch (JsonMappingException e) {
+            verifyException(e, "duplicate creator property \"bar\"");
+        }
+    }
+    
+    public void testCreatorMultipleArgumentWithoutAnnotation() throws Exception {
+        AutoDetectConstructorBean value = MAPPER.readValue("{\"bar\":\"bar\",\"foo\":\"foo\"}", AutoDetectConstructorBean.class);
+        assertEquals("bar", value.bar);
+        assertEquals("foo", value.foo);
+    }
+
+    // for [JACKSON-575]
+    public void testIgnoredSingleArgCtor() throws Exception
+    {
+        try {
+            MAPPER.readValue(quote("abc"), IgnoredCtor.class);
+            fail("Should have caught missing constructor problem");
+        } catch (JsonMappingException e) {
+            verifyException(e, "no single-String constructor/factory method");
+        }
+    }
+
+    public void testAbstractFactory() throws Exception
+    {
+        AbstractBase bean = MAPPER.readValue("{\"a\":3}", AbstractBase.class);
+        assertNotNull(bean);
+        AbstractBaseImpl impl = (AbstractBaseImpl) bean;
+        assertEquals(1, impl.props.size());
+        assertEquals(Integer.valueOf(3), impl.props.get("a"));
+    }
+
+    // [JACKSON-700]
+    public void testCreatorProperties() throws Exception
+    {
+        Issue700Bean value = MAPPER.readValue("{ \"item\" : \"foo\" }", Issue700Bean.class);
+        assertNotNull(value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
new file mode 100644
index 0000000..aa750c2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestCreatorsDelegating.java
@@ -0,0 +1,106 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JacksonInject;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestCreatorsDelegating extends BaseMapTest
+{
+    static class BooleanBean
+    {
+        protected Boolean value;
+
+        public BooleanBean(Boolean v) { value = v; }
+        
+        @JsonCreator
+        protected static BooleanBean create(Boolean value) {
+            return new BooleanBean(value);
+        }
+    }
+
+    // for [JACKSON-711]; should allow delegate-based one(s) too
+    static class CtorBean711
+    {
+        protected String name;
+        protected int age;
+        
+        @JsonCreator
+        public CtorBean711(@JacksonInject String n, int a)
+        {
+            name = n;
+            age = a;
+        }
+    }
+
+    // for [JACKSON-711]; should allow delegate-based one(s) too
+    static class FactoryBean711
+    {
+        protected String name1;
+        protected String name2;
+        protected int age;
+        
+        private FactoryBean711(int a, String n1, String n2) {
+            age = a;
+            name1 = n1;
+            name2 = n2;
+        }
+        
+        @JsonCreator
+        public static FactoryBean711 create(@JacksonInject String n1, int a, @JacksonInject String n2) {
+            return new FactoryBean711(a, n1, n2);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testBooleanDelegate() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // should obviously work with booleans...
+        BooleanBean bb = m.readValue("true", BooleanBean.class);
+        assertEquals(Boolean.TRUE, bb.value);
+
+        // but also with value conversion from String
+        bb = m.readValue(quote("true"), BooleanBean.class);
+        assertEquals(Boolean.TRUE, bb.value);
+    }
+    
+    // As per [JACKSON-711]: should also work with delegate model (single non-annotated arg)
+    public void testWithCtorAndDelegate() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "Pooka")
+            );
+        CtorBean711 bean = null;
+        try {
+            bean = mapper.readValue("38", CtorBean711.class);
+        } catch (JsonMappingException e) {
+            fail("Did not expect problems, got: "+e.getMessage());
+        }
+        assertEquals(38, bean.age);
+        assertEquals("Pooka", bean.name);
+    }
+
+    public void testWithFactoryAndDelegate() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "Fygar")
+            );
+        FactoryBean711 bean = null;
+        try {
+            bean = mapper.readValue("38", FactoryBean711.class);
+        } catch (JsonMappingException e) {
+            fail("Did not expect problems, got: "+e.getMessage());
+        }
+        assertEquals(38, bean.age);
+        assertEquals("Fygar", bean.name1);
+        assertEquals("Fygar", bean.name2);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
new file mode 100644
index 0000000..59818d3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestPolymorphicCreators.java
@@ -0,0 +1,147 @@
+package com.fasterxml.jackson.databind.creators;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that it is possible to annotate
+ * various kinds of things with {@link JsonCreator} annotation.
+ */
+public class TestPolymorphicCreators
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+
+    static class Animal
+    {
+        // All animals have names, for our demo purposes...
+        public String name;
+
+        protected Animal() { }
+
+        /**
+         * Creator method that can instantiate instances of
+         * appropriate polymoprphic type
+         */
+        @JsonCreator
+        public static Animal create(@JsonProperty("type") String type)
+        {
+	    if ("dog".equals(type)) {
+		return new Dog();
+	    }
+	    if ("cat".equals(type)) {
+		return new Cat();
+	    }
+	    throw new IllegalArgumentException("No such animal type ('"+type+"')");
+        }
+    }
+
+    static class Dog extends Animal
+    {
+        double barkVolume; // in decibels
+        public Dog() { }
+        public void setBarkVolume(double v) { barkVolume = v; }
+    }
+
+    static class Cat extends Animal
+    {
+        boolean likesCream;
+        public int lives;
+        public Cat() { }
+        public void setLikesCream(boolean likesCreamSurely) { likesCream = likesCreamSurely; }
+    }
+
+    abstract static class AbstractRoot
+    {
+        private final String opt;
+
+        private AbstractRoot(String opt) {
+            this.opt = opt;
+        }
+
+        @JsonCreator
+        public static final AbstractRoot make(@JsonProperty("which") int which,
+            @JsonProperty("opt") String opt) {
+            if(1 == which) {
+                return new One(opt);
+            }
+            throw new RuntimeException("cannot instantiate " + which);
+        }
+
+        abstract public int getWhich();
+
+        public final String getOpt() {
+                return opt;
+        }
+    }
+
+    static final class One extends AbstractRoot {
+        private One(String opt) {
+            super(opt);
+        }
+
+        @Override public int getWhich() {
+            return 1;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Actual tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * Simple test to verify that it is possible to implement polymorphic
+     * deserialization manually.
+     */
+    public void testManualPolymorphicDog() throws Exception
+    {
+        // first, a dog, start with type
+        Animal animal = MAPPER.readValue("{ \"type\":\"dog\", \"name\":\"Fido\", \"barkVolume\" : 95.0 }", Animal.class);
+        assertEquals(Dog.class, animal.getClass());
+        assertEquals("Fido", animal.name);
+        assertEquals(95.0, ((Dog) animal).barkVolume);
+    }
+
+    public void testManualPolymorphicCatBasic() throws Exception
+    {
+        // and finally, lactose-intolerant, but otherwise robust super-cat:
+        Animal animal = MAPPER.readValue("{ \"name\" : \"Macavity\", \"type\":\"cat\", \"lives\":18, \"likesCream\":false }", Animal.class);
+        assertEquals(Cat.class, animal.getClass());
+        assertEquals("Macavity", animal.name); // ... there's no one like Macavity!
+        Cat cat = (Cat) animal;
+        assertEquals(18, cat.lives);
+        // ok, he can't drink dairy products. Let's verify:
+        assertEquals(false, cat.likesCream);
+    }
+
+    public void testManualPolymorphicCatWithReorder() throws Exception
+    {
+        // Then cat; shuffle order to mandate buffering
+        Animal animal = MAPPER.readValue("{ \"likesCream\":true, \"name\" : \"Venla\", \"type\":\"cat\" }", Animal.class);
+        assertEquals(Cat.class, animal.getClass());
+        assertEquals("Venla", animal.name);
+        // bah, of course cats like cream. But let's ensure Jackson won't mess with laws of nature!
+        assertTrue(((Cat) animal).likesCream);
+    }
+
+    public void testManualPolymorphicWithNumbered() throws Exception
+    {
+         final ObjectWriter w = MAPPER.writerWithType(AbstractRoot.class);
+         final ObjectReader r = MAPPER.reader(AbstractRoot.class);
+
+         AbstractRoot input = AbstractRoot.make(1, "oh hai!");
+         String json = w.writeValueAsString(input);
+         AbstractRoot result = r.readValue(json);
+         assertNotNull(result);
+         assertEquals("oh hai!", result.getOpt());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
new file mode 100644
index 0000000..bcf6770
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/creators/TestValueInstantiator.java
@@ -0,0 +1,521 @@
+package com.fasterxml.jackson.databind.creators;
+
+import java.util.*;
+
+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.module.SimpleModule;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Test related to [JACKSON-580] (allow specifying custom instantiators)
+ */
+public class TestValueInstantiator extends BaseMapTest
+{
+    static class MyBean
+    {
+        String _secret;
+        
+        public MyBean(String s, boolean bogus) {
+            _secret = s;
+        }
+    }
+
+    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;
+        }
+    }
+
+    static abstract class InstantiatorBase extends ValueInstantiator
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return "UNKNOWN";
+        }
+
+        @Override
+        public boolean canCreateUsingDelegate() { return false; }
+    }
+    
+    static abstract class PolymorphicBeanBase { }
+    
+    static class PolymorphicBean extends PolymorphicBeanBase
+    {
+        public String name;
+    }
+    
+    @SuppressWarnings("serial")
+    static class MyList extends ArrayList<Object>
+    {
+        public MyList(boolean b) { super(); }
+    }
+
+    @SuppressWarnings("serial")
+    static class MyMap extends HashMap<String,Object>
+    {
+        public MyMap(boolean b) { super(); }
+        public MyMap(String name) {
+            super();
+            put(name, name);
+        }
+    }
+    
+    static class MyBeanInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return MyBean.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateUsingDefault() { return true; }
+
+        @Override
+        public MyBean createUsingDefault(DeserializationContext ctxt) {
+            return new MyBean("secret!", true);
+        }
+    }
+
+    /**
+     * Something more ambitious: semi-automated approach to polymorphic
+     * deserialization, using ValueInstantiator; from Object to any
+     * type...
+     */
+    static class PolymorphicBeanInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return Object.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateFromObjectWith() { return true; }
+        
+        @Override
+        public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) {
+            return  new CreatorProperty[] {
+                    new CreatorProperty("type", config.constructType(Class.class), null,
+                            null, null, null, 0, null, true)
+            };
+        }
+
+        @Override
+        public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) {
+            try {
+                Class<?> cls = (Class<?>) args[0];
+                return cls.newInstance();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+    
+    static class CreatorMapInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return MyMap.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateFromObjectWith() { return true; }
+
+        @Override
+        public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) {
+            return  new CreatorProperty[] {
+                    new CreatorProperty("name", config.constructType(String.class), null,
+                            null, null, null, 0, null, true)
+            };
+        }
+
+        @Override
+        public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) {
+            return new MyMap((String) args[0]);
+        }
+    }
+    
+    static class MyDelegateBeanInstantiator extends ValueInstantiator
+    {
+        @Override
+        public String getValueTypeDesc() { return "xxx"; }
+        
+        @Override
+        public boolean canCreateUsingDelegate() { return true; }
+
+        @Override
+        public JavaType getDelegateType(DeserializationConfig config) {
+            return config.constructType(Object.class);
+        }
+        
+        @Override
+        public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) {
+            return new MyBean(""+delegate, true);
+        }
+    }
+    
+    static class MyListInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return MyList.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateUsingDefault() { return true; }
+
+        @Override
+        public MyList createUsingDefault(DeserializationContext ctxt) {
+            return new MyList(true);
+        }
+    }
+
+    static class MyDelegateListInstantiator extends ValueInstantiator
+    {
+        @Override
+        public String getValueTypeDesc() { return "xxx"; }
+        
+        @Override
+        public boolean canCreateUsingDelegate() { return true; }
+
+        @Override
+        public JavaType getDelegateType(DeserializationConfig config) {
+            return config.constructType(Object.class);
+        }
+        
+        @Override
+        public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) {
+            MyList list = new MyList(true);
+            list.add(delegate);
+            return list;
+        }
+    }
+    
+    static class MyMapInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return MyMap.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateUsingDefault() { return true; }
+
+        @Override
+        public MyMap createUsingDefault(DeserializationContext ctxt) {
+            return new MyMap(true);
+        }
+    }
+
+    static class MyDelegateMapInstantiator extends ValueInstantiator
+    {
+        @Override
+        public String getValueTypeDesc() { return "xxx"; }
+        
+        @Override
+        public boolean canCreateUsingDelegate() { return true; }
+
+        @Override
+        public JavaType getDelegateType(DeserializationConfig config) {
+            return TypeFactory.defaultInstance().constructType(Object.class);
+        }
+        
+        @Override
+        public Object createUsingDelegate(DeserializationContext ctxt, Object delegate) {
+            MyMap map = new MyMap(true);
+            map.put("value", delegate);
+            return map;
+        }
+    }
+
+    @JsonValueInstantiator(AnnotatedBeanInstantiator.class)
+    static class AnnotatedBean {
+        protected final String a;
+        protected final int b;
+        
+        public AnnotatedBean(String a, int b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
+    static class AnnotatedBeanInstantiator extends InstantiatorBase
+    {
+        @Override
+        public String getValueTypeDesc() {
+            return MyMap.class.getName();
+        }
+        
+        @Override
+        public boolean canCreateUsingDefault() { return true; }
+
+        @Override
+        public AnnotatedBean createUsingDefault(DeserializationContext ctxt) {
+            return new AnnotatedBean("foo", 3);
+        }
+    }
+    
+    @SuppressWarnings("serial")
+    static class MyModule extends SimpleModule
+    {
+        public MyModule(Class<?> cls, ValueInstantiator inst)
+        {
+            super("Test", Version.unknownVersion());
+            this.addValueInstantiator(cls, inst);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests for default creators
+    /**********************************************************
+     */
+
+    public void testCustomBeanInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyBean.class, new MyBeanInstantiator()));
+        MyBean bean = mapper.readValue("{}", MyBean.class);
+        assertNotNull(bean);
+        assertEquals("secret!", bean._secret);
+    }
+
+    public void testCustomListInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyList.class, new MyListInstantiator()));
+        MyList result = mapper.readValue("[]", MyList.class);
+        assertNotNull(result);
+        assertEquals(MyList.class, result.getClass());
+        assertEquals(0, result.size());
+    }
+
+    public void testCustomMapInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyMap.class, new MyMapInstantiator()));
+        MyMap result = mapper.readValue("{ \"a\":\"b\" }", MyMap.class);
+        assertNotNull(result);
+        assertEquals(MyMap.class, result.getClass());
+        assertEquals(1, result.size());
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests for delegate creators
+    /**********************************************************
+     */
+
+    public void testDelegateBeanInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyBean.class, new MyDelegateBeanInstantiator()));
+        MyBean bean = mapper.readValue("123", MyBean.class);
+        assertNotNull(bean);
+        assertEquals("123", bean._secret);
+    }
+
+    public void testDelegateListInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyList.class, new MyDelegateListInstantiator()));
+        MyList result = mapper.readValue("123", MyList.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(123), result.get(0));
+    }
+    
+    public void testDelegateMapInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyMap.class, new MyDelegateMapInstantiator()));
+        MyMap result = mapper.readValue("123", MyMap.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(123), result.values().iterator().next());
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests for property-based creators
+    /**********************************************************
+     */
+
+    public void testPropertyBasedBeanInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(CreatorBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromObjectWith() { return true; }
+        
+                    @Override
+                    public CreatorProperty[] getFromObjectArguments(DeserializationConfig config) {
+                        return  new CreatorProperty[] {
+                                new CreatorProperty("secret", config.constructType(String.class), null,
+                                        null, null, null, 0, null, true)
+                        };
+                    }
+        
+                    @Override
+                    public Object createFromObjectWith(DeserializationContext ctxt, Object[] args) {
+                        return new CreatorBean((String) args[0]);
+                    }
+        }));
+        CreatorBean bean = mapper.readValue("{\"secret\":123,\"value\":37}", CreatorBean.class);
+        assertNotNull(bean);
+        assertEquals("123", bean._secret);
+    }
+
+    public void testPropertyBasedMapInstantiator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MyMap.class, new CreatorMapInstantiator()));
+        MyMap result = mapper.readValue("{\"name\":\"bob\", \"x\":\"y\"}", MyMap.class);
+        assertNotNull(result);
+        assertEquals(2, result.size());
+        assertEquals("bob", result.get("bob"));
+        assertEquals("y", result.get("x"));
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests for scalar-delegates
+    /**********************************************************
+     */
+
+    public void testBeanFromString() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MysteryBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromString() { return true; }
+                    
+                    @Override
+                    public Object createFromString(DeserializationContext ctxt, String value) {
+                        return new MysteryBean(value);
+                    }
+        }));
+        MysteryBean result = mapper.readValue(quote("abc"), MysteryBean.class);
+        assertNotNull(result);
+        assertEquals("abc", result.value);
+    }
+
+    public void testBeanFromInt() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MysteryBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromInt() { return true; }
+                    
+                    @Override
+                    public Object createFromInt(DeserializationContext ctxt, int value) {
+                        return new MysteryBean(value+1);
+                    }
+        }));
+        MysteryBean result = mapper.readValue("37", MysteryBean.class);
+        assertNotNull(result);
+        assertEquals(Integer.valueOf(38), result.value);
+    }
+
+    public void testBeanFromLong() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MysteryBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromLong() { return true; }
+                    
+                    @Override
+                    public Object createFromLong(DeserializationContext ctxt, long value) {
+                        return new MysteryBean(value+1L);
+                    }
+        }));
+        MysteryBean result = mapper.readValue("9876543210", MysteryBean.class);
+        assertNotNull(result);
+        assertEquals(Long.valueOf(9876543211L), result.value);
+    }
+
+    public void testBeanFromDouble() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MysteryBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromDouble() { return true; }
+
+                    @Override
+                    public Object createFromDouble(DeserializationContext ctxt, double value) {
+                        return new MysteryBean(2.0 * value);
+                    }
+        }));
+        MysteryBean result = mapper.readValue("0.25", MysteryBean.class);
+        assertNotNull(result);
+        assertEquals(Double.valueOf(0.5), result.value);
+    }
+
+    public void testBeanFromBoolean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(MysteryBean.class,
+                new InstantiatorBase() {
+                    @Override
+                    public boolean canCreateFromBoolean() { return true; }
+                    
+                    @Override
+                    public Object createFromBoolean(DeserializationContext ctxt, boolean value) {
+                        return new MysteryBean(Boolean.valueOf(value));
+                    }
+        }));
+        MysteryBean result = mapper.readValue("true", MysteryBean.class);
+        assertNotNull(result);
+        assertEquals(Boolean.TRUE, result.value);
+    }
+    
+    /*
+    /**********************************************************
+    /* Other tests
+    /**********************************************************
+     */
+
+    
+    /**
+     * Beyond basic features, it should be possible to even implement
+     * polymorphic handling...
+     */
+    public void testPolymorphicCreatorBean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new MyModule(PolymorphicBeanBase.class, new PolymorphicBeanInstantiator()));
+        String JSON = "{\"type\":"+quote(PolymorphicBean.class.getName())+",\"name\":\"Axel\"}";
+        PolymorphicBeanBase result = mapper.readValue(JSON, PolymorphicBeanBase.class);
+        assertNotNull(result);
+        assertSame(PolymorphicBean.class, result.getClass());
+        assertEquals("Axel", ((PolymorphicBean) result).name);
+    }
+
+    public void testJackson633() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotatedBean bean = mapper.readValue("{}", AnnotatedBean.class);
+        assertNotNull(bean);
+        assertEquals("foo", bean.a);
+        assertEquals(3, bean.b);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java
new file mode 100644
index 0000000..28830fe
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAbstract.java
@@ -0,0 +1,38 @@
+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/TestAnnotationIgnore.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
new file mode 100644
index 0000000..d12e949
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationIgnore.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite that tests use of {@link JsonIgnore}
+ * annotation with deserialization.
+ */
+public class TestAnnotationIgnore
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /// Class for testing {@link JsonIgnore} annotations with setters
+    final static class SizeClassIgnore
+    {
+        int _x = 0;
+        int _y = 0;
+
+        public void setX(int value) { _x = value; }
+        @JsonIgnore public void setY(int value) { _y = value; }
+
+        /* Just igoring won't help a lot here; let's define a replacement
+         * so that we won't get an exception for "unknown field"
+         */
+        @JsonProperty("y") void foobar(int value) {
+            ; // nop
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimpleIgnore() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        SizeClassIgnore result = m.readValue
+            ("{ \"x\":1, \"y\" : 2 }",
+             SizeClassIgnore.class);
+        // x should be set, y not
+        assertEquals(1, result._x);
+        assertEquals(0, result._y);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationUsing.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationUsing.java
new file mode 100644
index 0000000..c0390c8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnnotationUsing.java
@@ -0,0 +1,230 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.util.*;
+
+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;
+
+/**
+ * Unit test suite that tests "usingXxx" properties of
+ * {@link JsonDeserialize} annotation.
+ */
+ at SuppressWarnings("serial")
+public class TestAnnotationUsing
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************************
+    /* Annotated Bean classes
+    /**********************************************************************
+     */
+
+    /**
+     * Class for testing {@link JsonDeserializer} annotation
+     * for class itself.
+     */
+    @JsonDeserialize(using=ValueDeserializer.class)
+    final static class ValueClass {
+        int _a;
+        
+        /* we'll test it by not having default no-arg ctor, and leaving
+         * out single-int-arg ctor (because deserializer would use that too)
+         */
+        public ValueClass(int a, int b) {
+            _a = a;
+        }
+    }
+
+    /**
+     * Class for testing {@link JsonDeserializer} annotation
+     * for a method
+     */
+    final static class MethodBean {
+        int[] _ints;
+
+        /* Note: could be made to work otherwise, except that
+         * to trigger failure (in absence of annotation) Json
+         * is of type VALUE_NUMBER_INT, not an Array: array would
+         * work by default, but scalar not
+         */
+        @JsonDeserialize(using=IntsDeserializer.class)
+        public void setInts(int[] i) {
+            _ints = i;
+        }
+    }
+
+    static class ArrayBean {
+        @JsonDeserialize(contentUsing=ValueDeserializer.class)
+        public Object[] values;
+    }
+
+
+    static class ListBean {
+        @JsonDeserialize(contentUsing=ValueDeserializer.class)
+        public List<Object> values;
+    }
+
+    static class MapBean {
+        @JsonDeserialize(contentUsing=ValueDeserializer.class)
+        public Map<String,Object> values;
+    }
+
+    static class MapKeyBean {
+        @JsonDeserialize(keyUsing=MapKeyDeserializer.class)
+        public Map<Object,Object> values;
+    }
+
+    @JsonDeserialize(keyUsing=MapKeyDeserializer.class, contentUsing=ValueDeserializer.class)
+    static class MapKeyMap extends HashMap<Object,Object> { }
+    
+    /*
+    /**********************************************************************
+    /* Deserializers
+    /**********************************************************************
+     */
+
+    static class ValueDeserializer extends StdDeserializer<ValueClass>
+    {
+        public ValueDeserializer() { super(ValueClass.class); }
+        @Override
+        public ValueClass deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            int i = jp.getIntValue();
+            return new ValueClass(i, i);
+        }
+    }
+
+    private final static class IntsDeserializer extends StdDeserializer<int[]>
+    {
+        public IntsDeserializer() { super(int[].class); }
+        @Override
+        public int[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return new int[] { jp.getIntValue() };
+        }
+    }
+
+    private final static class MapKeyDeserializer extends KeyDeserializer
+    {
+        @Override
+        public Object deserializeKey(String key, DeserializationContext ctxt)
+        {
+            return new String[] { key };
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Tests: specifying deserializer of value itself
+    /**********************************************************************
+     */
+
+    // Unit test to verify that {@link JsonDeserialize#using} annotation works
+    // when applied to a class
+    public void testClassDeserializer() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ValueClass result = m.readValue("  123  ", ValueClass.class);
+        assertEquals(123, result._a);
+    }
+
+    // Unit test to verify that {@link JsonDeserialize#using} annotation works
+    // when applied to a Method
+    public void testMethodDeserializer() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // note: since it's part of method, must parse from Object struct
+        MethodBean result = m.readValue(" { \"ints\" : 3 } ", MethodBean.class);
+        assertNotNull(result);
+        int[] ints = result._ints;
+        assertNotNull(ints);
+        assertEquals(1, ints.length);
+        assertEquals(3, ints[0]);
+    }
+
+    /*
+    /**********************************************************************
+    /* Tests: specifying deserializer for keys and/or contents
+    /**********************************************************************
+     */
+
+    public void testArrayContentUsing() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ArrayBean result = m.readValue(" { \"values\" : [ 1, 2, 3 ] } ", ArrayBean.class);
+        assertNotNull(result);
+        Object[] obs = result.values;
+        assertNotNull(obs);
+        assertEquals(3, obs.length);
+        assertEquals(ValueClass.class, obs[0].getClass());
+        assertEquals(1, ((ValueClass) obs[0])._a);
+        assertEquals(ValueClass.class, obs[1].getClass());
+        assertEquals(2, ((ValueClass) obs[1])._a);
+        assertEquals(ValueClass.class, obs[2].getClass());
+        assertEquals(3, ((ValueClass) obs[2])._a);
+    }
+
+    public void testListContentUsing() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ListBean result = m.readValue(" { \"values\" : [ 1, 2, 3 ] } ", ListBean.class);
+        assertNotNull(result);
+        List<Object> obs = result.values;
+        assertNotNull(obs);
+        assertEquals(3, obs.size());
+        assertEquals(ValueClass.class, obs.get(0).getClass());
+        assertEquals(1, ((ValueClass) obs.get(0))._a);
+        assertEquals(ValueClass.class, obs.get(1).getClass());
+        assertEquals(2, ((ValueClass) obs.get(1))._a);
+        assertEquals(ValueClass.class, obs.get(2).getClass());
+        assertEquals(3, ((ValueClass) obs.get(2))._a);
+    }
+
+    public void testMapContentUsing() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MapBean result = m.readValue(" { \"values\" : { \"a\": 1, \"b\":2 } } ", MapBean.class);
+        assertNotNull(result);
+        Map<String,Object> map = result.values;
+        assertNotNull(map);
+        assertEquals(2, map.size());
+        assertEquals(ValueClass.class, map.get("a").getClass());
+        assertEquals(1, ((ValueClass) map.get("a"))._a);
+        assertEquals(ValueClass.class, map.get("b").getClass());
+        assertEquals(2, ((ValueClass) map.get("b"))._a);
+    }
+
+    public void testMapKeyUsing() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MapKeyBean result = m.readValue(" { \"values\" : { \"a\": true } } ", MapKeyBean.class);
+        assertNotNull(result);
+        Map<Object,Object> map = result.values;
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        Map.Entry<Object,Object> en = map.entrySet().iterator().next();
+        assertEquals(String[].class, en.getKey().getClass());
+        assertEquals(Boolean.TRUE, en.getValue());
+    }
+    
+    // @since 1.8
+    public void testRootValueWithCustomKey() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MapKeyMap result = m.readValue(" { \"a\": 13 } ", MapKeyMap.class);
+        assertNotNull(result);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        Map.Entry<Object,Object> en = result.entrySet().iterator().next();
+        assertEquals(ValueClass.class, en.getValue().getClass());
+        assertEquals(13, ((ValueClass) en.getValue())._a);
+        assertEquals(String[].class, en.getKey().getClass());
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
new file mode 100644
index 0000000..17f7a01
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAnyProperties.java
@@ -0,0 +1,202 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that {@link JsonAnySetter} annotation
+ * works as expected.
+ */
+public class TestAnyProperties
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    static class MapImitator
+    {
+        HashMap<String,Object> _map;
+
+        public MapImitator() {
+            _map = new HashMap<String,Object>();
+        }
+
+        @JsonAnySetter
+        void addEntry(String key, Object value)
+        {
+            _map.put(key, value);
+        }
+    }
+
+    /**
+     * Let's also verify that it is possible to define different
+     * value: not often useful, but possible.
+     */
+    static class MapImitatorWithValue
+    {
+        HashMap<String,int[]> _map;
+
+        public MapImitatorWithValue() {
+            _map = new HashMap<String,int[]>();
+        }
+
+        @JsonAnySetter
+        void addEntry(String key, int[] value)
+        {
+            _map.put(key, value);
+        }
+    }
+
+    // Bad; 2 "any setters"
+    static class Broken
+    {
+        @JsonAnySetter
+        void addEntry1(String key, Object value) { }
+        @JsonAnySetter
+        void addEntry2(String key, Object value) { }
+    }
+
+    @JsonIgnoreProperties("dummy")
+    static class Ignored
+    {
+        HashMap<String,Object> map = new HashMap<String,Object>();
+ 
+        @JsonIgnore
+        public String bogus;
+        
+        @JsonAnySetter
+        void addEntry(String key, Object value)
+        {
+            map.put(key, value);
+        }        
+    }
+
+    static class Bean744
+    {
+        protected Map<String,Object> additionalProperties;
+        
+        @JsonAnySetter
+        public void addAdditionalProperty(String key, Object value) {
+            if (additionalProperties == null) additionalProperties = new HashMap<String, Object>();
+            additionalProperties.put(key,value);
+        }
+        
+        public void setAdditionalProperties(Map<String, Object> additionalProperties) {
+            this.additionalProperties = additionalProperties;
+        }
+
+        @JsonAnyGetter
+        public Map<String,Object> getAdditionalProperties() { return additionalProperties; }
+
+        @JsonIgnore
+        public String getName() {
+           return (String) additionalProperties.get("name");
+        }
+    }
+
+    public class Bean797Base
+    {
+        @JsonAnyGetter
+        public Map<String, JsonNode> getUndefinedProperties() {
+            throw new IllegalStateException("Should not call parent version!");
+        }
+    }
+
+    public class Bean797BaseImpl extends Bean797Base
+    {
+        @Override
+        public Map<String, JsonNode> getUndefinedProperties() {
+            return new HashMap<String, JsonNode>();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleMapImitation() throws Exception
+    {
+        MapImitator mapHolder = MAPPER.readValue
+            ("{ \"a\" : 3, \"b\" : true }", MapImitator.class);
+        Map<String,Object> result = mapHolder._map;
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(3), result.get("a"));
+        assertEquals(Boolean.TRUE, result.get("b"));
+    }
+
+    public void testSimpleTyped() throws Exception
+    {
+        MapImitatorWithValue mapHolder = MAPPER.readValue
+            ("{ \"a\" : [ 3, -1 ], \"b\" : [ ] }", MapImitatorWithValue.class);
+        Map<String,int[]> result = mapHolder._map;
+        assertEquals(2, result.size());
+        assertEquals(new int[] { 3, -1 }, result.get("a"));
+        assertEquals(new int[0], result.get("b"));
+    }
+
+    public void testBrokenWithDoubleAnnotations() throws Exception
+    {
+        try {
+            @SuppressWarnings("unused")
+            Broken b = MAPPER.readValue("{ \"a\" : 3 }", Broken.class);
+            fail("Should have gotten an exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Multiple 'any-setters'");
+        }
+    }
+
+    // [JACKSON-313]
+    public void testIgnored() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        _testIgnorals(mapper);
+    }
+
+    public void testIgnored383() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+        _testIgnorals(mapper);
+    }
+
+    public void testProblem744() throws Exception
+    {
+        Bean744 bean = MAPPER.readValue("{\"name\":\"Bob\"}", Bean744.class);
+        assertNotNull(bean.additionalProperties);
+        assertEquals(1, bean.additionalProperties.size());
+        assertEquals("Bob", bean.additionalProperties.get("name"));
+    }
+
+    public void testIssue797() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new Bean797BaseImpl());
+        assertEquals("{}", json);
+    }
+
+    /*
+    /**********************************************************
+    /* Private helper methods
+    /**********************************************************
+     */
+
+    private void _testIgnorals(ObjectMapper mapper) throws Exception
+    {
+        Ignored bean = mapper.readValue("{\"name\":\"Bob\", \"bogus\": [ 1, 2, 3], \"dummy\" : 13 }", Ignored.class);
+        // as of 2.0, @JsonIgnoreProperties does block; @JsonIgnore not
+        assertNull(bean.map.get("dummy"));
+        assertEquals("[1, 2, 3]", ""+bean.map.get("bogus"));
+        assertEquals("Bob", bean.map.get("name"));
+        assertEquals(2, bean.map.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java
new file mode 100644
index 0000000..ea3fb70
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestArrayDeserialization.java
@@ -0,0 +1,568 @@
+package com.fasterxml.jackson.databind.deser;
+
+
+import java.io.*;
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * This unit test suite tries to verify that the "Native" java type
+ * mapper can properly re-construct Java array objects from Json arrays.
+ */
+public class TestArrayDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    public final static class Bean1
+    {
+        int _x, _y;
+        List<Bean2> _beans;
+
+        // Just for deserialization:
+        @SuppressWarnings("unused")
+        private Bean1() { }
+
+        public Bean1(int x, int y, List<Bean2> beans)
+        {
+            _x = x;
+            _y = y;
+            _beans = beans;
+        }
+
+        public int getX() { return _x; }
+        public int getY() { return _y; }
+        public List<Bean2> getBeans() { return _beans; }
+
+        public void setX(int x) { _x = x; }
+        public void setY(int y) { _y = y; }
+        public void setBeans(List<Bean2> b) { _beans = b; }
+
+        @Override public boolean equals(Object o) {
+            if (!(o instanceof Bean1)) return false;
+            Bean1 other = (Bean1) o;
+            return (_x == other._x)
+                && (_y == other._y)
+                && _beans.equals(other._beans)
+                ;
+        }
+    }
+
+    /**
+     * Simple bean that just gets serialized as a String value.
+     * Deserialization from String value will be done via single-arg
+     * constructor.
+     */
+    public final static class Bean2
+        implements JsonSerializable // so we can output as simple String
+    {
+        final String _desc;
+
+        public Bean2(String d)
+        {
+            _desc = d;
+        }
+
+        @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 Bean2)) return false;
+            Bean2 other = (Bean2) o;
+            return _desc.equals(other._desc);
+        }
+
+        @Override
+        public void serializeWithType(JsonGenerator jgen,
+                SerializerProvider provider, TypeSerializer typeSer)
+                throws IOException, JsonProcessingException {
+        }
+    }	
+
+    static class ObjectWrapper {
+        public Object wrapped;
+    }
+
+    static class ObjectArrayWrapper {
+    	public Object[] wrapped;
+    }
+
+    static class CustomNonDeserArrayDeserializer extends JsonDeserializer<NonDeserializable[]>
+    {
+        @Override
+        public NonDeserializable[] deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            List<NonDeserializable> list = new ArrayList<NonDeserializable>();
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                list.add(new NonDeserializable(jp.getText(), false));
+            }
+            return list.toArray(new NonDeserializable[list.size()]);
+        }
+    }
+
+    static class NonDeserializable {
+        protected String value;
+        
+        public NonDeserializable(String v, boolean bogus) {
+            value = v;
+        }
+    }
+
+    static class Product { 
+        public String name; 
+        public List<Things> thelist; 
+    }
+
+    static class Things {
+        public String height;
+        public String width;
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests for "untyped" arrays, Object[]
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testUntypedArray() throws Exception
+    {
+
+        // to get "untyped" default map-to-map, pass Object[].class
+        String JSON = "[ 1, null, \"x\", true, 2.0 ]";
+
+        Object[] result = MAPPER.readValue(JSON, Object[].class);
+        assertNotNull(result);
+
+        assertEquals(5, result.length);
+
+        assertEquals(Integer.valueOf(1), result[0]);
+        assertNull(result[1]);
+        assertEquals("x", result[2]);
+        assertEquals(Boolean.TRUE, result[3]);
+        assertEquals(Double.valueOf(2.0), result[4]);
+    }
+
+    public void testIntegerArray() throws Exception
+    {
+        final int LEN = 90000;
+
+        // Let's construct array to get it big enough
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(i);
+        }
+        sb.append(']');
+
+        Integer[] result = MAPPER.readValue(sb.toString(), Integer[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            assertEquals(i, result[i].intValue());
+        }
+    }
+
+    // [JACKSON-620]: allow "" to mean 'null' for Arrays, List and Maps
+    public void testFromEmptyString() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+        assertNull(m.readValue(quote(""), Object[].class));
+        assertNull( m.readValue(quote(""), String[].class));
+        assertNull( m.readValue(quote(""), int[].class));
+    }
+
+    // [JACKSON-620]: allow "" to mean 'null' for Arrays, List and Maps
+    public void testFromEmptyString2() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+        m.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+        Product p = m.readValue("{\"thelist\":\"\"}", Product.class);
+        assertNotNull(p);
+        assertNull(p.thelist);
+    }
+    
+    /*
+    /**********************************************************
+    /* Arrays of arrays...
+    /**********************************************************
+     */
+
+    public void testUntypedArrayOfArrays() throws Exception
+    {
+        // to get "untyped" default map-to-map, pass Object[].class
+        final String JSON = "[[[-0.027512,51.503221],[-0.008497,51.503221],[-0.008497,51.509744],[-0.027512,51.509744]]]";
+
+        Object result = MAPPER.readValue(JSON, Object.class);
+        assertEquals(ArrayList.class, result.getClass());
+        assertNotNull(result);
+
+        // Should be able to get it as an Object array as well
+
+        Object[] array = MAPPER.readValue(JSON, Object[].class);
+        assertNotNull(array);
+        assertEquals(Object[].class, array.getClass());
+
+        // and as wrapped variants too
+        ObjectWrapper w = MAPPER.readValue("{\"wrapped\":"+JSON+"}", ObjectWrapper.class);
+        assertNotNull(w);
+        assertNotNull(w.wrapped);
+        assertEquals(ArrayList.class, w.wrapped.getClass());
+
+        ObjectArrayWrapper aw = MAPPER.readValue("{\"wrapped\":"+JSON+"}", ObjectArrayWrapper.class);
+        assertNotNull(aw);
+        assertNotNull(aw.wrapped);
+    }    
+    
+    /*
+    /**********************************************************
+    /* Tests for String arrays, char[]
+    /**********************************************************
+     */
+
+    public void testStringArray() throws Exception
+    {
+        final String[] STRS = new String[] {
+            "a", "b", "abcd", "", "???", "\"quoted\"", "lf: \n",
+        };
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = new JsonFactory().createGenerator(sw);
+        jg.writeStartArray();
+        for (String str : STRS) {
+            jg.writeString(str);
+        }
+        jg.writeEndArray();
+        jg.close();
+
+        String[] result = MAPPER.readValue(sw.toString(), String[].class);
+        assertNotNull(result);
+
+        assertEquals(STRS.length, result.length);
+        for (int i = 0; i < STRS.length; ++i) {
+            assertEquals(STRS[i], result[i]);
+        }
+    }
+
+    public void testCharArray() throws Exception
+    {
+        final String TEST_STR = "Let's just test it? Ok!";
+        char[] result = MAPPER.readValue("\""+TEST_STR+"\"", char[].class);
+        assertEquals(TEST_STR, new String(result));
+
+        // And just for [JACKSON-289], let's verify that fluffy arrays work too
+        result = MAPPER.readValue("[\"a\",\"b\",\"c\"]", char[].class);
+        assertEquals("abc", new String(result));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for primitive arrays
+    /**********************************************************
+     */
+
+    public void testBooleanArray() throws Exception
+    {
+        boolean[] result = MAPPER.readValue("[ true, false, false ]", boolean[].class);
+        assertNotNull(result);
+        assertEquals(3, result.length);
+        assertTrue(result[0]);
+        assertFalse(result[1]);
+        assertFalse(result[2]);
+    }
+
+    public void testByteArrayAsNumbers() throws Exception
+    {
+        final int LEN = 37000;
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            int value = i - 128;
+            sb.append((value < 256) ? value : (value & 0x7F));
+            sb.append(',');
+        }
+        sb.append("0]");
+        byte[] result = MAPPER.readValue(sb.toString(), byte[].class);
+        assertNotNull(result);
+        assertEquals(LEN+1, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            int value = i - 128;
+            byte exp = (byte) ((value < 256) ? value : (value & 0x7F));
+            if (exp != result[i]) {
+                fail("At offset #"+i+" ("+result.length+"), expected "+exp+", got "+result[i]);
+            }
+            assertEquals(exp, result[i]);
+        }
+        assertEquals(0, result[LEN]);
+    }
+
+    public void testByteArrayAsBase64() throws Exception
+    {
+        /* Hmmh... let's use JsonGenerator here, to hopefully ensure we
+         * get proper base64 encoding. Plus, not always using that
+         * silly sample from Wikipedia.
+         */
+        JsonFactory jf = new JsonFactory();
+        StringWriter sw = new StringWriter();
+
+        int LEN = 9000;
+        byte[] TEST = new byte[LEN];
+        for (int i = 0; i < LEN; ++i) {
+            TEST[i] = (byte) i;
+        }
+
+        JsonGenerator jg = jf.createGenerator(sw);
+        jg.writeBinary(TEST);
+        jg.close();
+        String inputData = sw.toString();
+
+        byte[] result = MAPPER.readValue(inputData, byte[].class);
+        assertNotNull(result);
+        assertArrayEquals(TEST, result);
+    }
+
+    /**
+     * And then bit more challenging case; let's try decoding
+     * multiple byte arrays from an array...
+     */
+    public void testByteArraysAsBase64() throws Exception
+    {
+        JsonFactory jf = new JsonFactory();
+        StringWriter sw = new StringWriter(1000);
+
+        final int entryCount = 15;
+
+        JsonGenerator jg = jf.createGenerator(sw);
+        jg.writeStartArray();
+
+        byte[][] entries = new byte[entryCount][];
+        for (int i = 0; i < entryCount; ++i) {
+            byte[] b = new byte[1000 - i * 20];
+            for (int x = 0; x < b.length; ++x) {
+                b[x] = (byte) (i + x);
+            }
+            entries[i] = b;
+            jg.writeBinary(b);
+        }
+        jg.writeEndArray();
+        jg.close();
+
+        String inputData = sw.toString();
+
+        byte[][] result = MAPPER.readValue(inputData, byte[][].class);
+        assertNotNull(result);
+
+        assertEquals(entryCount, result.length);
+        for (int i = 0; i < entryCount; ++i) {
+            byte[] b = result[i];
+            assertArrayEquals("Comparing entry #"+i+"/"+entryCount,entries[i], b);
+        }
+    }
+
+    // [JACKSON-763]
+    public void testByteArraysWith763() throws Exception
+    {
+        String[] input = new String[] { "YQ==", "Yg==", "Yw==" };
+        byte[][] data = MAPPER.convertValue(input, byte[][].class);
+        assertEquals("a", new String(data[0], "US-ASCII"));
+        assertEquals("b", new String(data[1], "US-ASCII"));
+        assertEquals("c", new String(data[2], "US-ASCII"));
+    }
+    
+    public void testShortArray() throws Exception
+    {
+        final int LEN = 31001; // fits in signed 16-bit
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(i);
+        }
+        sb.append(']');
+
+        short[] result = MAPPER.readValue(sb.toString(), short[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            short exp = (short) i;
+            assertEquals(exp, result[i]);
+        }
+    }
+
+    public void testIntArray() throws Exception
+    {
+        final int LEN = 70000;
+
+        // Let's construct array to get it big enough
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(-i);
+        }
+        sb.append(']');
+
+        int[] result = MAPPER.readValue(sb.toString(), int[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            assertEquals(-i, result[i]);
+        }
+    }
+
+    public void testLongArray() throws Exception
+    {
+        final int LEN = 12300;
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(i);
+        }
+        sb.append(']');
+
+        long[] result = MAPPER.readValue(sb.toString(), long[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            long exp = (long) i;
+            assertEquals(exp, result[i]);
+        }
+    }
+
+    public void testDoubleArray() throws Exception
+    {
+        final int LEN = 7000;
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            // not ideal, but has to do...
+            if (i > 0) {
+                sb.append(',');
+            }
+            sb.append(i).append('.').append(i % 10);
+        }
+        sb.append(']');
+
+        double[] result = MAPPER.readValue(sb.toString(), double[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            String expStr = String.valueOf(i) + "." + String.valueOf(i % 10);
+            String actStr = String.valueOf(result[i]);
+            if (!expStr.equals(actStr)) {
+                fail("Entry #"+i+"/"+LEN+"; exp '"+expStr+"', got '"+actStr+"'");
+            }
+        }
+    }
+
+    public void testFloatArray() throws Exception
+    {
+        final int LEN = 7000;
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        for (int i = 0; i < LEN; ++i) {
+            if (i > 0) {
+                sb.append(',');
+            }
+            // not ideal, but has to do...
+            sb.append(i).append('.').append(i % 10);
+        }
+        sb.append(']');
+
+        float[] result = MAPPER.readValue(sb.toString(), float[].class);
+        assertNotNull(result);
+
+        assertEquals(LEN, result.length);
+        for (int i = 0; i < LEN; ++i) {
+            String expStr = String.valueOf(i) + "." + String.valueOf(i % 10);
+            assertEquals(expStr, String.valueOf(result[i]));
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Bean arrays
+    /**********************************************************
+     */
+
+    public void testBeanArray()
+        throws Exception
+    {
+        List<Bean1> src = new ArrayList<Bean1>();
+
+        List<Bean2> b2 = new ArrayList<Bean2>();
+        b2.add(new Bean2("a"));
+        b2.add(new Bean2("foobar"));
+        src.add(new Bean1(1, 2, b2));
+
+        b2 = new ArrayList<Bean2>();
+        b2.add(null);
+        src.add(new Bean1(4, 5, b2));
+
+        // Ok: let's assume bean serializer works ok....
+        StringWriter sw = new StringWriter();
+
+        MAPPER.writeValue(sw, src);
+
+        // And then test de-serializer
+        List<Bean1> result = MAPPER.readValue(sw.toString(), new TypeReference<List<Bean1>>() { });
+        assertNotNull(result);
+        assertEquals(src, result);
+    }
+
+    /*
+    /**********************************************************
+    /* And custom deserializers too
+    /**********************************************************
+     */
+
+    public void testCustomDeserializers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule testModule = new SimpleModule("test", Version.unknownVersion());
+        testModule.addDeserializer(NonDeserializable[].class, new CustomNonDeserArrayDeserializer());
+        mapper.registerModule(testModule);
+        
+        NonDeserializable[] result = mapper.readValue("[\"a\"]", NonDeserializable[].class);
+        assertNotNull(result);
+        assertEquals(1, result.length);
+        assertEquals("a", result[0].value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java
new file mode 100644
index 0000000..32c3ed1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestAutoDetect.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
+
+public class TestAutoDetect
+    extends BaseMapTest
+{
+    /*
+    /********************************************************
+    /* Helper beans
+    /********************************************************
+     */
+
+    static class PrivateBean {
+        String a;
+
+        private PrivateBean() { }
+
+        private PrivateBean(String a) { this.a = a; }
+    }
+    
+    /*
+    /********************************************************
+    /* Unit tests
+    /********************************************************
+     */
+    
+    public void testPrivateCtor() throws Exception
+    {
+        // first, default settings, with which construction works ok
+        ObjectMapper m = new ObjectMapper();
+        PrivateBean bean = m.readValue("\"abc\"", PrivateBean.class);
+        assertEquals("abc", bean.a);
+
+        // then by increasing visibility requirement:
+        m = new ObjectMapper();
+        // note: clumsy code, but needed for Eclipse/JDK1.5 compilation (not for 1.6)
+        VisibilityChecker<?> vc = m.getVisibilityChecker();
+        vc = vc.withCreatorVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY);
+        m.setVisibilityChecker(vc);
+        try {
+            m.readValue("\"abc\"", PrivateBean.class);
+            fail("Expected exception for missing constructor");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "no single-String constructor/factory");
+        }
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
new file mode 100644
index 0000000..fde6d39
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBasicAnnotations.java
@@ -0,0 +1,159 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+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;
+
+/**
+ * This unit test suite tests use of basic Annotations for
+ * bean deserialization; ones that indicate (non-constructor)
+ * method types, explicit deserializer annotations.
+ */
+ at SuppressWarnings("serial")
+public class TestBasicAnnotations
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    /// Class for testing {@link JsonProperty} annotations
+    final static class SizeClassSetter
+    {
+        int _size;
+        int _length;
+        int _other;
+
+        @JsonProperty public void size(int value) { _size = value; }
+        @JsonProperty("length") public void foobar(int value) { _length = value; }
+
+        // note: need not be public if annotated
+        @JsonProperty protected void other(int value) { _other = value; }
+
+        // finally: let's add a red herring that should be avoided...
+        public void errorOut(int value) { throw new Error(); }
+    }
+
+    final static class SizeClassSetter2
+    {
+        int _x;
+
+        @JsonProperty public void setX(int value) { _x = value; }
+
+        // another red herring, which shouldn't be included
+        public void setXandY(int x, int y) { throw new Error(); }
+    }
+
+    /**
+     * One more, but this time checking for implied setter
+     * using @JsonDeserialize
+     */
+    final static class SizeClassSetter3
+    {
+        int _x;
+
+        @JsonDeserialize public void x(int value) { _x = value; }
+    }
+
+
+    /// Classes for testing Setter discovery with inheritance
+    static class BaseBean
+    {
+        int _x = 0, _y = 0;
+
+        public void setX(int value) { _x = value; }
+        @JsonProperty("y") void foobar(int value) { _y = value; }
+    }
+
+    static class BeanSubClass extends BaseBean
+    {
+        int _z;
+
+        public void setZ(int value) { _z = value; }
+    }
+
+    static class BeanWithDeserialize {
+        @JsonDeserialize private int a;
+    }
+    
+    /*
+    /**********************************************************
+    /* Other helper classes
+    /**********************************************************
+     */
+
+    final static class IntsDeserializer extends StdDeserializer<int[]>
+    {
+        public IntsDeserializer() { super(int[].class); }
+        @Override
+        public int[] deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return new int[] { jp.getIntValue() };
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleSetter() throws Exception
+    {
+        SizeClassSetter result = MAPPER.readValue
+            ("{ \"other\":3, \"size\" : 2, \"length\" : -999 }",
+             SizeClassSetter.class);
+                                             
+        assertEquals(3, result._other);
+        assertEquals(2, result._size);
+        assertEquals(-999, result._length);
+    }
+
+    // Test for checking [JACKSON-64]
+    public void testSimpleSetter2() throws Exception
+    {
+        SizeClassSetter2 result = MAPPER.readValue("{ \"x\": -3 }",
+             SizeClassSetter2.class);
+        assertEquals(-3, result._x);
+    }
+
+    // Checking parts of [JACKSON-120]
+    public void testSimpleSetter3() throws Exception
+    {
+        SizeClassSetter3 result = MAPPER.readValue
+            ("{ \"x\": 128 }",
+             SizeClassSetter3.class);
+        assertEquals(128, result._x);
+    }
+
+    /**
+     * Test for verifying that super-class setters are used as
+     * expected.
+     */
+    public void testSetterInheritance() throws Exception
+    {
+        BeanSubClass result = MAPPER.readValue
+            ("{ \"x\":1, \"z\" : 3, \"y\" : 2 }",
+             BeanSubClass.class);
+        assertEquals(1, result._x);
+        assertEquals(2, result._y);
+        assertEquals(3, result._z);
+    }
+
+    public void testImpliedProperty() throws Exception
+    {
+        BeanWithDeserialize bean = MAPPER.readValue("{\"a\":3}", BeanWithDeserialize.class);
+        assertNotNull(bean);
+        assertEquals(3, bean.a);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
new file mode 100644
index 0000000..67af95f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBeanDeserializer.java
@@ -0,0 +1,329 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+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.module.SimpleModule;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapType;
+
+ at SuppressWarnings("serial")
+public class TestBeanDeserializer extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static class Bean {
+        public String b = "b";
+        public String a = "a";
+
+        public Bean() { }
+        public Bean(String a, String b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
+    static class ModuleImpl extends SimpleModule
+    {
+        protected BeanDeserializerModifier modifier;
+        
+        public ModuleImpl(BeanDeserializerModifier modifier)
+        {
+            super("test", Version.unknownVersion());
+            this.modifier = modifier;
+        }
+        
+        @Override
+        public void setupModule(SetupContext context)
+        {
+            super.setupModule(context);
+            if (modifier != null) {
+                context.addBeanDeserializerModifier(modifier);
+            }
+        }
+    }
+
+    static class RemovingModifier extends BeanDeserializerModifier
+    {
+        private final String _removedProperty;
+        
+        public RemovingModifier(String remove) { _removedProperty = remove; }
+        
+        @Override
+        public BeanDeserializerBuilder updateBuilder(DeserializationConfig config,
+                BeanDescription beanDesc, BeanDeserializerBuilder builder) {
+            builder.addIgnorable(_removedProperty);
+            return builder;
+        }
+    }
+    
+    static class ReplacingModifier extends BeanDeserializerModifier
+    {
+        private final JsonDeserializer<?> _deserializer;
+        
+        public ReplacingModifier(JsonDeserializer<?> s) { _deserializer = s; }
+        
+        @Override
+        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return _deserializer;
+        }
+    }
+
+    static class BogusBeanDeserializer extends JsonDeserializer<Object>
+    {
+        private final String a, b;
+        
+        public BogusBeanDeserializer(String a, String b) {
+            this.a = a;
+            this.b = b;
+        }
+        
+        @Override
+        public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            return new Bean(a, b);
+        }
+    }
+
+    static class Issue476Bean {
+        public Issue476Type value1, value2;
+    }
+    static class Issue476Type {
+        public String name, value;
+    }
+    static class Issue476Deserializer extends BeanDeserializer
+        implements ContextualDeserializer
+    {
+        protected static int propCount;
+
+        public Issue476Deserializer(BeanDeserializer src) {
+            super(src);
+        }
+
+        @Override
+        public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+                BeanProperty property) throws JsonMappingException {
+            propCount++;
+            return this;
+        }        
+    }
+    public class Issue476DeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            if (Issue476Type.class == beanDesc.getBeanClass()) {
+                return new Issue476Deserializer((BeanDeserializer)deserializer);
+            }
+            return super.modifyDeserializer(config, beanDesc, deserializer);
+        }        
+    }
+    public class Issue476Module extends SimpleModule
+    {
+        public Issue476Module() {
+            super("Issue476Module", Version.unknownVersion());
+        }
+        
+        @Override
+        public void setupModule(SetupContext context) {
+            context.addBeanDeserializerModifier(new Issue476DeserializerModifier());
+        }        
+    }
+    
+    // [Issue#121], arrays, collections, maps
+
+    enum EnumABC { A, B, C; }
+    
+    static class ArrayDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public JsonDeserializer<?> modifyArrayDeserializer(DeserializationConfig config, ArrayType valueType,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) {
+                @Override public Object deserialize(JsonParser jp,
+                        DeserializationContext ctxt) {
+                    return new String[] { "foo" };
+                }
+            };
+        }
+    }
+
+    static class CollectionDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public JsonDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config, CollectionType valueType,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) {
+                @Override public Object deserialize(JsonParser jp,
+                        DeserializationContext ctxt) {
+                    ArrayList<String> list = new ArrayList<String>();
+                    list.add("foo");
+                    return list;
+                }
+            };
+        }
+    }
+
+    static class MapDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public JsonDeserializer<?> modifyMapDeserializer(DeserializationConfig config, MapType valueType,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) {
+                @Override public Object deserialize(JsonParser jp,
+                        DeserializationContext ctxt) {
+                    HashMap<String,String> map = new HashMap<String,String>();
+                    map.put("a", "foo");
+                    return map;
+                }
+            };
+        }
+    }
+
+    static class EnumDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public JsonDeserializer<?> modifyEnumDeserializer(DeserializationConfig config, JavaType valueType,
+                BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
+            return (JsonDeserializer<?>) new StdDeserializer<Object>(Object.class) {
+                @Override public Object deserialize(JsonParser jp,
+                        DeserializationContext ctxt) {
+                    return "foo";
+                }
+            };
+        }
+    }
+
+    static class KeyDeserializerModifier extends BeanDeserializerModifier {
+        @Override
+        public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, JavaType valueType,
+                KeyDeserializer kd) {
+            return new KeyDeserializer() {
+                @Override
+                public Object deserializeKey(String key,
+                        DeserializationContext ctxt) throws IOException,
+                        JsonProcessingException {
+                    return "foo";
+                }
+            };
+        }
+    }
+    
+    /*
+    /********************************************************
+    /* Unit tests
+    /********************************************************
+     */
+
+    public void testPropertyRemoval() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new RemovingModifier("a")));
+        Bean bean = mapper.readValue("{\"b\":\"2\"}", Bean.class);
+        assertEquals("2", bean.b);
+        // and 'a' has its default value:
+        assertEquals("a", bean.a);
+    } 
+
+    public void testDeserializerReplacement() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanDeserializer("foo", "bar"))));
+        Bean bean = mapper.readValue("{\"a\":\"xyz\"}", Bean.class);
+        // custom deserializer always produces instance like this:
+        assertEquals("foo", bean.a);
+        assertEquals("bar", bean.b);
+    }
+
+    public void testIssue476() throws Exception
+    {
+        final String JSON = "{\"value1\" : {\"name\" : \"fruit\", \"value\" : \"apple\"}, \"value2\" : {\"name\" : \"color\", \"value\" : \"red\"}}";
+        
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new Issue476Module());
+        mapper.readValue(JSON, Issue476Bean.class);
+
+        // there are 2 properties
+        assertEquals(2, Issue476Deserializer.propCount);
+    }
+
+    public void testPOJOFromEmptyString() throws Exception
+    {
+        // first, verify default settings which do not accept empty String:
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            mapper.readValue(quote(""), Bean.class);
+            fail("Should not accept Empty String for POJO");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "from String value");
+        }
+
+        // should be ok to enable dynamically:
+        mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+        Bean result = mapper.readValue(quote(""), Bean.class);
+        assertNull(result);
+    }
+
+    // [Issue#120]
+    public void testModifyArrayDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setDeserializerModifier(new ArrayDeserializerModifier()));
+        Object[] result = mapper.readValue("[1,2]", Object[].class);
+        assertEquals(1, result.length);
+        assertEquals("foo", result[0]);
+    }
+
+    public void testModifyCollectionDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setDeserializerModifier(new CollectionDeserializerModifier())
+        );
+        List<?> result = mapper.readValue("[1,2]", List.class);
+        assertEquals(1, result.size());
+        assertEquals("foo", result.get(0));
+    }
+
+    public void testModifyMapDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setDeserializerModifier(new MapDeserializerModifier())
+        );
+        Map<?,?> result = mapper.readValue("{\"a\":1,\"b\":2}", Map.class);
+        assertEquals(1, result.size());
+        assertEquals("foo", result.get("a"));
+    }
+
+    public void testModifyEnumDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setDeserializerModifier(new EnumDeserializerModifier())
+        );
+        Object result = mapper.readValue(quote("B"), EnumABC.class);
+        assertEquals("foo", result);
+    }
+
+    public void testModifyKeyDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setDeserializerModifier(new KeyDeserializerModifier())
+        );
+        Map<?,?> result = mapper.readValue("{\"a\":1}", Map.class);
+        assertEquals(1, result.size());
+        assertEquals("foo", result.entrySet().iterator().next().getKey());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestBlocking.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestBlocking.java
new file mode 100644
index 0000000..b95da1e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestBlocking.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit test mostly written to cover issue [JACKSON-81]; unintended blocking
+ * after data binding.
+ */
+public class TestBlocking
+    extends BaseMapTest
+{
+    /**
+     * This is an indirect test that should trigger problems if (and only if)
+     * underlying parser is advanced beyond the only element array.
+     * Basically, although content is invalid, this should be encountered
+     * quite yet.
+     */
+    public void testEagerAdvance() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonParser jp = createParserUsingReader("[ 1  ");
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+
+        // And then try to map just a single entry: shouldn't fail:
+        Integer I = mapper.readValue(jp, Integer.class);
+        assertEquals(Integer.valueOf(1), I);
+
+        // and should fail only now:
+        try {
+            jp.nextToken();
+        } catch (IOException ioe) {
+            verifyException(ioe, "Unexpected end-of-input: expected close marker for ARRAY");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
new file mode 100644
index 0000000..581332a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCollectionDeserialization.java
@@ -0,0 +1,199 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ArrayBlockingQueue;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+ at SuppressWarnings("serial")
+public class TestCollectionDeserialization
+    extends BaseMapTest
+{
+    enum Key {
+        KEY1, KEY2, WHATEVER;
+    }
+
+    @JsonDeserialize(using=ListDeserializer.class)
+    static class CustomList extends LinkedList<String> { }
+
+    static class ListDeserializer extends StdDeserializer<CustomList>
+    {
+        public ListDeserializer() { super(CustomList.class); }
+
+        @Override
+        public CustomList deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException
+        {
+            CustomList result = new CustomList();
+            result.add(jp.getText());
+            return result;
+        }
+    }
+
+    static class XBean {
+        public int x;
+    }
+
+    // [Issue#199]
+    static class ListAsIterable {
+        public Iterable<String> values;
+    }
+
+    static class ListAsIterableX {
+        public Iterable<XBean> nums;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testUntypedList() throws Exception
+    {
+        // to get "untyped" default List, pass Object.class
+        String JSON = "[ \"text!\", true, null, 23 ]";
+
+        // Not a guaranteed cast theoretically, but will work:
+        // (since we know that Jackson will construct an ArrayList here...)
+        Object value = MAPPER.readValue(JSON, Object.class);
+        assertNotNull(value);
+        assertTrue(value instanceof ArrayList<?>);
+        List<?> result = (List<?>) value;
+
+        assertEquals(4, result.size());
+
+        assertEquals("text!", result.get(0));
+        assertEquals(Boolean.TRUE, result.get(1));
+        assertNull(result.get(2));
+        assertEquals(Integer.valueOf(23), result.get(3));
+    }
+
+    public void testExactStringCollection() throws Exception
+    {
+        // to get typing, must use type reference
+        String JSON = "[ \"a\", \"b\" ]";
+        List<String> result = MAPPER.readValue(JSON, new TypeReference<ArrayList<String>>() { });
+
+        assertNotNull(result);
+        assertEquals(ArrayList.class, result.getClass());
+        assertEquals(2, result.size());
+
+        assertEquals("a", result.get(0));
+        assertEquals("b", result.get(1));
+    }
+
+    public void testHashSet() throws Exception
+    {
+        String JSON = "[ \"KEY1\", \"KEY2\" ]";
+
+        EnumSet<Key> result = MAPPER.readValue(JSON, new TypeReference<EnumSet<Key>>() { });
+        assertNotNull(result);
+        assertTrue(EnumSet.class.isAssignableFrom(result.getClass()));
+        assertEquals(2, result.size());
+
+        assertTrue(result.contains(Key.KEY1));
+        assertTrue(result.contains(Key.KEY2));
+        assertFalse(result.contains(Key.WHATEVER));
+    }
+
+    /// Test to verify that @JsonDeserialize.using works as expected
+    public void testCustomDeserializer() throws IOException
+    {
+        CustomList result = MAPPER.readValue(quote("abc"), CustomList.class);
+        assertEquals(1, result.size());
+        assertEquals("abc", result.get(0));
+    }
+
+    // Testing [JACKSON-526], "implicit JSON array" for single-element arrays,
+    // mostly produced by Jettison, Badgerfish conversions (from XML)
+    @SuppressWarnings("unchecked")
+    public void testImplicitArrays() throws Exception
+    {
+        // can't share mapper, custom configs (could create ObjectWriter tho)
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+
+        // first with simple scalar types (numbers), with collections
+        List<Integer> ints = mapper.readValue("4", List.class);
+        assertEquals(1, ints.size());
+        assertEquals(Integer.valueOf(4), ints.get(0));
+        List<String> strings = mapper.readValue(quote("abc"), new TypeReference<ArrayList<String>>() { });
+        assertEquals(1, strings.size());
+        assertEquals("abc", strings.get(0));
+        // and arrays:
+        int[] intArray = mapper.readValue("-7", int[].class);
+        assertEquals(1, intArray.length);
+        assertEquals(-7, intArray[0]);
+        String[] stringArray = mapper.readValue(quote("xyz"), String[].class);
+        assertEquals(1, stringArray.length);
+        assertEquals("xyz", stringArray[0]);
+
+        // and then with Beans:
+        List<XBean> xbeanList = mapper.readValue("{\"x\":4}", new TypeReference<List<XBean>>() { });
+        assertEquals(1, xbeanList.size());
+        assertEquals(XBean.class, xbeanList.get(0).getClass());
+
+        Object ob = mapper.readValue("{\"x\":29}", XBean[].class);
+        XBean[] xbeanArray = (XBean[]) ob;
+        assertEquals(1, xbeanArray.length);
+        assertEquals(XBean.class, xbeanArray[0].getClass());
+    }
+
+    // [JACKSON-620]: allow "" to mean 'null' for Maps
+    public void testFromEmptyString() throws Exception
+    {
+        ObjectReader r = MAPPER.reader(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
+        List<?> result = r.withType(List.class).readValue(quote(""));
+        assertNull(result);
+    }
+
+    // [Issue#161]
+    public void testArrayBlockingQueue() throws Exception
+    {
+        // ok to skip polymorphic type to get Object
+        ArrayBlockingQueue<?> q = MAPPER.readValue("[1, 2, 3]", ArrayBlockingQueue.class);
+        assertNotNull(q);
+        assertEquals(3, q.size());
+        assertEquals(Integer.valueOf(1), q.take());
+        assertEquals(Integer.valueOf(2), q.take());
+        assertEquals(Integer.valueOf(3), q.take());
+    }
+
+    // [Issue#199]
+    public void testIterableWithStrings() throws Exception
+    {
+        String JSON = "{ \"values\":[\"a\",\"b\"]}";
+        ListAsIterable w = MAPPER.readValue(JSON, ListAsIterable.class);
+        assertNotNull(w);
+        assertNotNull(w.values);
+        Iterator<String> it = w.values.iterator();
+        assertTrue(it.hasNext());
+        assertEquals("a", it.next());
+        assertEquals("b", it.next());
+        assertFalse(it.hasNext());
+    }
+
+    public void testIterableWithBeans() throws Exception
+    {
+        String JSON = "{ \"nums\":[{\"x\":1},{\"x\":2}]}";
+        ListAsIterableX w = MAPPER.readValue(JSON, ListAsIterableX.class);
+        assertNotNull(w);
+        assertNotNull(w.nums);
+        Iterator<XBean> it = w.nums.iterator();
+        assertTrue(it.hasNext());
+        XBean xb = it.next();
+        assertNotNull(xb);
+        assertEquals(1, xb.x);
+        xb = it.next();
+        assertEquals(2, xb.x);
+        assertFalse(it.hasNext());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java
new file mode 100644
index 0000000..aea0e49
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestConcurrency.java
@@ -0,0 +1,98 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * Testing for [JACKSON-237] (NPE due to race condition)
+ */
+public class TestConcurrency extends BaseMapTest
+{
+    /*
+    /**********************************************
+    /* Helper beans
+    /**********************************************
+     */
+
+    @JsonDeserialize(using=BeanDeserializer.class)
+    static class Bean
+    {
+        public int value = 42;
+    }
+
+    /*
+    /**********************************************
+    /* Helper classes
+    /**********************************************
+     */
+    
+    /**
+     * Dummy deserializer used for verifying that partially handled (i.e. not yet
+     * resolved) deserializers are not allowed to be used.
+     */
+    static class BeanDeserializer
+        extends JsonDeserializer<Bean>
+        implements ResolvableDeserializer
+    {
+        protected volatile boolean resolved = false;
+        
+        @Override
+        public Bean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
+        {
+            if (!resolved) {
+                throw new IOException("Deserializer not yet completely resolved");
+            }
+            Bean b = new Bean();
+            b.value = 13;
+            return b;
+        }
+
+        @Override
+        public void resolve(DeserializationContext ctxt) throws JsonMappingException
+        {
+            try {
+                Thread.sleep(100L);
+            } catch (Exception e) { }
+            resolved = true;
+        }
+    }
+
+    /*
+    /**********************************************
+    /* Unit tests
+    /**********************************************
+     */
+
+    public void testDeserializerResolution() throws Exception
+    {
+        /* Let's repeat couple of times, just to be sure; thread timing is not
+         * exact science; plus caching plays a role too
+         */
+        final String JSON = "{\"value\":42}";
+        
+        for (int i = 0; i < 5; ++i) {
+            final ObjectMapper mapper = new ObjectMapper();
+            Runnable r = new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        /*Bean b =*/ mapper.readValue(JSON, Bean.class);
+                    } catch (Exception e) { }
+                }
+            };
+            Thread t = new Thread(r);
+            t.start();
+            // then let it proceed
+            Thread.sleep(10L);
+            // and try the same...
+            Bean b = mapper.readValue(JSON, Bean.class);
+            // note: funny deserializer, mangles data.. :)
+            assertEquals(13, b.value);
+            t.join();
+        }   
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java
new file mode 100644
index 0000000..6b2dbf4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestConfig.java
@@ -0,0 +1,120 @@
+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
new file mode 100644
index 0000000..5081fbb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomDeserializers.java
@@ -0,0 +1,229 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+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.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.deser.std.StdDelegatingDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.util.StdConverter;
+
+/**
+ * Test to check that customizations work as expected.
+ */
+ at SuppressWarnings("serial")
+public class TestCustomDeserializers
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class DummyDeserializer<T>
+        extends StdDeserializer<T>
+    {
+        final T value;
+
+        public DummyDeserializer(T v, Class<T> cls) {
+            super(cls);
+            value = v;
+        }
+
+        @Override
+        public T deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // need to skip, if structured...
+            jp.skipChildren();
+            return value;
+        }
+    }
+
+    static class TestBeans {
+        public List<TestBean> beans;
+    }
+    static class TestBean {
+        public CustomBean c;
+        public String d;
+    }
+    @JsonDeserialize(using=CustomBeanDeserializer.class)
+    static class CustomBean {
+        protected final int a, b;
+        public CustomBean(int a, int b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
+    static class CustomBeanDeserializer extends JsonDeserializer<CustomBean>
+    {
+        @Override
+        public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            int a = 0, b = 0;
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.START_OBJECT) {
+                t = jp.nextToken();
+            } else if (t != JsonToken.FIELD_NAME) {
+                throw new Error();
+            }
+            while(t == JsonToken.FIELD_NAME) {
+                final String fieldName = jp.getCurrentName();
+                t = jp.nextToken();
+                if (t != JsonToken.VALUE_NUMBER_INT) {
+                    throw new JsonParseException("expecting number got "+ t, jp.getCurrentLocation());
+                }
+                if (fieldName.equals("a")) {
+                    a = jp.getIntValue();
+                } else if (fieldName.equals("b")) {
+                    b = jp.getIntValue();
+                } else {
+                    throw new Error();
+                }
+                t = jp.nextToken();
+            }
+            return new CustomBean(a, b);
+        }
+    }
+
+    public static class Immutable {
+        protected int x, y;
+        
+        public Immutable(int x0, int y0) {
+            x = x0;
+            y = y0;
+        }
+    }
+
+    // [JACKSON-882]
+    public static class CustomKey {
+        private final int id;
+
+        public CustomKey(int id) {this.id = id;}
+
+        public int getId() { return id; }
+    }
+    
+    public static class Model
+    {
+        protected final Map<CustomKey, String> map;
+
+        @JsonCreator
+        public Model(@JsonProperty("map") @JsonDeserialize(keyUsing = CustomKeyDeserializer.class) Map<CustomKey, String> map)
+        {
+            this.map = new HashMap<CustomKey, String>(map);
+        }
+
+        @JsonProperty
+        @JsonSerialize(keyUsing = CustomKeySerializer.class)
+        public Map<CustomKey, String> getMap() {
+            return map;
+        }
+    }
+     
+    static class CustomKeySerializer extends JsonSerializer<CustomKey> {
+        @Override
+        public void serialize(CustomKey value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeFieldName(String.valueOf(value.getId()));
+        }
+    }
+
+    static class CustomKeyDeserializer extends KeyDeserializer {
+        @Override
+        public CustomKey deserializeKey(String key, DeserializationContext ctxt) throws IOException {
+            return new CustomKey(Integer.valueOf(key));
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testCustomBeanDeserializer() throws Exception
+    {
+        String json = "{\"beans\":[{\"c\":{\"a\":10,\"b\":20},\"d\":\"hello, tatu\"}]}";
+        TestBeans beans = MAPPER.readValue(json, TestBeans.class);
+
+        assertNotNull(beans);
+        List<TestBean> results = beans.beans;
+        assertNotNull(results);
+        assertEquals(1, results.size());
+        TestBean bean = results.get(0);
+        assertEquals("hello, tatu", bean.d);
+        CustomBean c = bean.c;
+        assertNotNull(c);
+        assertEquals(10, c.a);
+        assertEquals(20, c.b);
+
+        json = "{\"beans\":[{\"c\":{\"b\":3,\"a\":-4},\"d\":\"\"},"
+            +"{\"d\":\"abc\", \"c\":{\"b\":15}}]}";
+        beans = MAPPER.readValue(json, TestBeans.class);
+
+        assertNotNull(beans);
+        results = beans.beans;
+        assertNotNull(results);
+        assertEquals(2, results.size());
+
+        bean = results.get(0);
+        assertEquals("", bean.d);
+        c = bean.c;
+        assertNotNull(c);
+        assertEquals(-4, c.a);
+        assertEquals(3, c.b);
+
+        bean = results.get(1);
+        assertEquals("abc", bean.d);
+        c = bean.c;
+        assertNotNull(c);
+        assertEquals(0, c.a);
+        assertEquals(15, c.b);
+    }
+
+    // [Issue#87]: delegating deserializer
+    public void testDelegating() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addDeserializer(Immutable.class,
+            new StdDelegatingDeserializer<Immutable>(
+                new StdConverter<JsonNode, Immutable>() {
+                    @Override
+                    public Immutable convert(JsonNode value)
+                    {
+                        int x = value.path("x").asInt();
+                        int y = value.path("y").asInt();
+                        return new Immutable(x, y);
+                    }
+                }
+                ));
+
+        mapper.registerModule(module);
+        Immutable imm = mapper.readValue("{\"x\":3,\"y\":7}", Immutable.class);
+        assertEquals(3, imm.x);
+        assertEquals(7, imm.y);
+    }
+
+    public void testIssue882() throws Exception
+    {
+        Model original = new Model(Collections.singletonMap(new CustomKey(123), "test"));
+        String json = MAPPER.writeValueAsString(original);
+        Model deserialized = MAPPER.readValue(json, Model.class);
+        assertNotNull(deserialized);
+        assertNotNull(deserialized.map);
+        assertEquals(1, deserialized.map.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java
new file mode 100644
index 0000000..bf77093
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCustomFactory.java
@@ -0,0 +1,140 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.util.*;
+
+
+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;
+
+/**
+ * Test to check that customizations work as expected.
+ */
+ at SuppressWarnings("serial")
+public class TestCustomFactory
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class DummyDeserializer<T>
+        extends StdDeserializer<T>
+    {
+        final T value;
+
+        public DummyDeserializer(T v, Class<T> cls) {
+            super(cls);
+            value = v;
+        }
+
+        @Override
+        public T deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            // need to skip, if structured...
+            jp.skipChildren();
+            return value;
+        }
+    }
+
+    static class TestBeans {
+        public List<TestBean> beans;
+    }
+    static class TestBean {
+        public CustomBean c;
+        public String d;
+    }
+    @JsonDeserialize(using=CustomBeanDeserializer.class)
+    static class CustomBean {
+        protected final int a, b;
+        public CustomBean(int a, int b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
+    static class CustomBeanDeserializer extends JsonDeserializer<CustomBean>
+    {
+        @Override
+        public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException
+        {
+            int a = 0, b = 0;
+            JsonToken t = jp.getCurrentToken();
+            if (t == JsonToken.START_OBJECT) {
+                t = jp.nextToken();
+            } else if (t != JsonToken.FIELD_NAME) {
+                throw new Error();
+            }
+            while(t == JsonToken.FIELD_NAME) {
+                final String fieldName = jp.getCurrentName();
+                t = jp.nextToken();
+                if (t != JsonToken.VALUE_NUMBER_INT) {
+                    throw new JsonParseException("expecting number got "+ t, jp.getCurrentLocation());
+                }
+                if (fieldName.equals("a")) {
+                    a = jp.getIntValue();
+                } else if (fieldName.equals("b")) {
+                    b = jp.getIntValue();
+                } else {
+                    throw new Error();
+                }
+                t = jp.nextToken();
+            }
+            return new CustomBean(a, b);
+        }
+    }
+
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testCustomBeanDeserializer() throws Exception
+    {
+
+        final ObjectMapper map = new ObjectMapper();
+        String json = "{\"beans\":[{\"c\":{\"a\":10,\"b\":20},\"d\":\"hello, tatu\"}]}";
+        TestBeans beans = map.readValue(json, TestBeans.class);
+
+        assertNotNull(beans);
+        List<TestBean> results = beans.beans;
+        assertNotNull(results);
+        assertEquals(1, results.size());
+        TestBean bean = results.get(0);
+        assertEquals("hello, tatu", bean.d);
+        CustomBean c = bean.c;
+        assertNotNull(c);
+        assertEquals(10, c.a);
+        assertEquals(20, c.b);
+
+        json = "{\"beans\":[{\"c\":{\"b\":3,\"a\":-4},\"d\":\"\"},"
+            +"{\"d\":\"abc\", \"c\":{\"b\":15}}]}";
+        beans = map.readValue(json, TestBeans.class);
+
+        assertNotNull(beans);
+        results = beans.beans;
+        assertNotNull(results);
+        assertEquals(2, results.size());
+
+        bean = results.get(0);
+        assertEquals("", bean.d);
+        c = bean.c;
+        assertNotNull(c);
+        assertEquals(-4, c.a);
+        assertEquals(3, c.b);
+
+        bean = results.get(1);
+        assertEquals("abc", bean.d);
+        c = bean.c;
+        assertNotNull(c);
+        assertEquals(0, c.a);
+        assertEquals(15, c.b);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java
new file mode 100644
index 0000000..86cb185
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestCyclicTypes.java
@@ -0,0 +1,86 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Simple unit tests to verify that it is possible to handle
+ * potentially cyclic structures, as long as object graph itself
+ * is not cyclic. This is the case for directed hierarchies like
+ * trees and DAGs.
+ */
+public class TestCyclicTypes
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class Bean
+    {
+        Bean _next;
+        String _name;
+
+        public Bean() { }
+
+        public void setNext(Bean b) { _next = b; }
+        public void setName(String n) { _name = n; }
+
+    }
+
+    static class LinkA {
+        public LinkB next;
+    }
+
+    static class LinkB {
+        private LinkA a;
+
+        public void setA(LinkA a) { this.a = a; }
+        public LinkA getA() { return a; }
+    }
+
+    static class GenericLink<T> {
+        public GenericLink<T> next;
+    }
+
+    static class StringLink extends GenericLink<String> {
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testLinked() throws Exception
+    {
+        Bean first = new ObjectMapper().readValue
+            ("{\"name\":\"first\", \"next\": { "
+             +" \"name\":\"last\", \"next\" : null }}",
+             Bean.class);
+
+        assertNotNull(first);
+        assertEquals("first", first._name);
+        Bean last = first._next;
+        assertNotNull(last);
+        assertEquals("last", last._name);
+        assertNull(last._next);
+    }
+
+    public void testLinkedGeneric() throws Exception
+    {
+        StringLink link = new ObjectMapper().readValue
+            ("{\"next\":null}", StringLink.class);
+        assertNotNull(link);
+        assertNull(link.next);
+    }
+
+    public void testCycleWith2Classes() throws Exception
+    {
+        LinkA a = new ObjectMapper().readValue("{\"next\":{\"a\":null}}", LinkA.class);
+        assertNotNull(a.next);
+        LinkB b = a.next;
+        assertNull(b.a);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
new file mode 100644
index 0000000..77ced8c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestDateDeserialization.java
@@ -0,0 +1,359 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+
+public class TestDateDeserialization
+    extends BaseMapTest
+{
+    // Test for [JACKSON-435]
+    static class DateAsStringBean
+    {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="/yyyy/MM/dd/")
+        public Date date;
+    }
+
+    static class CalendarAsStringBean
+    {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern=";yyyy/MM/dd;")
+        public Calendar cal;
+    }
+
+    static class DateInCETBean {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH", timezone="CET")
+        public Date date;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testDateUtil() throws Exception
+    {
+        long now = 123456789L;
+        java.util.Date value = new java.util.Date(now);
+
+        // First from long
+        assertEquals(value, MAPPER.readValue(""+now, java.util.Date.class));
+
+        // then from String
+        String dateStr = dateToString(value);
+        java.util.Date result = MAPPER.readValue("\""+dateStr+"\"", java.util.Date.class);
+
+        assertEquals("Date: expect "+value+" ("+value.getTime()+"), got "+result+" ("+result.getTime()+")",
+                value.getTime(), result.getTime());
+    }
+
+    public void testDateUtilWithStringTimestamp() throws Exception
+    {
+        long now = 1321992375446L;
+        /* As of 1.5.0, 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));
+        java.util.Date value = MAPPER.readValue(json, java.util.Date.class);
+        assertEquals(now, value.getTime());
+    }
+
+    public void testDateUtilRFC1123() throws Exception
+    {
+        DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+        // let's use an arbitrary value...
+        String inputStr = "Sat, 17 Jan 2009 06:13:58 +0000";
+        java.util.Date inputDate = fmt.parse(inputStr);
+        assertEquals(inputDate, MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class));
+    }
+
+    public void testDateUtilRFC1123OnNonUSLocales() throws Exception
+    {
+        Locale old = Locale.getDefault();
+        Locale.setDefault(Locale.GERMAN);
+        DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+        // let's use an arbitrary value...
+        String inputStr = "Sat, 17 Jan 2009 06:13:58 +0000";
+        java.util.Date inputDate = fmt.parse(inputStr);
+        assertEquals(inputDate, MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class));
+        Locale.setDefault(old);
+    }
+
+    /**
+     * ISO8601 is supported as well
+     */
+    public void testDateUtilISO8601() throws Exception
+    {
+        /* let's use simple baseline value, arbitrary date in GMT,
+         * using the standard notation
+         */
+        String inputStr = "1972-12-28T00:00:00.000+0000";
+        Date inputDate = MAPPER.readValue("\""+inputStr+"\"", java.util.Date.class);
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        c.setTime(inputDate);
+        assertEquals(1972, c.get(Calendar.YEAR));
+        assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH));
+        assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+
+        // And then the same, but using 'Z' as alias for +0000 (very common)
+        inputStr = "1972-12-28T00:00:00.000Z";
+        inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        c.setTime(inputDate);
+        assertEquals(1972, c.get(Calendar.YEAR));
+        assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH));
+        assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+
+        // Same but using colon in timezone
+        inputStr = "1972-12-28T00:00:00.000+00:00";
+        inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        c.setTime(inputDate);
+        assertEquals(1972, c.get(Calendar.YEAR));
+        assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH));
+        assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+
+        // Same but only passing hour difference as timezone
+        inputStr = "1972-12-28T00:00:00.000+00";
+        inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        c.setTime(inputDate);
+        assertEquals(1972, c.get(Calendar.YEAR));
+        assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH));
+        assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+
+        inputStr = "1984-11-30T00:00:00.000Z";
+        inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        c.setTime(inputDate);
+        assertEquals(1984, c.get(Calendar.YEAR));
+        assertEquals(Calendar.NOVEMBER, c.get(Calendar.MONTH));
+        assertEquals(30, c.get(Calendar.DAY_OF_MONTH));
+    }
+
+    public void testDateUtilISO8601NoTimezone() throws Exception
+    {
+        // Timezone itself is optional as well... 
+        String inputStr = "1984-11-13T00:00:09";
+        Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        c.setTime(inputDate);
+        assertEquals(1984, c.get(Calendar.YEAR));
+        assertEquals(Calendar.NOVEMBER, c.get(Calendar.MONTH));
+        assertEquals(13, c.get(Calendar.DAY_OF_MONTH));
+        assertEquals(0, c.get(Calendar.HOUR_OF_DAY));
+        assertEquals(0, c.get(Calendar.MINUTE));
+        assertEquals(9, c.get(Calendar.SECOND));
+        assertEquals(0, c.get(Calendar.MILLISECOND));
+    }
+
+    public void testDateUtilISO8601JustDate() throws Exception
+    {
+        // Plain date (no time)
+        String inputStr = "1972-12-28";
+        Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        c.setTime(inputDate);
+        assertEquals(1972, c.get(Calendar.YEAR));
+        assertEquals(Calendar.DECEMBER, c.get(Calendar.MONTH));
+        assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testDateSql() throws Exception
+    {
+        java.sql.Date value = new java.sql.Date(0L);
+        value.setYear(99); // 1999
+        value.setDate(19);
+        value.setMonth(Calendar.APRIL);
+        long now = value.getTime();
+
+        // First from long
+        assertEquals(value, MAPPER.readValue(String.valueOf(now), java.sql.Date.class));
+
+        // then from default java.sql.Date String serialization:
+        
+        java.sql.Date result = MAPPER.readValue(quote(value.toString()), java.sql.Date.class);
+        Calendar c = gmtCalendar(result.getTime());
+        assertEquals(1999, c.get(Calendar.YEAR));
+        assertEquals(Calendar.APRIL, c.get(Calendar.MONTH));
+        assertEquals(19, c.get(Calendar.DAY_OF_MONTH));
+
+        /* [JACKSON-200]: looks like we better add support for regular date
+         *   formats as well
+         */
+        String expStr = "1981-07-13";
+        result = MAPPER.readValue(quote(expStr), java.sql.Date.class);
+        c.setTimeInMillis(result.getTime());
+        assertEquals(1981, c.get(Calendar.YEAR));
+        assertEquals(Calendar.JULY, c.get(Calendar.MONTH));
+        assertEquals(13, c.get(Calendar.DAY_OF_MONTH));
+
+        /* 20-Nov-2009, tatus: I'll be damned if I understand why string serialization
+         *   is off-by-one, but day-of-month does seem to be one less. My guess is
+         *   that something is funky with timezones (i.e. somewhere local TZ is
+         *   being used), but just can't resolve it. Hence, need to comment this:
+         */
+//        assertEquals(expStr, result.toString());
+    }
+
+    public void testCalendar() throws Exception
+    {
+        // not ideal, to use (ever-changing) current date, but...
+        java.util.Calendar value = Calendar.getInstance();
+        long l = 12345678L;
+        value.setTimeInMillis(l);
+
+        // First from long
+        Calendar result = MAPPER.readValue(""+l, Calendar.class);
+        assertEquals(l, result.getTimeInMillis());
+
+        // Then from serialized String
+        String dateStr = dateToString(new Date(l));
+        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());
+    }
+
+    public void testCustom() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss");
+        df.setTimeZone(TimeZone.getTimeZone("PST"));
+        mapper.setDateFormat(df);
+
+        String dateStr = "1972-12-28X15:45:00";
+        java.util.Date exp = df.parse(dateStr);
+        java.util.Date result = mapper.readValue("\""+dateStr+"\"", java.util.Date.class);
+        assertEquals(exp, result);
+    }
+
+    /**
+     * Test for [JACKSON-203]: make empty Strings deserialize as nulls by default,
+     * without need to turn on feature (which may be added in future)
+     */
+    public void testDatesWithEmptyStrings() throws Exception
+    {
+        assertNull(MAPPER.readValue(quote(""), java.util.Date.class));
+        assertNull(MAPPER.readValue(quote(""), java.util.Calendar.class));
+        assertNull(MAPPER.readValue(quote(""), java.sql.Date.class));
+    }
+
+    // for [JACKSON-334]
+    public void test8601DateTimeNoMilliSecs() throws Exception
+    {
+        // ok, Zebra, no milliseconds
+        for (String inputStr : new String[] {
+               "2010-06-28T23:34:22Z",
+               "2010-06-28T23:34:22+0000",
+               "2010-06-28T23:34:22+00",
+        }) {
+            Date inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
+            Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+            c.setTime(inputDate);
+            assertEquals(2010, c.get(Calendar.YEAR));
+            assertEquals(Calendar.JUNE, c.get(Calendar.MONTH));
+            assertEquals(28, c.get(Calendar.DAY_OF_MONTH));
+            assertEquals(23, c.get(Calendar.HOUR_OF_DAY));
+            assertEquals(34, c.get(Calendar.MINUTE));
+            assertEquals(22, c.get(Calendar.SECOND));
+            assertEquals(0, c.get(Calendar.MILLISECOND));
+        }
+    }
+
+    public void testTimeZone() throws Exception
+    {
+        TimeZone result = MAPPER.readValue(quote("PST"), TimeZone.class);
+        assertEquals("PST", result.getID());
+    }
+
+    public void testCustomDateWithAnnotation() throws Exception
+    {
+        DateAsStringBean result = MAPPER.readValue("{\"date\":\"/2005/05/25/\"}", DateAsStringBean.class);
+        assertNotNull(result);
+        assertNotNull(result.date);
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        long l = result.date.getTime();
+        if (l == 0L) {
+            fail("Should not get null date");
+        }
+        c.setTimeInMillis(l);
+        assertEquals(2005, c.get(Calendar.YEAR));
+        assertEquals(Calendar.MAY, c.get(Calendar.MONTH));
+        assertEquals(25, c.get(Calendar.DAY_OF_MONTH));
+    }
+
+    public void testCustomCalendarWithAnnotation() throws Exception
+    {
+        CalendarAsStringBean cbean = MAPPER.readValue("{\"cal\":\";2007/07/13;\"}",
+                CalendarAsStringBean.class);
+        assertNotNull(cbean);
+        assertNotNull(cbean.cal);
+        Calendar c = cbean.cal;
+        assertEquals(2007, c.get(Calendar.YEAR));
+        assertEquals(Calendar.JULY, c.get(Calendar.MONTH));
+        assertEquals(13, c.get(Calendar.DAY_OF_MONTH));
+    }
+
+    public void testCustomCalendarWithTimeZone() throws Exception
+    {
+        // And then with different TimeZone: CET is +01:00 from GMT -- read as CET
+        DateInCETBean cet = MAPPER.readValue("{\"date\":\"2001-01-01,10\"}",
+                DateInCETBean.class);
+        Calendar c = Calendar.getInstance(getUTCTimeZone());
+        c.setTimeInMillis(cet.date.getTime());
+        // so, going to UTC/GMT should reduce hour by one
+        assertEquals(2001, c.get(Calendar.YEAR));
+        assertEquals(Calendar.JANUARY, c.get(Calendar.MONTH));
+        assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
+        assertEquals(9, c.get(Calendar.HOUR_OF_DAY));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests to verify failing cases
+    /**********************************************************
+     */
+
+    public void testInvalidFormat() throws Exception
+    {
+        try {
+            MAPPER.readValue(quote("foobar"), Date.class);
+            fail("Should have failed with an exception");
+        } catch (InvalidFormatException e) {
+            verifyException(e, "Can not construct instance");
+            assertEquals("foobar", e.getValue());
+            assertEquals(Date.class, e.getTargetType());
+        } catch (Exception e) {
+            fail("Wrong type of exception ("+e.getClass().getName()+"), should get "
+                    +InvalidFormatException.class.getName());
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private String dateToString(java.util.Date value)
+    {
+        /* Then from String. This is bit tricky, since JDK does not really
+         * suggest a 'standard' format. So let's try using something...
+         */
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+        return df.format(value);
+    }
+
+    private static Calendar gmtCalendar(long time)
+    {
+        Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+        c.setTimeInMillis(time);
+        return c;
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
new file mode 100644
index 0000000..ae1e5cd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestEnumDeserialization.java
@@ -0,0 +1,335 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+ at SuppressWarnings("serial")
+public class TestEnumDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes, enums
+    /**********************************************************
+     */
+
+    enum TestEnum { JACKSON, RULES, OK; }
+
+    /**
+     * Alternative version that annotates which deserializer to use
+     */
+    @JsonDeserialize(using=DummySerializer.class)
+    enum AnnotatedTestEnum {
+        JACKSON, RULES, OK;
+    }
+
+    public static class DummySerializer extends StdDeserializer<Object>
+    {
+        public DummySerializer() { super(Object.class); }
+        @Override
+        public Object deserialize(JsonParser jp, DeserializationContext ctxt)
+        {
+            return AnnotatedTestEnum.OK;
+        }
+    }
+
+    protected enum EnumWithCreator {
+        A, B;
+
+        @JsonCreator
+        public static EnumWithCreator fromEnum(String str) {
+            if ("enumA".equals(str)) return A;
+            if ("enumB".equals(str)) return B;
+            return null;
+        }
+    }
+    
+    protected enum LowerCaseEnum {
+        A, B, C;
+        private LowerCaseEnum() { }
+        @Override
+        public String toString() { return name().toLowerCase(); }
+    }
+
+    // for [JACKSON-749]
+    protected enum EnumWithJsonValue {
+        A("foo"), B("bar");
+        private final String name;
+        private EnumWithJsonValue(String n) {
+            name = n;
+        }
+        @JsonValue
+        @Override
+        public String toString() { return name; }
+    }
+    
+    // [JACKSON-810]
+    static class ClassWithEnumMapKey {
+    	@JsonProperty Map<TestEnum, String> map;
+    }
+
+    // [JACKSON-834]
+    protected enum TestEnumFor834
+    {
+        ENUM_A(1), ENUM_B(2), ENUM_C(3);
+        
+        private final int id;
+        
+        private TestEnumFor834(int id) {
+            this.id = id;
+        }
+        
+        @JsonCreator public static TestEnumFor834 fromId(int id) {
+            for (TestEnumFor834 e: values()) {
+                if (e.id == id) return e;
+            }
+            return null;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    protected final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimple() throws Exception
+    {
+        // First "good" case with Strings
+        String JSON = "\"OK\" \"RULES\"  null";
+        // multiple main-level mappings, need explicit parser:
+        JsonParser jp = MAPPER.getFactory().createParser(JSON);
+
+        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
+         */
+        assertNull(MAPPER.readValue(jp, TestEnum.class));
+
+        // and no more content beyond that...
+        assertFalse(jp.hasCurrentToken());
+
+        /* Then alternative with index (0 means first entry)
+         */
+        assertEquals(TestEnum.JACKSON, MAPPER.readValue(" 0 ", TestEnum.class));
+
+        /* Then error case: unrecognized value
+         */
+        try {
+            /*Object result =*/ MAPPER.readValue("\"NO-SUCH-VALUE\"", TestEnum.class);
+            fail("Expected an exception for bogus enum value...");
+        } catch (JsonMappingException jex) {
+            verifyException(jex, "value not one of declared");
+        }
+    }
+
+    /**
+     * Enums are considered complex if they have code (and hence sub-classes)... an
+     * example is TimeUnit
+     */
+    public void testComplexEnum() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(TimeUnit.SECONDS);
+        assertEquals(quote("SECONDS"), json);
+        TimeUnit result = MAPPER.readValue(json, TimeUnit.class);
+        assertSame(TimeUnit.SECONDS, result);
+    }
+    
+    /**
+     * Testing to see that annotation override works
+     */
+    public void testAnnotated() throws Exception
+    {
+        AnnotatedTestEnum e = MAPPER.readValue("\"JACKSON\"", AnnotatedTestEnum.class);
+        /* dummy deser always returns value OK, independent of input;
+         * only works if annotation is used
+         */
+        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));
+    }
+    
+    // Test [JACKSON-214]
+    public void testSubclassedEnums() throws Exception
+    {
+        EnumWithSubClass value = MAPPER.readValue("\"A\"", EnumWithSubClass.class);
+        assertEquals(EnumWithSubClass.A, value);
+    }
+
+    // [JACKSON-193]
+    public void testCreatorEnums() throws Exception
+    {
+        EnumWithCreator value = MAPPER.readValue("\"enumA\"", EnumWithCreator.class);
+        assertEquals(EnumWithCreator.A, value);
+    }
+    
+    // [JACKSON-212]
+    public void testToStringEnums() throws Exception
+    {
+        // can't reuse global one due to reconfig
+        ObjectMapper m = new ObjectMapper();
+        m.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
+        LowerCaseEnum value = m.readValue("\"c\"", LowerCaseEnum.class);
+        assertEquals(LowerCaseEnum.C, value);
+    }
+
+    // [JACKSON-212]
+    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));
+    }
+
+    // [JACKSON-412], disallow use of numbers
+    public void testNumbersToEnums() throws Exception
+    {
+        // by default numbers are fine:
+        assertFalse(MAPPER.getDeserializationConfig().isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS));
+        TestEnum value = MAPPER.readValue("1", TestEnum.class);
+        assertSame(TestEnum.RULES, value);
+
+        // but can also be changed to errors:
+        ObjectMapper m = new ObjectMapper();
+        m.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
+        try {
+            value = m.readValue("1", TestEnum.class);
+            fail("Expected an error");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Not allowed to deserialize Enum value out of JSON number");
+        }
+    }
+
+    // [JACKSON-684], enums using index
+    public void testEnumsWithIndex() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
+        String json = m.writeValueAsString(TestEnum.RULES);
+        assertEquals(String.valueOf(TestEnum.RULES.ordinal()), json);
+        TestEnum result = m.readValue(json, TestEnum.class);
+        assertSame(TestEnum.RULES, result);
+    }
+    
+    // [JACKSON-749]: @JsonValue should be considered as well
+    public void testEnumsWithJsonValue() throws Exception
+    {
+        // first, enum as is
+        EnumWithJsonValue e = MAPPER.readValue(quote("foo"), EnumWithJsonValue.class);
+        assertSame(EnumWithJsonValue.A, e);
+        e = MAPPER.readValue(quote("bar"), EnumWithJsonValue.class);
+        assertSame(EnumWithJsonValue.B, e);
+
+        // then in EnumSet
+        EnumSet<EnumWithJsonValue> set = MAPPER.readValue("[\"bar\"]",
+                new TypeReference<EnumSet<EnumWithJsonValue>>() { });
+        assertNotNull(set);
+        assertEquals(1, set.size());
+        assertTrue(set.contains(EnumWithJsonValue.B));
+        assertFalse(set.contains(EnumWithJsonValue.A));
+
+        // and finally EnumMap
+        EnumMap<EnumWithJsonValue,Integer> map = MAPPER.readValue("{\"foo\":13}",
+                new TypeReference<EnumMap<EnumWithJsonValue, Integer>>() { });
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        assertEquals(Integer.valueOf(13), map.get(EnumWithJsonValue.A));
+    }
+
+    // [JACKSON-756], next three tests
+
+    public void testEnumWithCreatorEnumMaps() throws Exception {
+          EnumMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
+                  new TypeReference<EnumMap<EnumWithCreator,String>>() {});
+          assertEquals("value", value.get(EnumWithCreator.A));
+    }
+
+    public void testEnumWithCreatorMaps() throws Exception {
+          java.util.HashMap<EnumWithCreator,String> value = MAPPER.readValue("{\"enumA\":\"value\"}",
+                  new TypeReference<java.util.HashMap<EnumWithCreator,String>>() {});
+          assertEquals("value", value.get(EnumWithCreator.A));
+    }
+
+    public void testEnumWithCreatorEnumSets() throws Exception {
+          EnumSet<EnumWithCreator> value = MAPPER.readValue("[\"enumA\"]",
+                  new TypeReference<EnumSet<EnumWithCreator>>() {});
+          assertTrue(value.contains(EnumWithCreator.A));
+    }
+
+    // [JACKSON-810], ability to ignore unknown Enum values:
+
+    public void testAllowUnknownEnumValuesReadAsNull() throws Exception
+    {
+        // can not use shared mapper when changing configs...
+        ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
+        assertNull(reader.withType(TestEnum.class).readValue("\"NO-SUCH-VALUE\""));
+        assertNull(reader.withType(TestEnum.class).readValue(" 4343 "));
+    }
+
+    public void testAllowUnknownEnumValuesForEnumSets() throws Exception
+    {
+        ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
+        EnumSet<TestEnum> result = reader.withType(new TypeReference<EnumSet<TestEnum>>() { })
+                .readValue("[\"NO-SUCH-VALUE\"]");
+        assertEquals(0, result.size());
+    }
+    
+    public void testAllowUnknownEnumValuesAsMapKeysReadAsNull() throws Exception
+    {
+        ObjectReader reader = MAPPER.reader(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
+        ClassWithEnumMapKey result = reader.withType(ClassWithEnumMapKey.class)
+                .readValue("{\"map\":{\"NO-SUCH-VALUE\":\"val\"}}");
+        assertTrue(result.map.containsKey(null));
+    }
+    
+    public void testDoNotAllowUnknownEnumValuesAsMapKeysWhenReadAsNullDisabled() throws Exception
+    {
+        assertFalse(MAPPER.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL));
+         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 construct Map key");
+         }
+    }
+
+    // [JACKSON-834]
+    public void testEnumsFromInts() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        TestEnumFor834 res = mapper.readValue("1 ", TestEnumFor834.class);
+        assertSame(TestEnumFor834.ENUM_A, res);
+    }
+
+    // [Issue#141]: allow mapping of empty String into null
+    public void testEnumsWithEmpty() throws Exception
+    {
+       final ObjectMapper m = new ObjectMapper();
+       m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+       TestEnum result = m.readValue("\"\"", TestEnum.class);
+       assertNull(result);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java
new file mode 100644
index 0000000..391ee83
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionDeserialization.java
@@ -0,0 +1,100 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that simple exceptions can be deserialized.
+ */
+public class TestExceptionDeserialization
+    extends BaseMapTest
+{
+    @SuppressWarnings("serial")
+    static class MyException extends Exception
+    {
+        protected int value;
+
+        protected String myMessage;
+        protected HashMap<String,Object> stuff = new HashMap<String, Object>();
+        
+        @JsonCreator
+        MyException(@JsonProperty("message") String msg, @JsonProperty("value") int v)
+        {
+            super(msg);
+            myMessage = msg;
+            value = v;
+        }
+
+        public int getValue() { return value; }
+        
+        public String getFoo() { return "bar"; }
+
+        @JsonAnySetter public void setter(String key, Object value)
+        {
+            stuff.put(key, value);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class MyNoArgException extends Exception
+    {
+        @JsonCreator MyNoArgException() { }
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testIOException() throws IOException
+    {
+        IOException ioe = new IOException("TEST");
+        String json = MAPPER.writeValueAsString(ioe);
+        IOException result = MAPPER.readValue(json, IOException.class);
+        assertEquals(ioe.getMessage(), result.getMessage());
+    }
+
+    // As per [JACKSON-377]
+    public void testWithCreator() throws IOException
+    {
+        final String MSG = "the message";
+        String json = MAPPER.writeValueAsString(new MyException(MSG, 3));
+
+        MyException result = MAPPER.readValue(json, MyException.class);
+        assertEquals(MSG, result.getMessage());
+        assertEquals(3, result.value);
+        assertEquals(1, result.stuff.size());
+        assertEquals(result.getFoo(), result.stuff.get("foo"));
+    }
+
+    // [JACKSON-388]
+    public void testWithNullMessage() throws IOException
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        String json = mapper.writeValueAsString(new IOException((String) null));
+        IOException result = mapper.readValue(json, IOException.class);
+        assertNotNull(result);
+        assertNull(result.getMessage());
+    }
+
+    public void testNoArgsException() throws IOException
+    {
+        MyNoArgException exc = MAPPER.readValue("{}", MyNoArgException.class);
+        assertNotNull(exc);
+    }
+
+    // [JACKSON-794]: try simulating JDK 7 behavior
+    public void testJDK7SuppressionProperty() throws IOException
+    {
+        Exception exc = MAPPER.readValue("{\"suppressed\":[]}", IOException.class);
+        assertNotNull(exc);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java
new file mode 100644
index 0000000..6e902ee
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestExceptionHandling.java
@@ -0,0 +1,125 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
+import com.fasterxml.jackson.test.BrokenStringReader;
+
+/**
+ * Unit test for verifying that exceptions are properly handled (caught,
+ * re-thrown or wrapped, depending)
+ * with Object deserialization.
+ */
+public class TestExceptionHandling
+    extends BaseMapTest
+{
+    static class Bean {
+        public String propX;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    /**
+     * Verification of [JACKSON-301]
+     */
+    public void testHandlingOfUnrecognized() throws Exception
+    {
+        UnrecognizedPropertyException exc = null;
+        try {
+            new ObjectMapper().readValue("{\"bar\":3}", Bean.class);
+        } catch (UnrecognizedPropertyException e) {
+            exc = e;
+        }
+        if (exc == null) {
+            fail("Should have failed binding");
+        }
+        assertEquals("bar", exc.getUnrecognizedPropertyName());
+        assertEquals(Bean.class, exc.getReferringClass());
+        // also: should get list of known properties
+        verifyException(exc, "propX");
+    }
+
+    /**
+     * Simple test to check behavior when end-of-stream is encountered
+     * without content. Used to expect EOFException (Jackson 1.x); but
+     * nowadays ought to be JsonMappingException
+     */
+    public void testExceptionWithEmpty() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            Object result = mapper.readValue("    ", Object.class);
+            fail("Expected an exception, but got result value: "+result);
+        } catch (Exception e) {
+            verifyException(e, JsonMappingException.class, "No content");
+        }
+    }
+
+    public void testExceptionWithIncomplete()
+        throws Exception
+    {
+        BrokenStringReader r = new BrokenStringReader("[ 1, ", "TEST");
+        JsonFactory f = new JsonFactory();
+        JsonParser jp = f.createParser(r);
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            @SuppressWarnings("unused")
+            Object ob = mapper.readValue(jp, Object.class);
+            fail("Should have gotten an exception");
+        } catch (IOException e) {
+            /* For "bona fide" IO problems (due to low-level problem,
+             * thrown by reader/stream), IOException must be thrown
+             */
+            verifyException(e, IOException.class, "TEST");
+        }
+    }
+
+    public void testExceptionWithEOF()
+        throws Exception
+    {
+        StringReader r = new StringReader("  3");
+        JsonFactory f = new JsonFactory();
+        JsonParser jp = f.createParser(r);
+        ObjectMapper mapper = new ObjectMapper();
+
+        Integer I = mapper.readValue(jp, Integer.class);
+        assertEquals(3, I.intValue());
+
+        // and then end-of-input...
+        try {
+            I = mapper.readValue(jp, Integer.class);
+            fail("Should have gotten an exception");
+        } catch (IOException e) {
+            verifyException(e, JsonMappingException.class, "No content");
+        }
+        // also: should have no current token after end-of-input
+        JsonToken t = jp.getCurrentToken();
+        if (t != null) {
+            fail("Expected current token to be null after end-of-stream, was: "+t);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    void verifyException(Exception e, Class<?> expType, String expMsg)
+        throws Exception
+    {
+        if (e.getClass() != expType) {
+            fail("Expected exception of type "+expType.getName()+", got "+e.getClass().getName());
+        }
+        if (expMsg != null) {
+            verifyException(e, expMsg);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestFieldDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestFieldDeserialization.java
new file mode 100644
index 0000000..b8b59d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestFieldDeserialization.java
@@ -0,0 +1,162 @@
+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.annotation.*;
+
+/**
+ * Unit tests for verifying that field-backed properties can also be
+ * deserialized (since version 1.1) as well as
+ * setter-accessible properties.
+ */
+public class TestFieldDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    static class SimpleFieldBean
+    {
+        public int x, y;
+
+        // not auto-detectable, not public
+        int z;
+
+        // ignored, not detectable either
+        @JsonIgnore public int a;
+    }
+
+    static class SimpleFieldBean2
+    {
+        @JsonDeserialize String[] values;
+    }
+
+    @JsonAutoDetect(fieldVisibility=Visibility.NONE)
+    static class NoAutoDetectBean
+    {
+        // not auto-detectable any more
+        public int z;
+
+        @JsonProperty("z")
+        public int _z;
+    }
+
+    // Let's test invalid bean too
+    static class DupFieldBean
+    {
+        public int z;
+
+        @JsonProperty("z")
+        public int _z;
+    }
+
+    public static class DupFieldBean2
+    {
+        @JsonProperty("foo")
+        public int _z;
+
+        @JsonDeserialize
+        private int foo;
+    }
+
+    public static class OkDupFieldBean
+        extends SimpleFieldBean
+    {
+        @JsonProperty("x")
+        protected int myX = 10;
+
+        public int y = 11;
+    }
+    
+    abstract static class Abstract { }
+    static class Concrete extends Abstract
+    {
+        String value;
+
+        public Concrete(String v) { value = v; }
+    }
+
+    static class AbstractWrapper {
+        @JsonDeserialize(as=Concrete.class)
+        public Abstract value;
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+
+    public void testSimpleAutoDetect() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        SimpleFieldBean result = m.readValue("{ \"x\" : -13 }",
+                                           SimpleFieldBean.class);
+        assertEquals(-13, result.x);
+        assertEquals(0, result.y);
+    }
+
+    public void testSimpleAnnotation() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        SimpleFieldBean2 bean = m.readValue("{ \"values\" : [ \"x\", \"y\" ] }",
+                SimpleFieldBean2.class);
+        String[] values = bean.values;
+        assertNotNull(values);
+        assertEquals(2, values.length);
+        assertEquals("x", values[0]);
+        assertEquals("y", values[1]);
+    }
+
+    public void testNoAutoDetect() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        NoAutoDetectBean bean = m.readValue("{ \"z\" : 7 }",
+                                            NoAutoDetectBean.class);
+        assertEquals(7, bean._z);
+    }
+
+    public void testTypeAnnotation() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        AbstractWrapper w = m.readValue("{ \"value\" : \"abc\" }",
+                                        AbstractWrapper.class);
+        Abstract bean = w.value;
+        assertNotNull(bean);
+        assertEquals(Concrete.class, bean.getClass());
+        assertEquals("abc", ((Concrete)bean).value);
+    }
+
+    public void testFailureDueToDups() throws Exception
+    {
+        try {
+            writeAndMap(new ObjectMapper(), new DupFieldBean());
+        } catch (JsonMappingException e) {
+            verifyException(e, "Multiple fields representing property");
+        }
+    }
+
+    public void testFailureDueToDups2() throws Exception
+    {
+        try {
+            writeAndMap(new ObjectMapper(), new DupFieldBean2());
+        } catch (JsonMappingException e) {
+            verifyException(e, "Multiple fields representing property");
+        }
+    }
+
+    // For [JACKSON-226], acceptable field overrides
+    public void testOkFieldOverride() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        OkDupFieldBean result = m.readValue("{ \"x\" : 1, \"y\" : 2 }",
+                OkDupFieldBean.class);
+        assertEquals(1, result.myX);
+        assertEquals(2, result.y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java
new file mode 100644
index 0000000..f99bbb2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericCollectionDeser.java
@@ -0,0 +1,82 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+ at SuppressWarnings("serial")
+public class TestGenericCollectionDeser
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test classes, enums
+    /**********************************************************
+     */
+
+    static class ListSubClass extends ArrayList<StringWrapper> { }
+
+    /**
+     * Map class that should behave like {@link ListSubClass}, but by
+     * using annotations.
+     */
+    @JsonDeserialize(contentAs=StringWrapper.class)
+    static class AnnotatedStringList extends ArrayList<Object> { }
+
+    @JsonDeserialize(contentAs=BooleanWrapper.class)
+    static class AnnotatedBooleanList extends ArrayList<Object> { }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    /*
+    /**********************************************************
+    /* Tests for sub-classing
+    /**********************************************************
+     */
+
+    /**
+     * Verifying that sub-classing works ok wrt generics information
+     */
+    public void testListSubClass() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ListSubClass result = mapper.readValue("[ \"123\" ]", ListSubClass.class);
+        assertEquals(1, result.size());
+        Object value = result.get(0);
+        assertEquals(StringWrapper.class, value.getClass());
+        StringWrapper bw = (StringWrapper) value;
+        assertEquals("123", bw.str);
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for annotations
+    /**********************************************************
+     */
+
+    // Verifying that sub-classing works ok wrt generics information
+    public void testAnnotatedLStringist() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotatedStringList result = mapper.readValue("[ \"...\" ]", AnnotatedStringList.class);
+        assertEquals(1, result.size());
+        Object ob = result.get(0);
+        assertEquals(StringWrapper.class, ob.getClass());
+        assertEquals("...", ((StringWrapper) ob).str);
+    }
+
+    public void testAnnotatedBooleanList() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        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);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericMapDeser.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericMapDeser.java
new file mode 100644
index 0000000..e18dd25
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericMapDeser.java
@@ -0,0 +1,170 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+ at SuppressWarnings("serial")
+public class TestGenericMapDeser
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test classes, enums
+    /**********************************************************
+     */
+
+    static class BooleanWrapper {
+        final Boolean b;
+        @JsonCreator BooleanWrapper(Boolean value) { b = value; }
+    }
+
+    static class StringWrapper {
+        final String str;
+        @JsonCreator StringWrapper(String value) {
+            str = value;
+        }
+    }
+
+    static class MapSubClass extends HashMap<String,BooleanWrapper> { }
+
+    /**
+     * Map class that should behave like {@link MapSubClass}, but by
+     * using annotations.
+     */
+    @JsonDeserialize(keyAs=StringWrapper.class, contentAs=BooleanWrapper.class)
+        static class AnnotatedMap extends HashMap<Object,Object> { }
+
+    interface MapWrapper<K,V> extends java.io.Serializable {
+        public abstract Map<K,V> getEntries();
+    }
+
+    static class StringMap implements MapWrapper<String,Long>
+    {
+        private Map<String,Long> entries = new LinkedHashMap<String,Long>();
+
+        public StringMap() { }
+
+        @Override
+        public Map<String,Long> getEntries() { return entries; }
+    }
+
+    static class StringWrapperValueMap<KEY> extends HashMap<KEY,StringWrapper> { }
+
+    static class StringStringWrapperMap extends StringWrapperValueMap<String> { }
+
+    static class KeyTypeCtor  {
+        protected String value;
+        public KeyTypeCtor(String v) { value = v; }
+    }
+
+    static class KeyTypeFactory  {
+        protected String value;
+        private KeyTypeFactory(String v, boolean foo) { value = v; }
+
+        @JsonCreator
+        public static KeyTypeFactory create(String str) {
+            return new KeyTypeFactory(str, true);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods for sub-classing
+    /**********************************************************
+     */
+
+    /**
+     * Verifying that sub-classing works ok wrt generics information
+     */
+    public void testMapSubClass() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        MapSubClass result = mapper.readValue
+            ("{\"a\":true }", MapSubClass.class);
+        assertEquals(1, result.size());
+        Object value = result.get("a");
+        assertEquals(BooleanWrapper.class, value.getClass());
+        BooleanWrapper bw = (BooleanWrapper) value;
+        assertEquals(Boolean.TRUE, bw.b);
+    }
+
+    public void testMapWrapper() throws Exception
+    {
+        StringMap value = new ObjectMapper().readValue
+            ("{\"entries\":{\"a\":9} }", StringMap.class);
+        assertNotNull(value.getEntries());
+        assertEquals(1, value.getEntries().size());
+        assertEquals(Long.valueOf(9), value.getEntries().get("a"));
+    }
+
+    public void testIntermediateTypes() throws Exception
+    {
+        StringStringWrapperMap result = new ObjectMapper().readValue
+            ("{\"a\":\"b\"}", StringStringWrapperMap.class);
+        assertEquals(1, result.size());
+        Object value = result.get("a");
+        assertNotNull(value);
+        assertEquals(value.getClass(), StringWrapper.class);
+        assertEquals("b", ((StringWrapper) value).str);
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods for sub-classing for annotation handling
+    /**********************************************************
+     */
+
+    /**
+     * Verifying that sub-classing works ok wrt generics information
+     */
+    public void testAnnotatedMap() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AnnotatedMap result = mapper.readValue
+            ("{\"a\":true }", AnnotatedMap.class);
+        assertEquals(1, result.size());
+        Map.Entry<Object,Object> en = result.entrySet().iterator().next();
+        assertEquals(StringWrapper.class, en.getKey().getClass());
+        assertEquals(BooleanWrapper.class, en.getValue().getClass());
+        assertEquals("a", ((StringWrapper) en.getKey()).str);
+        assertEquals(Boolean.TRUE, ((BooleanWrapper) en.getValue()).b);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods for ensuring @JsonCreator works for keys
+    /**********************************************************
+     */
+
+    public void testKeyViaCtor() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<KeyTypeCtor,Integer> map = mapper.readValue("{\"a\":123}",
+                TypeFactory.defaultInstance().constructMapType(HashMap.class, KeyTypeCtor.class, Integer.class));
+        assertEquals(1, map.size());
+        Map.Entry<?,?> entry = map.entrySet().iterator().next();
+        assertEquals(Integer.valueOf(123), entry.getValue());
+        Object key = entry.getKey();
+        assertEquals(KeyTypeCtor.class, key.getClass());
+        assertEquals("a", ((KeyTypeCtor) key).value);
+    }
+
+    public void testKeyViaFactory() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<KeyTypeCtor,Integer> map = mapper.readValue("{\"a\":123}",
+                TypeFactory.defaultInstance().constructMapType(HashMap.class, KeyTypeFactory.class, Integer.class));
+        assertEquals(1, map.size());
+        Map.Entry<?,?> entry = map.entrySet().iterator().next();
+        assertEquals(Integer.valueOf(123), entry.getValue());
+        Object key = entry.getKey();
+        assertEquals(KeyTypeFactory.class, key.getClass());
+        assertEquals("a", ((KeyTypeFactory) key).value);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericNumber.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericNumber.java
new file mode 100644
index 0000000..0e90ac4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericNumber.java
@@ -0,0 +1,115 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying handling of non-specific numeric types.
+ */
+public class TestGenericNumber
+    extends BaseMapTest
+{
+    public void testIntAsNumber() throws Exception
+    {
+        /* Even if declared as 'generic' type, should return using most
+         * efficient type... here, Integer
+         */
+        Number result = new ObjectMapper().readValue(new StringReader(" 123 "), Number.class);
+        assertEquals(Integer.valueOf(123), result);
+    }
+
+    public void testLongAsNumber() throws Exception
+    {
+        // And beyond int range, should get long
+        long exp = 1234567890123L;
+        Number result = new ObjectMapper().readValue(String.valueOf(exp), Number.class);
+        assertEquals(Long.valueOf(exp), result);
+    }
+
+    public void testBigIntAsNumber() throws Exception
+    {
+        // and after long, BigInteger
+        BigInteger biggie = new BigInteger("1234567890123456789012345678901234567890");
+        Number result = new ObjectMapper().readValue(biggie.toString(), Number.class);
+        assertEquals(BigInteger.class, biggie.getClass());
+        assertEquals(biggie, result);
+    }
+
+    public void testIntTypeOverride() throws Exception
+    {
+        /* Slight twist; as per [JACKSON-100], can also request binding
+         * to BigInteger even if value would fit in Integer
+         */
+        ObjectMapper m = new ObjectMapper();
+        m.enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
+        BigInteger exp = BigInteger.valueOf(123L);
+
+        // first test as any Number
+        Number result = m.readValue(new StringReader(" 123 "), Number.class);
+        assertEquals(BigInteger.class, result.getClass());
+        assertEquals(exp, result);
+
+        // then as any Object
+        /*Object value =*/ m.readValue(new StringReader("123"), Object.class);
+        assertEquals(BigInteger.class, result.getClass());
+        assertEquals(exp, result);
+    }
+
+    /**
+     * Related to [JACKSON-72]: by default should wrap floating-point
+     * Number as Double
+     */
+    public void testDoubleAsNumber() throws Exception
+    {
+        Number result = new ObjectMapper().readValue(new StringReader(" 1.0 "), Number.class);
+        assertEquals(Double.valueOf(1.0), result);
+    }
+
+    /**
+     * Test for verifying [JACKSON-72].
+     */
+    public void testFpTypeOverrideSimple() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+        BigDecimal dec = new BigDecimal("0.1");
+
+        // First test generic stand-alone Number
+        Number result = m.readValue(dec.toString(), Number.class);
+        assertEquals(BigDecimal.class, result.getClass());
+        assertEquals(dec, result);
+
+        // Then plain old Object
+        Object value = m.readValue(dec.toString(), Object.class);
+        assertEquals(BigDecimal.class, result.getClass());
+        assertEquals(dec, value);
+    }
+
+	public void testFpTypeOverrideStructured() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        BigDecimal dec = new BigDecimal("-19.37");
+
+        m.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
+
+        // List element types
+        @SuppressWarnings("unchecked")
+        List<Object> list = (List<Object>)m.readValue("[ "+dec.toString()+" ]", List.class);
+        assertEquals(1, list.size());
+        Object val = list.get(0);
+        assertEquals(BigDecimal.class, val.getClass());
+        assertEquals(dec, val);
+
+        // and a map
+        Map<?,?> map = m.readValue("{ \"a\" : "+dec.toString()+" }", Map.class);
+        assertEquals(1, map.size());
+        val = map.get("a");
+        assertEquals(BigDecimal.class, val.getClass());
+        assertEquals(dec, val);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java
new file mode 100644
index 0000000..b84b12e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenerics.java
@@ -0,0 +1,124 @@
+package com.fasterxml.jackson.databind.deser;
+
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+public class TestGenerics
+    extends BaseMapTest
+{
+    static abstract class BaseNumberBean<T extends Number>
+    {
+        public abstract void setNumber(T value);
+    }
+
+    static class NumberBean
+        extends BaseNumberBean<Long>
+    {
+        long _number;
+
+        @Override
+        public void setNumber(Long value)
+        {
+            _number = value.intValue();
+        }
+    }
+
+    /**
+     * Very simple bean class
+     */
+    static class SimpleBean
+    {
+        public int x;
+    }
+
+    static class Wrapper<T>
+    {
+        public T value;
+
+        public Wrapper() { }
+
+        public Wrapper(T v) { value = v; }
+
+        @Override
+        public boolean equals(Object o) {
+            return (o instanceof Wrapper<?>) && (((Wrapper<?>) o).value.equals(value));
+        }
+    }
+
+    /*
+    /***************************************************
+    /* Test cases
+    /***************************************************
+     */
+
+    public void testSimpleNumberBean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        NumberBean result = mapper.readValue("{\"number\":17}", NumberBean.class);
+        assertEquals(17, result._number);
+    }
+
+    /**
+     * Unit test for verifying fix to [JACKSON-109].
+     */
+    public void testGenericWrapper() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Wrapper<SimpleBean> result = mapper.readValue
+            ("{\"value\": { \"x\" : 13 } }",
+             new TypeReference<Wrapper<SimpleBean>>() { });
+        assertNotNull(result);
+        assertEquals(Wrapper.class, result.getClass());
+        Object contents = result.value;
+        assertNotNull(contents);
+        assertEquals(SimpleBean.class, contents.getClass());
+        SimpleBean bean = (SimpleBean) contents;
+        assertEquals(13, bean.x);
+    }
+
+    /**
+     * Unit test for verifying that we can use different
+     * type bindings for individual generic types;
+     * problem with [JACKSON-190]
+     */
+    public void testMultipleWrappers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+
+        // First, numeric wrapper
+        Wrapper<Boolean> result = mapper.readValue
+            ("{\"value\": true}", new TypeReference<Wrapper<Boolean>>() { });
+        assertEquals(new Wrapper<Boolean>(Boolean.TRUE), result);
+
+        // Then string one
+        Wrapper<String> result2 = mapper.readValue
+            ("{\"value\": \"abc\"}", new TypeReference<Wrapper<String>>() { });
+        assertEquals(new Wrapper<String>("abc"), result2);
+
+        // And then number
+        Wrapper<Long> result3 = mapper.readValue
+            ("{\"value\": 7}", new TypeReference<Wrapper<Long>>() { });
+        assertEquals(new Wrapper<Long>(7L), result3);
+    }
+
+    /**
+     * Unit test for verifying fix to [JACKSON-109].
+     */
+    public void testArrayOfGenericWrappers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Wrapper<SimpleBean>[] result = mapper.readValue
+            ("[ {\"value\": { \"x\" : 9 } } ]",
+             new TypeReference<Wrapper<SimpleBean>[]>() { });
+        assertNotNull(result);
+        assertEquals(Wrapper[].class, result.getClass());
+        assertEquals(1, result.length);
+        Wrapper<SimpleBean> elem = result[0];
+        Object contents = elem.value;
+        assertNotNull(contents);
+        assertEquals(SimpleBean.class, contents.getClass());
+        SimpleBean bean = (SimpleBean) contents;
+        assertEquals(9, bean.x);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericsBounded.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericsBounded.java
new file mode 100644
index 0000000..a258b42
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestGenericsBounded.java
@@ -0,0 +1,120 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+import java.io.Serializable;
+
+public class TestGenericsBounded
+    extends BaseMapTest
+{
+    @SuppressWarnings("serial")
+    static class Range<E extends Comparable<E>> implements Serializable
+    {
+         protected E start, end;
+
+         public Range(){ }
+         public Range(E start, E end) {
+             this.start = start;
+             this.end = end;
+         }
+
+         public E getEnd() { return end; }
+         public void setEnd(E e) { end = e; }
+
+         public E getStart() { return start; }
+         public void setStart(E s) {
+             start = s;
+         }
+    }
+
+    @SuppressWarnings("serial")
+    static class DoubleRange extends Range<Double> {
+        public DoubleRange() { }
+        public DoubleRange(Double s, Double e) { super(s, e); }
+    }
+     
+    static class BoundedWrapper<A extends Serializable>
+    {
+        public List<A> values;
+    }
+
+    @SuppressWarnings("serial")
+    static class IntBean implements Serializable
+    {
+        public int x;
+    }
+
+    static class IntBeanWrapper<T extends IntBean> {
+        public T wrapped;
+    }
+
+    // Types for [JACKSON-778]
+    
+    static class Document {}
+    static class Row {}
+    static class RowWithDoc<D extends Document> extends Row {
+        @JsonProperty("d") D d;
+    }
+    static class ResultSet<R extends Row> {
+        @JsonProperty("rows") List<R> rows;
+    }
+    static class ResultSetWithDoc<D extends Document> extends ResultSet<RowWithDoc<D>> {}
+
+    static class MyDoc extends Document {}    
+    /*
+    /*******************************************************
+    /* Unit tests
+    /*******************************************************
+     */
+
+    public void testLowerBound() throws Exception
+    {
+        IntBeanWrapper<?> result = new ObjectMapper().readValue("{\"wrapped\":{\"x\":3}}",
+                IntBeanWrapper.class);
+        assertNotNull(result);
+        assertEquals(IntBean.class, result.wrapped.getClass());
+        assertEquals(3, result.wrapped.x);
+    }
+    
+    // Test related to type bound handling problem within [JACKSON-190]
+    public void testBounded() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        BoundedWrapper<IntBean> result = mapper.readValue
+            ("{\"values\":[ {\"x\":3} ] } ", new TypeReference<BoundedWrapper<IntBean>>() {});
+        List<?> list = result.values;
+        assertEquals(1, list.size());
+        Object ob = list.get(0);
+        assertEquals(IntBean.class, ob.getClass());
+        assertEquals(3, result.values.get(0).x);
+    }
+
+    public void testGenericsComplex() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        DoubleRange in = new DoubleRange(-0.5, 0.5);
+        String json = m.writeValueAsString(in);
+        DoubleRange out = m.readValue(json, DoubleRange.class);
+        assertNotNull(out);
+        assertEquals(-0.5, out.start);
+        assertEquals(0.5, out.end);
+    }
+
+    public void testIssue778() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        String json = "{\"rows\":[{\"d\":{}}]}";
+
+        final TypeReference<?> type = new TypeReference<ResultSetWithDoc<MyDoc>>() {};
+        
+        // type passed is correct, but somehow it gets mangled when passed...
+        ResultSetWithDoc<MyDoc> rs = mapper.readValue(json, type);
+        Document d = rs.rows.iterator().next().d;
+    
+        assertEquals(MyDoc.class, d.getClass()); //expected MyDoc but was Document
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
new file mode 100644
index 0000000..3668630
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestIgnoredTypes.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Test for [JACKSON-429]
+ */
+public class TestIgnoredTypes extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+    
+    @JsonIgnoreType
+    class IgnoredType { // note: non-static, can't be deserializer
+        public IgnoredType(IgnoredType src) { }
+    }
+
+    @JsonIgnoreType(false)
+    static class NonIgnoredType
+    {
+        public int value = 13;
+        public IgnoredType ignored;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testIgnoredType() throws Exception
+    {
+        // First: should be ok in general, even though couldn't build deserializer (due to non-static inner class):
+        ObjectMapper mapper = new ObjectMapper();
+        NonIgnoredType bean = mapper.readValue("{\"value\":13}", NonIgnoredType.class);
+        assertNotNull(bean);
+        assertEquals(13, bean.value);
+
+        // And also ok to see something with that value; will just get ignored
+        bean = mapper.readValue("{ \"ignored\":[1,2,{}], \"value\":9 }", NonIgnoredType.class);
+        assertNotNull(bean);
+        assertEquals(9, bean.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
new file mode 100644
index 0000000..fc5bc9c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestInjectables.java
@@ -0,0 +1,118 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestInjectables extends BaseMapTest
+{
+    static class InjectedBean
+    {
+        @JacksonInject
+        protected String stuff;
+
+        @JacksonInject("myId")
+        protected String otherStuff;
+
+        protected long third;
+        
+        public int value;
+
+        @JacksonInject
+        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 {
+        protected String name;
+        protected int age;
+        
+        public CtorBean(@JacksonInject String n, @JsonProperty("age") int a)
+        {
+            name = n;
+            age = a;
+        }
+    }
+
+    static class CtorBean2 {
+        protected String name;
+        protected Integer age;
+        
+        public CtorBean2(@JacksonInject String n, @JacksonInject("number") Integer a)
+        {
+            name = n;
+            age = a;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimple() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "stuffValue")
+            .addValue("myId", "xyz")
+            .addValue(Long.TYPE, Long.valueOf(37))
+            );
+        InjectedBean bean = mapper.readValue("{\"value\":3}", InjectedBean.class);
+        assertEquals(3, bean.value);
+        assertEquals("stuffValue", bean.stuff);
+        assertEquals("xyz", bean.otherStuff);
+        assertEquals(37L, bean.third);
+    }
+    
+    public void testWithCtors() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "Bubba")
+            );
+        CtorBean bean = mapper.readValue("{\"age\":55}", CtorBean.class);
+        assertEquals(55, bean.age);
+        assertEquals("Bubba", bean.name);
+    }
+
+    // [Issue-13]
+    public void testTwoInjectablesViaCreator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setInjectableValues(new InjectableValues.Std()
+            .addValue(String.class, "Bob")
+            .addValue("number", Integer.valueOf(13))
+            );
+        CtorBean2 bean = mapper.readValue("{ }", CtorBean2.class);
+        assertEquals(Integer.valueOf(13), bean.age);
+        assertEquals("Bob", bean.name);
+    }
+    
+    public void testInvalidDup() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        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");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java
new file mode 100644
index 0000000..4be0a63
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestInnerClass.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestInnerClass extends BaseMapTest
+{
+    // [JACKSON-594]
+    static class Dog
+    {
+      public String name;
+      public Brain brain;
+
+      public Dog() { }
+      public Dog(String n, boolean thinking) {
+          name = n;
+          brain = new Brain();
+          brain.isThinking = thinking;
+      }
+      
+      // note: non-static
+      public class Brain {
+          public boolean isThinking;
+
+          public String parentName() { return name; }
+      }
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    public void testSimpleNonStaticInner() throws Exception
+    {
+        // Let's actually verify by first serializing, then deserializing back
+        ObjectMapper mapper = new ObjectMapper();
+        Dog input = new Dog("Smurf", true);
+        String json = mapper.writeValueAsString(input);
+        Dog output = mapper.readValue(json, Dog.class);
+        assertEquals("Smurf", output.name);
+        assertNotNull(output.brain);
+        assertTrue(output.brain.isThinking);
+        // and verify correct binding...
+        assertEquals("Smurf", output.brain.parentName());
+        output.name = "Foo";
+        assertEquals("Foo", output.brain.parentName());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java
new file mode 100644
index 0000000..b0bba2d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestJacksonTypes.java
@@ -0,0 +1,109 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Unit tests for those Jackson types we want to ensure can be deserialized.
+ */
+public class TestJacksonTypes
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    public void testJsonLocation() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // note: source reference is untyped, only String guaranteed to work
+        JsonLocation loc = new JsonLocation("whatever",  -1, -1, 100, 13);
+        // Let's use serializer here; goal is round-tripping
+        String ser = serializeAsString(m, loc);
+        JsonLocation result = m.readValue(ser, JsonLocation.class);
+        assertNotNull(result);
+        assertEquals(loc.getSourceRef(), result.getSourceRef());
+        assertEquals(loc.getByteOffset(), result.getByteOffset());
+        assertEquals(loc.getCharOffset(), result.getCharOffset());
+        assertEquals(loc.getColumnNr(), result.getColumnNr());
+        assertEquals(loc.getLineNr(), result.getLineNr());
+    }
+
+    // doesn't really belong here but...
+    public void testJsonLocationProps()
+    {
+        JsonLocation loc = new JsonLocation(null,  -1, -1, 100, 13);
+        assertTrue(loc.equals(loc));
+        assertFalse(loc.equals(null));
+        assertFalse(loc.equals("abx"));
+
+        // should we check it's not 0?
+        loc.hashCode();
+    }
+
+    /**
+     * Verify that {@link TokenBuffer} can be properly deserialized
+     * automatically, using the "standard" JSON sample document
+     */
+    public void testTokenBufferWithSample() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // First, try standard sample doc:
+        TokenBuffer result = m.readValue(SAMPLE_DOC_JSON_SPEC, TokenBuffer.class);
+        verifyJsonSpecSampleDoc(result.asParser(), true);
+    }
+
+    public void testTokenBufferWithSequence() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // and then sequence of other things
+        JsonParser jp = createParserUsingReader("[ 32, [ 1 ], \"abc\", { \"a\" : true } ]");
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        TokenBuffer buf = m.readValue(jp, TokenBuffer.class);
+
+        // check manually...
+        JsonParser bufParser = buf.asParser();
+        assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken());
+        assertEquals(32, bufParser.getIntValue());
+        assertNull(bufParser.nextToken());
+
+        // then bind to another
+        buf = m.readValue(jp, TokenBuffer.class);
+        bufParser = buf.asParser();
+        assertToken(JsonToken.START_ARRAY, bufParser.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, bufParser.nextToken());
+        assertEquals(1, bufParser.getIntValue());
+        assertToken(JsonToken.END_ARRAY, bufParser.nextToken());
+        assertNull(bufParser.nextToken());
+
+        // third one, with automatic binding
+        buf = m.readValue(jp, TokenBuffer.class);
+        String str = m.readValue(buf.asParser(), String.class);
+        assertEquals("abc", str);
+
+        // and ditto for last one
+        buf = m.readValue(jp, TokenBuffer.class);
+        Map<?,?> map = m.readValue(buf.asParser(), Map.class);
+        assertEquals(1, map.size());
+        assertEquals(Boolean.TRUE, map.get("a"));
+        
+        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+    }
+
+    public void testJavaType() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // first simple type:
+        String json = mapper.writeValueAsString(tf.constructType(String.class));
+        assertEquals(quote(java.lang.String.class.getName()), json);
+        // and back
+        JavaType t = mapper.readValue(json, JavaType.class);
+        assertNotNull(t);
+        assertEquals(String.class, t.getRawClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
new file mode 100644
index 0000000..d9c442a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestJdkTypes.java
@@ -0,0 +1,354 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.net.*;
+import java.nio.charset.Charset;
+import java.util.Currency;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.databind.*;
+
+public class TestJdkTypes extends BaseMapTest
+{
+    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;
+    }
+
+    // for [JACKSON-616]
+    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;
+    }
+
+    
+    static class ParamClassBean
+    {
+         public String name = "bar";
+         public Class<String> clazz ;
+
+         public ParamClassBean() { }
+         public ParamClassBean(String name) {
+             this.name = name;
+             clazz = String.class;
+         }
+    }
+
+    static class BooleanBean {
+        public Boolean wrapper;
+        public boolean primitive;
+        
+        protected Boolean ctor;
+        
+        @JsonCreator
+        public BooleanBean(@JsonProperty("ctor") Boolean foo) {
+            ctor = foo;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    /**
+     * Related to issues [JACKSON-155], [#170].
+     */
+    public void testFile() throws Exception
+    {
+        // Not portable etc... has to do:
+        File src = new File("/test").getAbsoluteFile();
+        String abs = src.getAbsolutePath();
+
+        // escape backslashes (for portability with windows)
+        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 testRegexps() throws IOException
+    {
+        final String PATTERN_STR = "abc:\\s?(\\d+)";
+        Pattern exp = Pattern.compile(PATTERN_STR);
+        /* Ok: easiest way is to just serialize first; problem
+         * is the backslash
+         */
+        String json = mapper.writeValueAsString(exp);
+        Pattern result = mapper.readValue(json, Pattern.class);
+        assertEquals(exp.pattern(), result.pattern());
+    }
+
+    public void testCurrency() throws IOException
+    {
+        Currency usd = Currency.getInstance("USD");
+        assertEquals(usd, new ObjectMapper().readValue(quote("USD"), Currency.class));
+    }
+
+    /**
+     * Test for [JACKSON-419]
+     */
+    public void testLocale() throws IOException
+    {
+        assertEquals(new Locale("en"), mapper.readValue(quote("en"), Locale.class));
+        assertEquals(new Locale("es", "ES"), mapper.readValue(quote("es_ES"), Locale.class));
+        assertEquals(new Locale("FI", "fi", "savo"), mapper.readValue(quote("fi_FI_savo"), Locale.class));
+    }
+
+    /**
+     * Test for [JACKSON-420] (add DeserializationConfig.FAIL_ON_NULL_FOR_PRIMITIVES)
+     */
+    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 ObjectMapper mapper2 = new ObjectMapper();
+        mapper2.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
+
+        // boolean
+        try {
+            mapper2.readValue("{\"booleanValue\":null}", PrimitivesBean.class);
+            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 {
+            mapper2.readValue("{\"byteValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for byte + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type byte");
+        }
+        try {
+            mapper2.readValue("{\"charValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for char + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type char");
+        }
+        try {
+            mapper2.readValue("{\"shortValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for short + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type short");
+        }
+        try {
+            mapper2.readValue("{\"intValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for int + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type int");
+        }
+        try {
+            mapper2.readValue("{\"longValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for long + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type long");
+        }
+
+        // float/double
+        try {
+            mapper2.readValue("{\"floatValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for float + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type float");
+        }
+        try {
+            mapper2.readValue("{\"doubleValue\":null}", PrimitivesBean.class);
+            fail("Expected failure for double + null");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Can not map JSON null into type double");
+        }
+    }
+    
+    /**
+     * Test for [JACKSON-483], allow handling of CharSequence
+     */
+    public void testCharSequence() throws IOException
+    {
+        CharSequence cs = mapper.readValue("\"abc\"", CharSequence.class);
+        assertEquals(String.class, cs.getClass());
+        assertEquals("abc", cs.toString());
+    }
+    
+    // [JACKSON-484]
+    public void testInetAddress() throws IOException
+    {
+        InetAddress address = mapper.readValue(quote("127.0.0.1"), InetAddress.class);
+        assertEquals("127.0.0.1", address.getHostAddress());
+
+        // should we try resolving host names? That requires connectivity... 
+        final String HOST = "www.ning.com";
+        address = mapper.readValue(quote(HOST), InetAddress.class);
+        assertEquals(HOST, address.getHostName());
+    }
+
+    // [JACKSON-597]
+    public void testClass() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertSame(String.class, mapper.readValue(quote("java.lang.String"), Class.class));
+
+        // then primitive types
+        assertSame(Boolean.TYPE, mapper.readValue(quote("boolean"), Class.class));
+        assertSame(Byte.TYPE, mapper.readValue(quote("byte"), Class.class));
+        assertSame(Short.TYPE, mapper.readValue(quote("short"), Class.class));
+        assertSame(Character.TYPE, mapper.readValue(quote("char"), Class.class));
+        assertSame(Integer.TYPE, mapper.readValue(quote("int"), Class.class));
+        assertSame(Long.TYPE, mapper.readValue(quote("long"), Class.class));
+        assertSame(Float.TYPE, mapper.readValue(quote("float"), Class.class));
+        assertSame(Double.TYPE, mapper.readValue(quote("double"), Class.class));
+        assertSame(Void.TYPE, mapper.readValue(quote("void"), Class.class));
+    }
+
+    // [JACKSON-605]
+    public void testClassWithParams() throws IOException
+    {
+        String json = mapper.writeValueAsString(new ParamClassBean("Foobar"));
+
+        ParamClassBean result = mapper.readValue(json, ParamClassBean.class);
+        assertEquals("Foobar", result.name);
+        assertSame(String.class, result.clazz);
+    }
+
+    // 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);
+    }
+
+    // for [JACKSON-616]
+    // @since 1.9
+    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);
+    }
+
+    // for [JACKSON-652]
+    // @since 1.9
+    public void testUntypedWithJsonArrays() throws Exception
+    {
+        // by default we get:
+        Object ob = mapper.readValue("[1]", Object.class);
+        assertTrue(ob instanceof List<?>);
+
+        // but can change to produce Object[]:
+        mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
+        ob = mapper.readValue("[1]", Object.class);
+        assertEquals(Object[].class, ob.getClass());
+    }
+
+    // Test for verifying that Long values are coerced to boolean correctly as well
+    public void testLongToBoolean() throws Exception
+    {
+        long value = 1L + Integer.MAX_VALUE;
+        BooleanBean b = mapper.readValue("{\"primitive\" : "+value+", \"wrapper\":"+value+", \"ctor\":"+value+"}",
+                    BooleanBean.class);
+        assertEquals(Boolean.TRUE, b.wrapper);
+        assertTrue(b.primitive);
+        assertEquals(Boolean.TRUE, b.ctor);
+    }
+
+    // [JACKSON-789]
+    public void testCharset() throws Exception
+    {
+        Charset UTF8 = Charset.forName("UTF-8");
+        assertSame(UTF8, mapper.readValue(quote("UTF-8"), Charset.class));
+    }
+
+    // [JACKSON-888]
+    public void testStackTraceElement() throws Exception
+    {
+        StackTraceElement elem = null;
+        try {
+            throw new IllegalStateException();
+        } catch (Exception e) {
+            elem = e.getStackTrace()[0];
+        }
+        String json = mapper.writeValueAsString(elem);
+        StackTraceElement back = mapper.readValue(json, StackTraceElement.class);
+        
+        assertEquals("testStackTraceElement", back.getMethodName());
+        assertEquals(elem.getLineNumber(), back.getLineNumber());
+        assertEquals(elem.getClassName(), back.getClassName());
+        assertEquals(elem.isNativeMethod(), back.isNativeMethod());
+        assertTrue(back.getClassName().endsWith("TestJdkTypes"));
+        assertFalse(back.isNativeMethod());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
new file mode 100644
index 0000000..a122926
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestMapDeserialization.java
@@ -0,0 +1,489 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+ at SuppressWarnings("serial")
+public class TestMapDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test classes, enums
+    /**********************************************************
+     */
+
+    enum Key {
+        KEY1, KEY2, WHATEVER;
+    }
+
+    static class BrokenMap
+        extends HashMap<Object,Object>
+    {
+        // No default ctor, nor @JsonCreators
+        public BrokenMap(boolean dummy) { super(); }
+    }
+
+    @JsonDeserialize(using=MapDeserializer.class)
+    static class CustomMap extends LinkedHashMap<String,String> { }
+
+    static class MapDeserializer extends StdDeserializer<CustomMap>
+    {
+        public MapDeserializer() { super(CustomMap.class); }
+        @Override
+        public CustomMap deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException
+        {
+            CustomMap result = new CustomMap();
+            result.put("x", jp.getText());
+            return result;
+        }
+    }
+
+    static class KeyType {
+        protected String value;
+        
+        private KeyType(String v, boolean bogus) {
+            value = v;
+        }
+
+        @JsonCreator
+        public static KeyType create(String v) {
+            return new KeyType(v, true);
+        }
+    }
+
+    // Issue #142
+    public static class EnumMapContainer {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
+        public EnumMap<KeyEnum,ITestType> testTypes;
+    }
+
+    public static class ListContainer {
+        public List<ITestType> testTypes;
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
+    public static interface ITestType { }
+
+    public static enum KeyEnum {
+        A, B
+    }
+    public static enum ConcreteType implements ITestType {
+        ONE, TWO;
+    }
+
+    
+    
+    /*
+    /**********************************************************
+    /* Test methods, untyped (Object valued) maps
+    /**********************************************************
+     */
+
+    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));
+    }
+
+    /**
+     * Let's also try another way to express "gimme a Map" deserialization;
+     * this time by specifying a Map class, to reduce need to cast
+     */
+    public void testUntypedMap2() throws Exception
+    {
+        // to get "untyped" default map-to-map, pass Object.class
+        String JSON = "{ \"a\" : \"x\" }";
+
+        @SuppressWarnings("unchecked")
+        HashMap<String,Object> result = /*(HashMap<String,Object>)*/ MAPPER.readValue(JSON, HashMap.class);
+        assertNotNull(result);
+        assertTrue(result instanceof Map<?,?>);
+
+        assertEquals(1, result.size());
+
+        assertEquals("x", result.get("a"));
+    }
+
+    /**
+     * Unit test for [JACKSON-185]
+     */
+    public void testUntypedMap3() throws Exception
+    {
+        String JSON = "{\"a\":[{\"a\":\"b\"},\"value\"]}";
+        Map<?,?> result = MAPPER.readValue(JSON, Map.class);
+        assertTrue(result instanceof Map<?,?>);
+        assertEquals(1, result.size());
+        Object ob = result.get("a");
+        assertNotNull(ob);
+        Collection<?> list = (Collection<?>)ob;
+        assertEquals(2, list.size());
+
+        JSON = "{ \"var1\":\"val1\", \"var2\":\"val2\", "
+            +"\"subvars\": ["
+            +" {  \"subvar1\" : \"subvar2\", \"x\" : \"y\" }, "
+            +" { \"a\":1 } ]"
+            +" }"
+            ;
+        result = MAPPER.readValue(JSON, Map.class);
+        assertTrue(result instanceof Map<?,?>);
+        assertEquals(3, result.size());
+    }
+
+    private static final String UNTYPED_MAP_JSON =
+            "{ \"double\":42.0, \"string\":\"string\","
+            +"\"boolean\":true, \"list\":[\"list0\"],"
+            +"\"null\":null }";
+    
+    static class ObjectWrapperMap extends HashMap<String, ObjectWrapper> { }
+    
+    public void testSpecialMap() throws IOException
+    {
+       final ObjectWrapperMap map = MAPPER.readValue(UNTYPED_MAP_JSON, ObjectWrapperMap.class);
+       _doTestUntyped(map);
+    }
+
+    public void testGenericMap() throws IOException
+    {
+        final Map<String, ObjectWrapper> map = MAPPER.readValue
+            (UNTYPED_MAP_JSON,
+             new TypeReference<Map<String, ObjectWrapper>>() { });
+       _doTestUntyped(map);
+    }
+    
+    private void _doTestUntyped(final Map<String, ObjectWrapper> map)
+    {
+       assertEquals(Double.valueOf(42), map.get("double").getObject());
+       assertEquals("string", map.get("string").getObject());
+       assertEquals(Boolean.TRUE, map.get("boolean").getObject());
+       assertEquals(Collections.singletonList("list0"), map.get("list").getObject());
+       assertTrue(map.containsKey("null"));
+       assertNull(map.get("null"));
+       assertEquals(5, map.size());
+    }
+    
+    // [JACKSON-620]: allow "" to mean 'null' for Maps
+    public void testFromEmptyString() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
+        Map<?,?> result = m.readValue(quote(""), Map.class);
+        assertNull(result);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, typed maps
+    /**********************************************************
+     */
+
+    public void testExactStringIntMap() throws Exception
+    {
+        // to get typing, must use type reference
+        String JSON = "{ \"foo\" : 13, \"bar\" : -39, \n \"\" : 0 }";
+        Map<String,Integer> result = MAPPER.readValue
+            (JSON, new TypeReference<HashMap<String,Integer>>() { });
+
+        assertNotNull(result);
+        assertEquals(HashMap.class, result.getClass());
+        assertEquals(3, result.size());
+
+        assertEquals(Integer.valueOf(13), result.get("foo"));
+        assertEquals(Integer.valueOf(-39), result.get("bar"));
+        assertEquals(Integer.valueOf(0), result.get(""));
+        assertNull(result.get("foobar"));
+        assertNull(result.get(" "));
+    }
+
+    /**
+     * Let's also check that it is possible to do type conversions
+     * to allow use of non-String Map keys.
+     */
+    public void testIntBooleanMap() throws Exception
+    {
+        // 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>>() { });
+
+        assertNotNull(result);
+        assertEquals(HashMap.class, result.getClass());
+        assertEquals(2, result.size());
+
+        assertEquals(Boolean.TRUE, result.get(Integer.valueOf(1)));
+        assertEquals(Boolean.FALSE, result.get(Integer.valueOf(-1)));
+        assertNull(result.get("foobar"));
+        assertNull(result.get(0));
+    }
+
+    public void testExactStringStringMap() throws Exception
+    {
+        // to get typing, must use type reference
+        String JSON = "{ \"a\" : \"b\" }";
+        Map<String,Integer> result = MAPPER.readValue
+            (JSON, new TypeReference<TreeMap<String,String>>() { });
+
+        assertNotNull(result);
+        assertEquals(TreeMap.class, result.getClass());
+        assertEquals(1, result.size());
+
+        assertEquals("b", result.get("a"));
+        assertNull(result.get("b"));
+    }
+
+    /**
+     * Unit test that verifies that it's ok to have incomplete
+     * information about Map class itself, as long as it's something
+     * we good guess about: for example, <code>Map.Class</code> will
+     * be replaced by something like <code>HashMap.class</code>,
+     * if given.
+     */
+    public void testGenericStringIntMap() throws Exception
+    {
+        // to get typing, must use type reference; but with abstract type
+        String JSON = "{ \"a\" : 1, \"b\" : 2, \"c\" : -99 }";
+        Map<String,Integer> result = MAPPER.readValue
+            (JSON, new TypeReference<Map<String,Integer>>() { });
+        assertNotNull(result);
+        assertTrue(result instanceof Map<?,?>);
+        assertEquals(3, result.size());
+
+        assertEquals(Integer.valueOf(-99), result.get("c"));
+        assertEquals(Integer.valueOf(2), result.get("b"));
+        assertEquals(Integer.valueOf(1), result.get("a"));
+
+        assertNull(result.get(""));
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, maps with enums
+    /**********************************************************
+     */
+
+    public void testEnumMap() throws Exception
+    {
+        String JSON = "{ \"KEY1\" : \"\", \"WHATEVER\" : null }";
+
+        // to get typing, must use type reference
+        EnumMap<Key,String> result = MAPPER.readValue
+            (JSON, new TypeReference<EnumMap<Key,String>>() { });
+
+        assertNotNull(result);
+        assertEquals(EnumMap.class, result.getClass());
+        assertEquals(2, result.size());
+
+        assertEquals("", result.get(Key.KEY1));
+        // null should be ok too...
+        assertTrue(result.containsKey(Key.WHATEVER));
+        assertNull(result.get(Key.WHATEVER));
+
+        // plus we have nothing for this key
+        assertFalse(result.containsKey(Key.KEY2));
+        assertNull(result.get(Key.KEY2));
+    }
+
+    public void testMapWithEnums() throws Exception
+    {
+        String JSON = "{ \"KEY2\" : \"WHATEVER\" }";
+
+        // to get typing, must use type reference
+        Map<Enum<?>,Enum<?>> result = MAPPER.readValue
+            (JSON, new TypeReference<Map<Key,Key>>() { });
+
+        assertNotNull(result);
+        assertTrue(result instanceof Map<?,?>);
+        assertEquals(1, result.size());
+
+        assertEquals(Key.WHATEVER, result.get(Key.KEY2));
+        assertNull(result.get(Key.WHATEVER));
+        assertNull(result.get(Key.KEY1));
+    }
+
+    public void testEnumPolymorphicSerializationTest() throws Exception 
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        List<ITestType> testTypesList = new ArrayList<ITestType>();
+        testTypesList.add(ConcreteType.ONE);
+        testTypesList.add(ConcreteType.TWO);
+        ListContainer listContainer = new ListContainer();
+        listContainer.testTypes = testTypesList;
+        String json = mapper.writeValueAsString(listContainer);
+        listContainer = mapper.readValue(json, ListContainer.class);
+        EnumMapContainer enumMapContainer = new EnumMapContainer();
+        EnumMap<KeyEnum,ITestType> testTypesMap = new EnumMap<KeyEnum,ITestType>(KeyEnum.class);
+        testTypesMap.put(KeyEnum.A, ConcreteType.ONE);
+        testTypesMap.put(KeyEnum.B, ConcreteType.TWO);
+        enumMapContainer.testTypes = testTypesMap;
+        
+        json = mapper.writeValueAsString(enumMapContainer);
+        enumMapContainer = mapper.readValue(json, EnumMapContainer.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods, maps with Date
+    /**********************************************************
+     */
+    public void testDateMap() throws Exception
+    {
+    	 Date date1=new Date(123456000L);
+    	 DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+         
+    	 String JSON = "{ \""+  fmt.format(date1)+"\" : \"\", \""+new Date(0).getTime()+"\" : null }";
+    	 HashMap<Date,String> result=  MAPPER.readValue
+    	            (JSON, new TypeReference<HashMap<Date,String>>() { });
+    	 
+    	 assertNotNull(result);
+    	 assertEquals(HashMap.class, result.getClass());
+    	 assertEquals(2, result.size());
+    	 
+    	 assertTrue(result.containsKey(date1));
+    	 assertEquals("", result.get(new Date(123456000L)));
+    	 
+    	 assertTrue(result.containsKey(new Date(0)));
+    	 assertNull(result.get(new Date(0)));
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods, maps with various alternative key types
+    /**********************************************************
+     */
+
+    public void testCalendarMap() throws Exception
+    {
+    	 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+         c.setTimeInMillis(123456000L);
+         DateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+    	 String JSON = "{ \""+fmt.format(c.getTime())+"\" : \"\", \""+new Date(0).getTime()+"\" : null }";
+    	 HashMap<Calendar,String> result = MAPPER.readValue
+    	            (JSON, new TypeReference<HashMap<Calendar,String>>() { });
+    	 
+    	 assertNotNull(result);
+    	 assertEquals(HashMap.class, result.getClass());
+    	 assertEquals(2, result.size());
+    	
+    	 assertTrue(result.containsKey(c));
+    	 assertEquals("", result.get(c));
+    	 c.setTimeInMillis(0);
+    	 assertTrue(result.containsKey(c));
+    	 assertNull(result.get(c));
+    }
+
+    // [JACKSON-726]
+    public void testUUIDKeyMap() throws Exception
+    {
+         UUID key = UUID.nameUUIDFromBytes("foobar".getBytes("UTF-8"));
+         String JSON = "{ \""+key+"\":4}";
+         Map<UUID,Object> result = MAPPER.readValue(JSON, new TypeReference<Map<UUID,Object>>() { });
+         assertNotNull(result);
+         assertEquals(1, result.size());
+         Object ob = result.keySet().iterator().next();
+         assertNotNull(ob);
+         assertEquals(UUID.class, ob.getClass());
+         assertEquals(key, ob);
+    }
+
+    public void testLocaleKeyMap() throws Exception {
+        Locale key = Locale.CHINA;
+        String JSON = "{ \"" + key + "\":4}";
+        Map<Locale, Object> result = MAPPER.readValue(JSON, new TypeReference<Map<Locale, Object>>() {
+        });
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        Object ob = result.keySet().iterator().next();
+        assertNotNull(ob);
+        assertEquals(Locale.class, ob.getClass());
+        assertEquals(key, ob);
+    }
+
+    // Test confirming that @JsonCreator may be used with Map Key types
+    public void testKeyWithCreator() throws Exception
+    {
+        // first, key should deserialize normally:
+        KeyType key = MAPPER.readValue(quote("abc"), KeyType.class);
+        assertEquals("abc", key.value);
+
+        Map<KeyType,Integer> map = MAPPER.readValue("{\"foo\":3}", new TypeReference<Map<KeyType,Integer>>() {} );
+        assertEquals(1, map.size());
+        key = map.keySet().iterator().next();
+        assertEquals("foo", key.value);
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods, annotated Maps
+    /**********************************************************
+     */
+
+    /**
+     * Simple test to ensure that @JsonDeserialize.using is
+     * recognized
+     */
+    public void testMapWithDeserializer() throws IOException
+    {
+        CustomMap result = MAPPER.readValue(quote("xyz"), CustomMap.class);
+        assertEquals(1, result.size());
+        assertEquals("xyz", result.get("x"));
+    }
+
+    /*
+    /**********************************************************
+    /* Error tests
+    /**********************************************************
+     */
+
+    public void testMapError() throws Exception
+    {
+        try {
+            Object result = MAPPER.readValue("[ 1, 2 ]", 
+                                             new TypeReference<Map<String,String>>() { });
+            fail("Expected an exception, but got result value: "+result);
+        } catch (JsonMappingException jex) {
+            verifyException(jex, "START_ARRAY");
+        }
+    }
+
+    public void testNoCtorMap() throws Exception
+    {
+        try {
+            BrokenMap result = MAPPER.readValue("{ \"a\" : 3 }", BrokenMap.class);
+            // should never get here; assert added to remove compiler warning
+            assertNull(result);
+        } catch (JsonMappingException e) {
+            // instead, should get this exception:
+            verifyException(e, "no default constructor found");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java
new file mode 100644
index 0000000..89d684a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestNullHandling.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class TestNullHandling extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class FunnyNullDeserializer extends JsonDeserializer<String>
+    {
+        @Override
+        public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+            return "text";
+        }
+
+        @Override
+        public String getNullValue() { return "funny"; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    // Test for [JACKSON-643]
+    public void testCustomRootNulls() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addDeserializer(String.class, new FunnyNullDeserializer());
+        mapper.registerModule(module);
+
+        // should get non-default null directly:
+        String str = mapper.readValue("null", String.class);
+        assertNotNull(str);
+        assertEquals("funny", str);
+        
+        // as well as via ObjectReader
+        ObjectReader reader = mapper.reader(String.class);
+        str = reader.readValue("null");
+        assertNotNull(str);
+        assertEquals("funny", str);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java
new file mode 100644
index 0000000..f223fb2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestNumbers.java
@@ -0,0 +1,121 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * Tests related to [JACKSON-139]
+ */
+public class TestNumbers
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************************
+    /* Helper classes, beans
+    /**********************************************************************
+     */
+
+    static class MyBeanHolder {
+        public Long id;
+        public MyBeanDefaultValue defaultValue;
+    }
+
+    static class MyBeanDefaultValue
+    {
+        public MyBeanValue value;
+    }
+
+    @JsonDeserialize(using=MyBeanDeserializer.class)
+    static class MyBeanValue {
+        public BigDecimal decimal;
+        public MyBeanValue() { this(null); }
+        public MyBeanValue(BigDecimal d) { this.decimal = d; }
+    }
+
+    /*
+    /**********************************************************************
+    /* Helper classes, serializers/deserializers/resolvers
+    /**********************************************************************
+     */
+    
+    static class MyBeanDeserializer extends JsonDeserializer<MyBeanValue>
+    {
+        @Override
+        public MyBeanValue deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException
+        {
+            return new MyBeanValue(jp.getDecimalValue());
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Unit tests
+    /**********************************************************************
+     */
+    
+    public void testFloatNaN() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Float result = m.readValue(" \"NaN\"", Float.class);
+        assertEquals(Float.valueOf(Float.NaN), result);
+    }
+
+    public void testDoubleInf() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Double result = m.readValue(" \""+Double.POSITIVE_INFINITY+"\"", Double.class);
+        assertEquals(Double.valueOf(Double.POSITIVE_INFINITY), result);
+
+        result = m.readValue(" \""+Double.NEGATIVE_INFINITY+"\"", Double.class);
+        assertEquals(Double.valueOf(Double.NEGATIVE_INFINITY), result);
+    }
+
+    // [JACKSON-349]
+    public void testEmptyAsNumber() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertNull(m.readValue(quote(""), Integer.class));
+        assertNull(m.readValue(quote(""), Long.class));
+        assertNull(m.readValue(quote(""), Float.class));
+        assertNull(m.readValue(quote(""), Double.class));
+        assertNull(m.readValue(quote(""), BigInteger.class));
+        assertNull(m.readValue(quote(""), BigDecimal.class));
+    }
+
+    // // Tests for [JACKSON-668]
+    
+    public void testDeserializeDecimalHappyPath() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = "{\"defaultValue\": { \"value\": 123 } }";
+        MyBeanHolder result = mapper.readValue(json, MyBeanHolder.class);
+        assertEquals(BigDecimal.valueOf(123), result.defaultValue.value.decimal);
+    }
+
+    public void testDeserializeDecimalProperException() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = "{\"defaultValue\": { \"value\": \"123\" } }";
+        try {
+            mapper.readValue(json, MyBeanHolder.class);
+            fail("should have raised exception");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "not numeric");
+        }
+    }
+
+    public void testDeserializeDecimalProperExceptionWhenIdSet() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = "{\"id\": 5, \"defaultValue\": { \"value\": \"123\" } }";
+        try {
+            MyBeanHolder result = mapper.readValue(json, MyBeanHolder.class);
+            fail("should have raised exception instead value was set to " + result.defaultValue.value.decimal.toString());
+        } catch (JsonProcessingException e) {
+            verifyException(e, "not numeric");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestOverloaded.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestOverloaded.java
new file mode 100644
index 0000000..156e718
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestOverloaded.java
@@ -0,0 +1,156 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests related to handling of overloaded methods.
+ * and specifically addressing problems [JACKSON-189]
+ * and [JACKSON-739]
+ */
+public class TestOverloaded
+    extends BaseMapTest
+{
+    static class BaseListBean
+    {
+        List<String> list;
+
+        BaseListBean() { }
+
+        public void setList(List<String> l) { list = l; }
+    }
+
+    static class ArrayListBean extends BaseListBean
+    {
+        ArrayListBean() { }
+
+        public void setList(ArrayList<String> l) { super.setList(l); }
+    }
+
+    // 27-Feb-2010, tatus: Won't fix immediately, need to comment out
+    /*
+    static class OverloadBean
+    {
+        String a;
+
+        public OverloadBean() { }
+
+        public void setA(int value) { a = String.valueOf(value); }
+        public void setA(String value) { a = value; }
+    }
+    */
+
+    static class NumberBean {
+    	protected Object value;
+    	
+    	public void setValue(Number n) { value = n; }
+    }
+
+    static class WasNumberBean extends NumberBean {
+    	public void setValue(String str) { value = str; }
+    }
+
+    // [JACKSON-739]
+    static class Overloaded739
+    {
+        protected Object _value;
+        
+        @JsonProperty
+        public void setValue(String str) { _value = str; }
+
+        // no annotation, should not be chosen:
+        public void setValue(Object o) { throw new UnsupportedOperationException(); }
+    }
+    
+    /**
+     * And then a Bean that is conflicting and should not work
+     */
+    static class ConflictBean {
+    	public void setA(ArrayList<Object> a) { }
+    	public void setA(LinkedList<Object> a) { }
+    }
+    
+    /*
+    /************************************************************
+    /* Unit tests, valid
+    /************************************************************
+    */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * Unit test related to [JACKSON-189]
+     */
+    // 27-Feb-2010, tatus: Won't fix immediately, need to comment out
+    /*
+    public void testSimpleOverload() throws Exception
+    {
+        OverloadBean bean;
+        try {
+            bean = new ObjectMapper().readValue("{ \"a\" : 13 }", OverloadBean.class);
+        } catch (JsonMappingException e) {
+            fail("Did not expect an exception, got: "+e.getMessage());
+            return;
+        }
+        assertEquals("13", bean.a);
+    }
+    */
+
+    /**
+     * It should be ok to overload with specialized 
+     * version; more specific method should be used.
+     */
+    public void testSpecialization() throws Exception
+    {
+        ArrayListBean bean = MAPPER.readValue
+            ("{\"list\":[\"a\",\"b\",\"c\"]}", ArrayListBean.class);
+        assertNotNull(bean.list);
+        assertEquals(3, bean.list.size());
+        assertEquals(ArrayList.class, bean.list.getClass());
+        assertEquals("a", bean.list.get(0));
+        assertEquals("b", bean.list.get(1));
+        assertEquals("c", bean.list.get(2));
+    }
+
+    /**
+     * As per [JACKSON-255], should also allow more general overriding,
+     * as long as there are no in-class conflicts.
+     */
+    public void testOverride() throws Exception
+    {
+        WasNumberBean bean = MAPPER.readValue
+            ("{\"value\" : \"abc\"}", WasNumberBean.class);
+        assertNotNull(bean);
+        assertEquals("abc", bean.value);
+    }
+
+    // for [JACKSON-739]
+    public void testConflictResolution() throws Exception
+    {
+        Overloaded739 bean = MAPPER.readValue
+                ("{\"value\":\"abc\"}", Overloaded739.class);
+        assertNotNull(bean);
+        assertEquals("abc", bean._value);
+    }
+    
+    /*
+    /************************************************************
+    /* Unit tests, failures
+    /************************************************************
+    */
+    
+    /**
+     * For genuine setter conflict, an exception is to be thrown.
+     */
+    public void testSetterConflict() throws Exception
+    {
+    	try {    		
+    	MAPPER.readValue("{ }", ConflictBean.class);
+    	} catch (Exception e) {
+    	    verifyException(e, "Conflicting setter definitions");
+    	}
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java
new file mode 100644
index 0000000..c3eba07
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestParentChildReferences.java
@@ -0,0 +1,372 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestParentChildReferences
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test classes
+    /**********************************************************
+     */
+
+    /**
+     * First, a simple 'tree': just parent/child linkage
+     */
+    static class SimpleTreeNode
+    {
+        public String name;
+        
+        // Reference back to parent; reference, ignored during ser,
+        // re-constructed during deser
+        @JsonBackReference
+        public SimpleTreeNode parent;
+
+        // Reference that is serialized normally during ser, back
+        // reference within pointed-to instance assigned to point to
+        // referring bean ("this")
+        @JsonManagedReference
+        public SimpleTreeNode child;
+
+        public SimpleTreeNode() { this(null); }
+        public SimpleTreeNode(String n) { name = n; }
+    }
+
+    static class SimpleTreeNode2
+    {
+        public String name;
+        private SimpleTreeNode2 parent;
+        private SimpleTreeNode2 child;
+
+        public SimpleTreeNode2() { this(null); }
+        public SimpleTreeNode2(String n) { name = n; }
+
+        @JsonBackReference
+        public SimpleTreeNode2 getParent() { return parent; }
+        public void setParent(SimpleTreeNode2 p) { parent = p; }
+
+        @JsonManagedReference
+        public SimpleTreeNode2 getChild() { return child; }
+        public void setChild(SimpleTreeNode2 c) { child = c; }
+    }
+    
+    /**
+     * Then nodes with two separate linkages; parent/child
+     * and prev/next-sibling
+     */
+    static class FullTreeNode
+    {
+        public String name;
+
+        // parent-child links
+        @JsonBackReference("parent")
+        public FullTreeNode parent;
+        @JsonManagedReference("parent")
+        public FullTreeNode firstChild;
+
+        // sibling-links
+        @JsonManagedReference("sibling")
+        public FullTreeNode next;
+        @JsonBackReference("sibling")
+        protected FullTreeNode prev;
+        
+        public FullTreeNode() { this(null); }
+        public FullTreeNode(String name) {
+            this.name = name;
+        }
+    }
+
+    /**
+     * Class for testing managed references via arrays
+     */
+    static class NodeArray
+    {
+        @JsonManagedReference("arr")
+        public ArrayNode[] nodes;
+    }
+
+    static class ArrayNode
+    {
+        public String name;
+        
+        @JsonBackReference("arr")
+        public NodeArray parent;
+
+        public ArrayNode() { this(null); }
+        public ArrayNode(String n) { name = n; }
+    }
+    
+    /**
+     * Class for testing managed references via Collections
+     */
+    static class NodeList
+    {
+        @JsonManagedReference
+        public List<NodeForList> nodes;
+    }
+
+    static class NodeForList
+    {
+        public String name;
+        
+        @JsonBackReference
+        public NodeList parent;
+
+        public NodeForList() { this(null); }
+        public NodeForList(String n) { name = n; }
+    }
+    
+    static class NodeMap
+    {
+        @JsonManagedReference
+        public Map<String,NodeForMap> nodes;
+    }
+
+    static class NodeForMap
+    {
+        public String name;
+        
+        @JsonBackReference
+        public NodeMap parent;
+
+        public NodeForMap() { this(null); }
+        public NodeForMap(String n) { name = n; }
+    }
+
+    public static class Parent {
+        @JsonManagedReference
+        private final List<Child> children = new ArrayList<Child>();
+
+        public List<Child> getChildren() { return children; }
+
+        public void addChild(Child child) { children.add(child); child.setParent(this); }
+    }
+
+    public static class Child {
+        private Parent parent;
+        private final String value; // So that the bean is not empty of properties
+
+        public Child(@JsonProperty("value") String value) { this.value = value; }
+
+        public String getValue() { return value; }
+
+        @JsonBackReference
+        public Parent getParent() { return parent; }
+
+        public void setParent(Parent parent) { this.parent = parent; }
+    }    
+
+    // [JACKSON-368]
+
+    @JsonTypeInfo(use=Id.NAME)
+    @JsonSubTypes({@JsonSubTypes.Type(ConcreteNode.class)})
+    static abstract class AbstractNode
+    {
+        public String id;
+        
+        @JsonManagedReference public AbstractNode next;
+        @JsonBackReference public AbstractNode prev;
+    }
+
+    @JsonTypeName("concrete")
+    static class ConcreteNode extends AbstractNode {
+        public ConcreteNode() { }
+        public ConcreteNode(String id) { this.id = id; }
+    }
+    
+    // [JACKSON-708]
+    static class Model708 { }
+    
+    static class Advertisement708 extends Model708 {
+        public String title;
+        @JsonManagedReference public List<Photo708> photos;
+    }
+
+    static class Photo708 extends Model708 {
+        public int id;
+        @JsonBackReference public Advertisement708 advertisement;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testSimpleRefs() throws Exception
+    {
+        SimpleTreeNode root = new SimpleTreeNode("root");
+        SimpleTreeNode child = new SimpleTreeNode("kid");
+        ObjectMapper mapper = new ObjectMapper();
+        root.child = child;
+        child.parent = root;
+        
+        String json = mapper.writeValueAsString(root);
+        
+        SimpleTreeNode resultNode = mapper.readValue(json, SimpleTreeNode.class);
+        assertEquals("root", resultNode.name);
+        SimpleTreeNode resultChild = resultNode.child;
+        assertNotNull(resultChild);
+        assertEquals("kid", resultChild.name);
+        assertSame(resultChild.parent, resultNode);
+    }
+
+    // [JACKSON-693]
+    public void testSimpleRefsWithGetter() throws Exception
+    {
+        SimpleTreeNode2 root = new SimpleTreeNode2("root");
+        SimpleTreeNode2 child = new SimpleTreeNode2("kid");
+        ObjectMapper mapper = new ObjectMapper();
+        root.child = child;
+        child.parent = root;
+        
+        String json = mapper.writeValueAsString(root);
+        
+        SimpleTreeNode2 resultNode = mapper.readValue(json, SimpleTreeNode2.class);
+        assertEquals("root", resultNode.name);
+        SimpleTreeNode2 resultChild = resultNode.child;
+        assertNotNull(resultChild);
+        assertEquals("kid", resultChild.name);
+        assertSame(resultChild.parent, resultNode);
+    }
+    
+    public void testFullRefs() throws Exception
+    {
+        FullTreeNode root = new FullTreeNode("root");
+        FullTreeNode child1 = new FullTreeNode("kid1");
+        FullTreeNode child2 = new FullTreeNode("kid2");
+        ObjectMapper mapper = new ObjectMapper();
+        root.firstChild = child1;
+        child1.parent = root;
+        child1.next = child2;
+        child2.prev = child1;
+        
+        String json = mapper.writeValueAsString(root);
+        
+        FullTreeNode resultNode = mapper.readValue(json, FullTreeNode.class);
+        assertEquals("root", resultNode.name);
+        FullTreeNode resultChild = resultNode.firstChild;
+        assertNotNull(resultChild);
+        assertEquals("kid1", resultChild.name);
+        assertSame(resultChild.parent, resultNode);
+
+        // and then sibling linkage
+        assertNull(resultChild.prev);
+        FullTreeNode resultChild2 = resultChild.next;
+        assertNotNull(resultChild2);
+        assertEquals("kid2", resultChild2.name);
+        assertSame(resultChild, resultChild2.prev);
+        assertNull(resultChild2.next);
+    }
+
+    public void testArrayOfRefs() throws Exception
+    {
+        NodeArray root = new NodeArray();
+        ArrayNode node1 = new ArrayNode("a");
+        ArrayNode node2 = new ArrayNode("b");
+        root.nodes = new ArrayNode[] { node1, node2 };
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(root);
+        
+        NodeArray result = mapper.readValue(json, NodeArray.class);
+        ArrayNode[] kids = result.nodes;
+        assertNotNull(kids);
+        assertEquals(2, kids.length);
+        assertEquals("a", kids[0].name);
+        assertEquals("b", kids[1].name);
+        assertSame(result, kids[0].parent);
+        assertSame(result, kids[1].parent);
+    }
+
+    public void testListOfRefs() throws Exception
+    {
+        NodeList root = new NodeList();
+        NodeForList node1 = new NodeForList("a");
+        NodeForList node2 = new NodeForList("b");
+        root.nodes = Arrays.asList(node1, node2);
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(root);
+        
+        NodeList result = mapper.readValue(json, NodeList.class);
+        List<NodeForList> kids = result.nodes;
+        assertNotNull(kids);
+        assertEquals(2, kids.size());
+        assertEquals("a", kids.get(0).name);
+        assertEquals("b", kids.get(1).name);
+        assertSame(result, kids.get(0).parent);
+        assertSame(result, kids.get(1).parent);
+    }
+
+    public void testMapOfRefs() throws Exception
+    {
+        NodeMap root = new NodeMap();
+        NodeForMap node1 = new NodeForMap("a");
+        NodeForMap node2 = new NodeForMap("b");
+        Map<String,NodeForMap> nodes = new HashMap<String, NodeForMap>();
+        nodes.put("a1", node1);
+        nodes.put("b2", node2);
+        root.nodes = nodes;
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(root);
+        
+        NodeMap result = mapper.readValue(json, NodeMap.class);
+        Map<String,NodeForMap> kids = result.nodes;
+        assertNotNull(kids);
+        assertEquals(2, kids.size());
+        assertNotNull(kids.get("a1"));
+        assertNotNull(kids.get("b2"));
+        assertEquals("a", kids.get("a1").name);
+        assertEquals("b", kids.get("b2").name);
+        assertSame(result, kids.get("a1").parent);
+        assertSame(result, kids.get("b2").parent);
+    }
+
+    // for [JACKSON-368]
+    public void testAbstract368() throws Exception
+    {
+        AbstractNode parent = new ConcreteNode("p");
+        AbstractNode child = new ConcreteNode("c");
+        parent.next = child;
+        child.prev = parent;
+
+        // serialization ought to be ok
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(parent);
+
+        AbstractNode root = mapper.readValue(json, AbstractNode.class);
+
+        assertEquals(ConcreteNode.class, root.getClass());
+        assertEquals("p", root.id);
+        assertNull(root.prev);
+        AbstractNode leaf = root.next;
+        assertNotNull(leaf);
+        assertEquals("c", leaf.id);
+        assertSame(root, leaf.prev);
+    }
+    
+    public void testIssue693() throws Exception
+    {
+        Parent parent = new Parent();
+        parent.addChild(new Child("foo"));
+        parent.addChild(new Child("bar"));
+        ObjectMapper mapper = new ObjectMapper();
+        byte[] bytes = mapper.writeValueAsBytes(parent);
+        Parent value = mapper.readValue(bytes, Parent.class); 
+        for (Child child : value.children) {
+            assertEquals(value, child.getParent());
+        }
+    }
+
+    public void testIssue708() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Advertisement708 ad = mapper.readValue("{\"title\":\"Hroch\",\"photos\":[{\"id\":3}]}", Advertisement708.class);      
+        assertNotNull(ad);
+    }   
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java
new file mode 100644
index 0000000..471fd85
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestSetterlessProperties.java
@@ -0,0 +1,130 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that feature requested
+ * via [JACKSON-88] ("setterless collections") work as
+ * expected, similar to how Collections and Maps work
+ * with JAXB.
+ */
+public class TestSetterlessProperties
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+
+    static class CollectionBean
+    {
+        List<String> _values = new ArrayList<String>();
+
+        public List<String> getValues() { return _values; }
+    }
+
+    static class MapBean
+    {
+        Map<String,Integer> _values = new HashMap<String,Integer>();
+
+        public Map<String,Integer> getValues() { return _values; }
+    }
+
+    // testing to verify that field has precedence over getter, for lists
+    static class Dual
+    {
+        @JsonProperty("list") protected List<Integer> values = new ArrayList<Integer>();
+
+        public Dual() { }
+        
+        public List<Integer> getList() {
+            throw new IllegalStateException("Should not get called");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimpleSetterlessCollectionOk()
+        throws Exception
+    {
+        CollectionBean result = new ObjectMapper().readValue
+            ("{\"values\":[ \"abc\", \"def\" ]}", CollectionBean.class);
+        List<String> l = result._values;
+        assertEquals(2, l.size());
+        assertEquals("abc", l.get(0));
+        assertEquals("def", l.get(1));
+    }
+
+    /**
+     * Let's also verify that disabling the feature makes
+     * deserialization fail for setterless bean
+     */
+    public void testSimpleSetterlessCollectionFailure()
+        throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // by default, it should be enabled
+        assertTrue(m.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS));
+        m.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
+        assertFalse(m.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS));
+
+        // and now this should fail
+        try {
+            m.readValue
+                ("{\"values\":[ \"abc\", \"def\" ]}", CollectionBean.class);
+            fail("Expected an exception");
+        } catch (JsonMappingException e) {
+            /* Not a good exception, ideally could suggest a need for
+             * a setter...?
+             */
+            verifyException(e, "Unrecognized field");
+        }
+    }
+
+    public void testSimpleSetterlessMapOk()
+        throws Exception
+    {
+        MapBean result = new ObjectMapper().readValue
+            ("{\"values\":{ \"a\": 15, \"b\" : -3 }}", MapBean.class);
+        Map<String,Integer> m = result._values;
+        assertEquals(2, m.size());
+        assertEquals(Integer.valueOf(15), m.get("a"));
+        assertEquals(Integer.valueOf(-3), m.get("b"));
+    }
+
+    public void testSimpleSetterlessMapFailure()
+        throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_GETTERS_AS_SETTERS, false);
+        // so this should fail now without a setter
+        try {
+            m.readValue
+                ("{\"values\":{ \"a\":3 }}", MapBean.class);
+            fail("Expected an exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, "Unrecognized field");
+        }
+    }
+
+    /* Test for [JACKSON-328], precedence of "getter-as-setter" (for Lists) versus
+     * field for same property.
+     */
+    public void testSetterlessPrecedence() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_GETTERS_AS_SETTERS, true);
+        Dual value = m.readValue("{\"list\":[1,2,3]}, valueType)", Dual.class);
+        assertNotNull(value);
+        assertEquals(3, value.values.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleAtomicTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleAtomicTypes.java
new file mode 100644
index 0000000..fba4a3f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleAtomicTypes.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.util.concurrent.atomic.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestSimpleAtomicTypes
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    public void testAtomicBoolean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AtomicBoolean b = mapper.readValue("true", AtomicBoolean.class);
+        assertTrue(b.get());
+    }
+
+    public void testAtomicInt() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AtomicInteger value = mapper.readValue("13", AtomicInteger.class);
+        assertEquals(13, value.get());
+    }
+
+    public void testAtomicLong() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AtomicLong value = mapper.readValue("12345678901", AtomicLong.class);
+        assertEquals(12345678901L, value.get());
+    }
+
+    public void testAtomicReference() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        AtomicReference<long[]> value = mapper.readValue("[1,2]",
+                new com.fasterxml.jackson.core.type.TypeReference<AtomicReference<long[]>>() { });
+        Object ob = value.get();
+        assertNotNull(ob);
+        assertEquals(long[].class, ob.getClass());
+        long[] longs = (long[]) ob;
+        assertNotNull(longs);
+        assertEquals(2, longs.length);
+        assertEquals(1, longs[0]);
+        assertEquals(2, longs[1]);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java
new file mode 100644
index 0000000..1aeae81
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestSimpleTypes.java
@@ -0,0 +1,443 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.net.URI;
+import java.util.*;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Unit tests for verifying handling of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class TestSimpleTypes
+    extends BaseMapTest
+{
+    final static String NAN_STRING = "NaN";
+
+    final static class BooleanBean {
+        boolean _v;
+        void setV(boolean v) { _v = v; }
+    }
+
+    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; }
+    }
+
+    /**
+     * 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); }
+    }
+
+    /*
+    /**********************************************************
+    /* Then tests for primitives
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testBooleanPrimitive() throws Exception
+    {
+        // first, simple case:
+        BooleanBean result = MAPPER.readValue(new StringReader("{\"v\":true}"), BooleanBean.class);
+        assertTrue(result._v);
+        // then [JACKSON-79]:
+        result = MAPPER.readValue(new StringReader("{\"v\":null}"), BooleanBean.class);
+        assertNotNull(result);
+        assertFalse(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 testIntPrimitive() throws Exception
+    {
+        // first, simple case:
+        IntBean result = MAPPER.readValue(new StringReader("{\"v\":3}"), IntBean.class);
+        assertEquals(3, result._v);
+        // then [JACKSON-79]:
+        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]);
+    }
+
+    public void testDoublePrimitive() throws Exception
+    {
+        // first, simple case:
+        // bit tricky with binary fps but...
+        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]);
+    }
+
+    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]);
+    }
+    
+    /**
+     * 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);
+    }
+
+    /*
+    /**********************************************************
+    /* Then tests for wrappers
+    /**********************************************************
+     */
+
+    /**
+     * 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);
+
+        // [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
+        result = MAPPER.readValue(new StringReader("0"), Boolean.class);
+        assertEquals(Boolean.FALSE, result);
+        result = MAPPER.readValue(new StringReader("1"), Boolean.class);
+        assertEquals(Boolean.TRUE, result);
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    /* 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(new StringReader(str), Double.class);
+            	assertEquals(exp, result);
+            }
+            // and then as coerced String:
+            result = MAPPER.readValue(new StringReader(" \""+str+"\""), Double.class);
+            assertEquals(exp, result);
+        }
+    }
+
+    // as per [Issue#42], allow Base64 variant use as well
+    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.reader(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 testNull() throws Exception
+    {
+        // null doesn't really have a type, fake by assuming Object
+        Object result = MAPPER.readValue("   null", Object.class);
+        assertNull(result);
+    }
+
+    public void testClass() throws Exception
+    {
+        Class<?> result = MAPPER.readValue("\"java.lang.String\"", Class.class);
+        assertEquals(String.class, result);
+    }
+
+    public void testBigDecimal() throws Exception
+    {
+        BigDecimal value = new BigDecimal("0.001");
+        BigDecimal result = MAPPER.readValue(new StringReader(value.toString()), BigDecimal.class);
+        assertEquals(value, result);
+    }
+
+    public void testBigInteger() throws Exception
+    {
+        BigInteger value = new BigInteger("-1234567890123456789012345567809");
+        BigInteger result = MAPPER.readValue(new StringReader(value.toString()), BigInteger.class);
+        assertEquals(value, result);
+    }
+
+    public void testUUID() throws Exception
+    {
+        UUID value = UUID.fromString("76e6d183-5f68-4afa-b94a-922c1fdb83f8");
+        assertEquals(value, MAPPER.readValue("\""+value.toString()+"\"", UUID.class));
+
+        // [JACKSON-393] fix:
+
+        // first, null should come as null
+        TokenBuffer buf = new TokenBuffer(null);
+        buf.writeObject(null);
+        assertNull(MAPPER.readValue(buf.asParser(), UUID.class));
+        buf.close();
+
+        // then, UUID itself come as is:
+        buf = new TokenBuffer(null);
+        buf.writeObject(value);
+        assertSame(value, MAPPER.readValue(buf.asParser(), UUID.class));
+
+        // and finally from byte[]
+        // oh crap; JDK UUID just... sucks. Not even byte[] accessors or constructors? Huh?
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        DataOutputStream out = new DataOutputStream(bytes);
+        out.writeLong(value.getMostSignificantBits());
+        out.writeLong(value.getLeastSignificantBits());
+        byte[] data = bytes.toByteArray();
+        assertEquals(16, data.length);
+        
+        buf.writeObject(data);
+
+        UUID value2 = MAPPER.readValue(buf.asParser(), UUID.class);
+        
+        assertEquals(value, value2);
+        buf.close();
+    }
+
+    public void testURL() throws Exception
+    {
+        URL value = new URL("http://foo.com");
+        assertEquals(value, MAPPER.readValue("\""+value.toString()+"\"", URL.class));
+
+        // trivial case; null to null, embedded URL to URL
+        TokenBuffer buf = new TokenBuffer(null);
+        buf.writeObject(null);
+        assertNull(MAPPER.readValue(buf.asParser(), URL.class));
+        buf.close();
+
+        // then, UUID itself come as is:
+        buf = new TokenBuffer(null);
+        buf.writeObject(value);
+        assertSame(value, MAPPER.readValue(buf.asParser(), URL.class));
+        buf.close();
+    }
+
+    public void testURI() throws Exception
+    {
+        URI value = new URI("http://foo.com");
+        assertEquals(value, MAPPER.readValue("\""+value.toString()+"\"", URI.class));
+    }
+
+    /*
+    /**********************************************************
+    /* 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);
+        }
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestStatics.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestStatics.java
new file mode 100644
index 0000000..3ce2441
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestStatics.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.databind.deser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests for checking that static methods are not recognized as accessors
+ * for properties
+ */
+public class TestStatics
+    extends BaseMapTest
+{
+    static class Bean
+    {
+        int _x;
+
+        public static void setX(int value) { throw new Error("Should NOT call static method"); }
+        
+        @JsonProperty("x") public void assignX(int x) { _x = x; }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testSimpleIgnore() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // should not care about static setter...
+        Bean result = m.readValue("{ \"x\":3}", Bean.class);
+        assertEquals(3, result._x);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java
new file mode 100644
index 0000000..44959aa
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestTimestampDeserialization.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestTimestampDeserialization
+    extends BaseMapTest
+{
+    // As for TestDateDeserialization except we don't need to test date conversion routines, so
+    // just check we pick up timestamp class
+
+    public void testTimestampUtil() throws Exception
+    {
+        long now = 123456789L;
+        java.sql.Timestamp value = new java.sql.Timestamp(now);
+
+        // First from long
+        assertEquals(value, new ObjectMapper().readValue(""+now, java.sql.Timestamp.class));
+
+        String dateStr = serializeTimestampAsString(value);
+        java.sql.Timestamp result = new ObjectMapper().readValue("\""+dateStr+"\"", java.sql.Timestamp.class);
+
+        assertEquals("Date: expect "+value+" ("+value.getTime()+"), got "+result+" ("+result.getTime()+")", value.getTime(), result.getTime());
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private String serializeTimestampAsString(java.sql.Timestamp value)
+    {
+        /* Then from String. This is bit tricky, since JDK does not really
+         * suggest a 'standard' format. So let's try using something...
+         */
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+        return df.format(value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
new file mode 100644
index 0000000..b134c4d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestUntypedDeserialization.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+
+/**
+ * Unit tests for verifying "old" data binding from JSON to JDK objects;
+ * one that only uses core JDK types; wrappers, Maps and Lists.
+ */
+public class TestUntypedDeserialization
+    extends com.fasterxml.jackson.test.BaseTest
+{
+    @SuppressWarnings("unchecked")
+    public void testSampleDoc()
+        throws Exception
+    {
+        final String JSON = SAMPLE_DOC_JSON_SPEC;
+
+        JsonFactory jf = new JsonFactory();
+
+        /* To get "untyped" Mapping (to Maps, Lists, instead of beans etc),
+         * we'll specify plain old Object.class as the target.
+         */
+        Object root = new ObjectMapper().readValue(jf.createParser(new StringReader(JSON)), Object.class);
+
+        assertType(root, Map.class);
+        Map<?,?> rootMap = (Map<?,?>) root;
+        assertEquals(1, rootMap.size());
+        Map.Entry<?,?> rootEntry =  rootMap.entrySet().iterator().next();
+        assertEquals("Image", rootEntry.getKey());
+        Object image = rootEntry.getValue();
+        assertType(image, Map.class);
+        Map<?,?> imageMap = (Map<?,?>) image;
+        assertEquals(5, imageMap.size());
+
+        Object value = imageMap.get("Width");
+        assertType(value, Integer.class);
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_WIDTH), value);
+
+        value = imageMap.get("Height");
+        assertType(value, Integer.class);
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_HEIGHT), value);
+
+        assertEquals(SAMPLE_SPEC_VALUE_TITLE, imageMap.get("Title"));
+
+        // Another Object, "thumbnail"
+        value = imageMap.get("Thumbnail");
+        assertType(value, Map.class);
+        Map<?,?> tnMap = (Map<?,?>) value;
+        assertEquals(3, tnMap.size());
+
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_HEIGHT), tnMap.get("Height"));
+        // for some reason, width is textual, not numeric...
+        assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, tnMap.get("Width"));
+        assertEquals(SAMPLE_SPEC_VALUE_TN_URL, tnMap.get("Url"));
+
+        // And then number list, "IDs"
+        value = imageMap.get("IDs");
+        assertType(value, List.class);
+        List<Object> ids = (List<Object>) value;
+        assertEquals(4, ids.size());
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID1), ids.get(0));
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID2), ids.get(1));
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID3), ids.get(2));
+        assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID4), ids.get(3));
+
+        // and that's all folks!
+    }
+
+    // [JACKSON-839]: allow 'upgrade' of big integers into Long, BigInteger
+    public void testObjectSerializeWithLong() throws IOException
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT, As.PROPERTY);
+        final long VALUE = 1337800584532L;
+
+        String serialized = "{\"timestamp\":"+VALUE+"}";
+        // works fine as node
+        JsonNode deserialized = mapper.readTree(serialized);
+        assertEquals(VALUE, deserialized.get("timestamp").asLong());
+        // and actually should work in Maps too
+        Map<?,?> deserMap = mapper.readValue(serialized, Map.class);
+        Number n = (Number) deserMap.get("timestamp");
+        assertNotNull(n);
+        assertSame(Long.class, n.getClass());
+        assertEquals(Long.valueOf(VALUE), n);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
new file mode 100644
index 0000000..706a1bc
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/deser/TestValueAnnotations.java
@@ -0,0 +1,397 @@
+package com.fasterxml.jackson.databind.deser;
+
+import java.io.IOException;
+import java.util.*;
+
+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.test.BaseTest;
+
+/**
+ * This unit test suite tests use of "value" Annotations;
+ * annotations that define actual type (Class) to use for
+ * deserialization.
+ */
+public class TestValueAnnotations
+    extends BaseTest
+{
+    /*
+    /**********************************************************
+    /* Annotated root classes for @JsonDeserialize#as
+    /**********************************************************
+     */
+
+    @JsonDeserialize(using=RootStringDeserializer.class)
+    interface RootString {
+        public String contents();
+    }
+
+    static class RootStringImpl implements RootString
+    {
+        final String _contents;
+
+        public RootStringImpl(String x) { _contents = x; }
+
+        @Override
+        public String contents() { return _contents; }
+        public String contents2() { return _contents; }
+    }
+
+    @JsonDeserialize(as=RootInterfaceImpl.class)
+    interface RootInterface {
+        public String getA();
+    }
+
+    static class RootInterfaceImpl implements RootInterface {
+        public String a;
+
+        public RootInterfaceImpl() { }
+
+        @Override
+        public String getA() { return a; }
+    }
+
+    @SuppressWarnings("serial")
+    @JsonDeserialize(contentAs=RootStringImpl.class)
+    static class RootMap extends HashMap<String,RootStringImpl> { }
+
+    @SuppressWarnings("serial")
+    @JsonDeserialize(contentAs=RootStringImpl.class)
+    static class RootList extends LinkedList<RootStringImpl> { }
+
+    @SuppressWarnings("serial")
+    static class RootStringDeserializer
+        extends StdDeserializer<RootString>
+    {
+        public RootStringDeserializer() { super(RootString.class); }
+
+        @Override
+        public RootString deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            if (jp.getCurrentToken() == JsonToken.VALUE_STRING) {
+                return new RootStringImpl(jp.getText());
+            }
+            throw ctxt.mappingException(_valueClass);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Annotated helper classes for @JsonDeserialize#as
+    /**********************************************************
+     */
+
+    /* Class for testing valid {@link JsonDeserialize} annotation
+     * with 'as' parameter to define concrete class to deserialize to
+     */
+    final static class CollectionHolder
+    {
+        Collection<String> _strings;
+
+        /* Default for 'Collection' would probably be ArrayList or so;
+         * let's try to make it a TreeSet instead.
+         */
+        @JsonDeserialize(as=TreeSet.class)
+        public void setStrings(Collection<String> s)
+        {
+            _strings = s;
+        }
+    }
+
+    /* Another class for testing valid {@link JsonDeserialize} annotation
+     * with 'as' parameter to define concrete class to deserialize to
+     */
+    final static class MapHolder
+    {
+        // Let's also coerce numbers into Strings here
+        Map<String,String> _data;
+
+        /* Default for 'Collection' would be HashMap,
+         * let's try to make it a TreeMap instead.
+         */
+        @JsonDeserialize(as=TreeMap.class)
+        public void setStrings(Map<String,String> s)
+        {
+            _data = s;
+        }
+    }
+
+    /* Another class for testing valid {@link JsonDeserialize} annotation
+     * with 'as' parameter, but with array
+     */
+    final static class ArrayHolder
+    {
+        String[] _strings;
+
+        @JsonDeserialize(as=String[].class)
+        public void setStrings(Object[] o)
+        {
+            // should be passed instances of proper type, as per annotation
+            _strings = (String[]) o;
+        }
+    }
+
+    /* Another class for testing broken {@link JsonDeserialize} annotation
+     * with 'as' parameter; one with incompatible type
+     */
+    final static class BrokenCollectionHolder
+    {
+        @JsonDeserialize(as=String.class) // not assignable to Collection
+        public void setStrings(Collection<String> s) { }
+    }
+
+    /*
+    /**********************************************************
+    /* Annotated helper classes for @JsonDeserialize.keyAs
+    /**********************************************************
+     */
+
+    final static class StringWrapper
+    {
+        final String _string;
+
+        public StringWrapper(String s) { _string = s; }
+    }
+
+    final static class MapKeyHolder
+    {
+        Map<Object, String> _map;
+
+        @JsonDeserialize(keyAs=StringWrapper.class)
+        public void setMap(Map<Object,String> m)
+        {
+            // type should be ok, but no need to cast here (won't matter)
+            _map = m;
+        }
+    }
+
+    final static class BrokenMapKeyHolder
+    {
+        // Invalid: Integer not a sub-class of String
+        @JsonDeserialize(keyAs=Integer.class)
+            public void setStrings(Map<String,String> m) { }
+    }
+
+    /*
+    /**********************************************************
+    /* Annotated helper classes for @JsonDeserialize#contentAs
+    /**********************************************************
+     */
+
+    final static class ListContentHolder
+    {
+        List<?> _list;
+
+        @JsonDeserialize(contentAs=StringWrapper.class)
+        public void setList(List<?> l) {
+            _list = l;
+        }
+    }
+
+    final static class InvalidContentClass
+    {
+        /* Such annotation not allowed, since it makes no sense;
+         * non-container classes have no contents to annotate (but
+         * note that it is possible to first use @JsonDesiarialize.as
+         * to mark Object as, say, a List, and THEN use
+         * @JsonDeserialize.contentAs!)
+         */
+        @JsonDeserialize(contentAs=String.class)
+            public void setValue(Object x) { }
+    }
+
+    final static class ArrayContentHolder
+    {
+        Object[] _data;
+
+        @JsonDeserialize(contentAs=Long.class)
+        public void setData(Object[] o)
+        { // should have proper type, but no need to coerce here
+            _data = o;
+        }
+    }
+
+    final static class MapContentHolder
+    {
+        Map<Object,Object> _map;
+
+        @JsonDeserialize(contentAs=Integer.class)
+        public void setMap(Map<Object,Object> m)
+        {
+            _map = m;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods for @JsonDeserialize#as
+    /**********************************************************
+     */
+
+    public void testOverrideClassValid() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        CollectionHolder result = m.readValue
+            ("{ \"strings\" : [ \"test\" ] }", CollectionHolder.class);
+
+        Collection<String> strs = result._strings;
+        assertEquals(1, strs.size());
+        assertEquals(TreeSet.class, strs.getClass());
+        assertEquals("test", strs.iterator().next());
+    }
+
+    public void testOverrideMapValid() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // note: expecting conversion from number to String, as well
+        MapHolder result = m.readValue
+            ("{ \"strings\" :  { \"a\" : 3 } }", MapHolder.class);
+
+        Map<String,String> strs = result._data;
+        assertEquals(1, strs.size());
+        assertEquals(TreeMap.class, strs.getClass());
+        String value = strs.get("a");
+        assertEquals("3", value);
+    }
+
+    public void testOverrideArrayClass() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ArrayHolder result = m.readValue
+            ("{ \"strings\" : [ \"test\" ] }", ArrayHolder.class);
+
+        String[] strs = result._strings;
+        assertEquals(1, strs.length);
+        assertEquals(String[].class, strs.getClass());
+        assertEquals("test", strs[0]);
+    }
+
+    public void testOverrideClassInvalid() throws Exception
+    {
+        // should fail due to incompatible Annotation
+        try {
+            BrokenCollectionHolder result = new ObjectMapper().readValue
+                ("{ \"strings\" : [ ] }", BrokenCollectionHolder.class);
+            fail("Expected a failure, but got results: "+result);
+        } catch (JsonMappingException jme) {
+            verifyException(jme, "is not assignable to");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods for @JsonDeserialize#as used for root values
+    /**********************************************************
+     */
+
+    public void testRootInterfaceAs() throws Exception
+    {
+        RootInterface value = new ObjectMapper().readValue("{\"a\":\"abc\" }", RootInterface.class);
+        assertTrue(value instanceof RootInterfaceImpl);
+        assertEquals("abc", value.getA());
+    }
+
+    public void testRootInterfaceUsing() throws Exception
+    {
+        RootString value = new ObjectMapper().readValue("\"xxx\"", RootString.class);
+        assertTrue(value instanceof RootString);
+        assertEquals("xxx", value.contents());
+    }
+
+    public void testRootListAs() throws Exception
+    {
+        RootMap value = new ObjectMapper().readValue("{\"a\":\"b\"}", RootMap.class);
+        assertEquals(1, value.size());
+        Object v2 = value.get("a");
+        assertEquals(RootStringImpl.class, v2.getClass());
+        assertEquals("b", ((RootString) v2).contents());
+    }
+
+    public void testRootMapAs() throws Exception
+    {
+        RootList value = new ObjectMapper().readValue("[ \"c\" ]", RootList.class);
+        assertEquals(1, value.size());
+        Object v2 = value.get(0);
+        assertEquals(RootStringImpl.class, v2.getClass());
+        assertEquals("c", ((RootString) v2).contents());
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods for @JsonDeserialize#keyAs
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+	public void testOverrideKeyClassValid() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MapKeyHolder result = m.readValue("{ \"map\" : { \"xxx\" : \"yyy\" } }", MapKeyHolder.class);
+        Map<StringWrapper, String> map = (Map<StringWrapper,String>)(Map<?,?>)result._map;
+        assertEquals(1, map.size());
+        Map.Entry<StringWrapper, String> en = map.entrySet().iterator().next();
+
+        StringWrapper key = en.getKey();
+        assertEquals(StringWrapper.class, key.getClass());
+        assertEquals("xxx", key._string);
+        assertEquals("yyy", en.getValue());
+    }
+
+    public void testOverrideKeyClassInvalid() throws Exception
+    {
+        // should fail due to incompatible Annotation
+        try {
+            BrokenMapKeyHolder result = new ObjectMapper().readValue
+                ("{ \"123\" : \"xxx\" }", BrokenMapKeyHolder.class);
+            fail("Expected a failure, but got results: "+result);
+        } catch (JsonMappingException jme) {
+            verifyException(jme, "is not assignable to");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods for @JsonDeserialize#contentAs
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+	public void testOverrideContentClassValid() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ListContentHolder result = m.readValue("{ \"list\" : [ \"abc\" ] }", ListContentHolder.class);
+        List<StringWrapper> list = (List<StringWrapper>)result._list;
+        assertEquals(1, list.size());
+        Object value = list.get(0);
+        assertEquals(StringWrapper.class, value.getClass());
+        assertEquals("abc", ((StringWrapper) value)._string);
+    }
+
+    public void testOverrideArrayContents() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        ArrayContentHolder result = m.readValue("{ \"data\" : [ 1, 2, 3 ] }",
+                                                ArrayContentHolder.class);
+        Object[] data = result._data;
+        assertEquals(3, data.length);
+        assertEquals(Long[].class, data.getClass());
+        assertEquals(1L, data[0]);
+        assertEquals(2L, data[1]);
+        assertEquals(3L, data[2]);
+    }
+
+    public void testOverrideMapContents() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MapContentHolder result = m.readValue("{ \"map\" : { \"a\" : 9 } }",
+                                                MapContentHolder.class);
+        Map<Object,Object> map = result._map;
+        assertEquals(1, map.size());
+        Object ob = map.values().iterator().next();
+        assertEquals(Integer.class, ob.getClass());
+        assertEquals(Integer.valueOf(9), ob);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java
new file mode 100644
index 0000000..a1ad5c0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java
@@ -0,0 +1,122 @@
+package com.fasterxml.jackson.databind.ext;
+
+import javax.xml.datatype.*;
+import javax.xml.namespace.QName;
+
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ext.CoreXMLDeserializers;
+
+/**
+ * Core XML types (javax.xml) are considered "external" (or more precisely "optional")
+ * since some Java(-like) platforms do not include them: specifically, Google AppEngine
+ * and Android seem to skimp on their inclusion. As such, they are dynamically loaded
+ * only as needed, and need bit special handling.
+ * 
+ * @author tatu
+ */
+public class TestCoreXMLTypes
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Serializer tests
+    /**********************************************************
+     */
+
+    public void testQNameSer() throws Exception
+    {
+        QName qn = new QName("http://abc", "tag", "prefix");
+        assertEquals(quote(qn.toString()), serializeAsString(qn));
+    }
+
+    public void testDurationSer() throws Exception
+    {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        // arbitrary value
+        Duration dur = dtf.newDurationDayTime(false, 15, 19, 58, 1);
+        assertEquals(quote(dur.toString()), serializeAsString(dur));
+    }
+
+    public void testXMLGregorianCalendarSerAndDeser() throws Exception
+    {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        XMLGregorianCalendar cal = dtf.newXMLGregorianCalendar
+            (1974, 10, 10, 18, 15, 17, 123, 0);
+        /* Due to [JACKSON-308], 1.6 will use configurable Date serialization;
+         * and it defaults to using timestamp. So let's try couple of combinations.
+         */
+        ObjectMapper mapper = new ObjectMapper();
+        long timestamp = cal.toGregorianCalendar().getTimeInMillis();
+        String numStr = String.valueOf(timestamp);
+        assertEquals(numStr, mapper.writeValueAsString(cal));
+
+        // [JACKSON-403] Needs to come back ok as well:
+        XMLGregorianCalendar calOut = mapper.readValue(numStr, XMLGregorianCalendar.class);
+        assertNotNull(calOut);
+        assertEquals(timestamp, calOut.toGregorianCalendar().getTimeInMillis());
+
+        // and then textual variant
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        // this is ALMOST same as default for XMLGregorianCalendar... just need to unify Z/+0000
+        String exp = cal.toXMLFormat();
+        String act = mapper.writeValueAsString(cal);
+        act = act.substring(1, act.length() - 1); // remove quotes
+        exp = removeZ(exp);
+        act = removeZ(act);
+        assertEquals(exp, act);
+    }
+
+    private String removeZ(String dateStr) {
+        if (dateStr.endsWith("Z")) {
+            return dateStr.substring(0, dateStr.length()-1);
+        }
+        if (dateStr.endsWith("+0000")) {
+            return dateStr.substring(0, dateStr.length()-5);
+        }
+        return dateStr;
+    }
+    
+    /*
+    /**********************************************************
+    /* Deserializer tests
+    /**********************************************************
+     */
+    
+    // First things first: must be able to load the deserializers...
+    public void testDeserializerLoading()
+    {
+        new CoreXMLDeserializers.DurationDeserializer();
+        new CoreXMLDeserializers.GregorianCalendarDeserializer();
+        new CoreXMLDeserializers.QNameDeserializer();
+    }
+
+    public void testQNameDeser() throws Exception
+    {
+        QName qn = new QName("http://abc", "tag", "prefix");
+        String qstr = qn.toString();
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("Should deserialize to equal QName (exp serialization: '"+qstr+"')",
+                     qn, mapper.readValue(quote(qstr), QName.class));
+    }
+
+    public void testCalendarDeser() throws Exception
+    {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        XMLGregorianCalendar cal = dtf.newXMLGregorianCalendar
+            (1974, 10, 10, 18, 15, 17, 123, 0);
+        String exp = cal.toXMLFormat();
+        assertEquals("Should deserialize to equal XMLGregorianCalendar ('"+exp+"')", cal,
+                new ObjectMapper().readValue(quote(exp), XMLGregorianCalendar.class));
+    }
+
+    public void testDurationDeser() throws Exception
+    {
+        DatatypeFactory dtf = DatatypeFactory.newInstance();
+        // arbitrary value, like... say, 27d5h15m59s
+        Duration dur = dtf.newDurationDayTime(true, 27, 5, 15, 59);
+        String exp = dur.toString();
+        assertEquals("Should deserialize to equal Duration ('"+exp+"')", dur,
+                new ObjectMapper().readValue(quote(exp), Duration.class));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java
new file mode 100644
index 0000000..2938d20
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestDOM.java
@@ -0,0 +1,101 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.io.StringReader;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.xml.sax.InputSource;
+import org.w3c.dom.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestDOM extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final static String SIMPLE_XML =
+        "<root attr='3'><leaf>Rock & Roll!</leaf><?proc instr?></root>";
+    final static String SIMPLE_XML_NS =
+        "<root ns:attr='abc' xmlns:ns='http://foo' />";
+    
+    public void testSerializeSimpleNonNS() throws Exception
+    {
+        // Let's just parse first, easiest
+        Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse
+            (new InputSource(new StringReader(SIMPLE_XML)));
+        assertNotNull(doc);
+        ObjectMapper mapper = new ObjectMapper();
+        // need to strip xml declaration, if any
+        String outputRaw = mapper.writeValueAsString(doc);
+        // And re-parse as String, since JSON has quotes...
+        String output = mapper.readValue(outputRaw, String.class);
+        /* ... and finally, normalize to (close to) canonical XML
+         * output (single vs double quotes, xml declaration etc)
+         */
+        assertEquals(SIMPLE_XML, normalizeOutput(output));
+    }
+
+    public void testDeserializeNonNS() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        for (int i = 0; i < 2; ++i) {
+            Document doc;
+
+            if (i == 0) {
+                // First, as Document:
+                doc = mapper.readValue(quote(SIMPLE_XML), Document.class);
+            } else {
+                // and then as plain Node (no difference)
+                Node node = mapper.readValue(quote(SIMPLE_XML), Node.class);
+                doc = (Document) node;
+            }
+            Element root = doc.getDocumentElement();
+            assertNotNull(root);
+            // non-ns, simple...
+            assertEquals("root", root.getTagName());
+            assertEquals("3", root.getAttribute("attr"));
+            assertEquals(1, root.getAttributes().getLength());
+            NodeList nodes = root.getChildNodes();
+            assertEquals(2, nodes.getLength());
+            Element leaf = (Element) nodes.item(0);
+            assertEquals("leaf", leaf.getTagName());
+            assertEquals(0, leaf.getAttributes().getLength());
+            //"<root attr='3'><leaf>Rock & Roll!</leaf><?proc instr?></root>";
+            ProcessingInstruction pi = (ProcessingInstruction) nodes.item(1);
+            assertEquals("proc", pi.getTarget());
+            assertEquals("instr", pi.getData());
+        }
+    }
+    
+    public void testDeserializeNS() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Document doc = mapper.readValue(quote(SIMPLE_XML_NS), Document.class);
+        Element root = doc.getDocumentElement();
+        assertNotNull(root);
+        assertEquals("root", root.getTagName());
+        // Not sure if it ought to be "" or null...
+        String uri = root.getNamespaceURI();
+        assertTrue((uri == null) || "".equals(uri));
+        // no child nodes:
+        assertEquals(0, root.getChildNodes().getLength());
+        // DOM is weird, includes ns decls as attributes...
+        assertEquals(2, root.getAttributes().getLength());
+        assertEquals("abc", root.getAttributeNS("http://foo", "attr"));
+    }
+
+    /*
+     **********************************************************
+     * Helper methods
+     **********************************************************
+     */
+
+    protected static String normalizeOutput(String output)
+    {
+        // XML declaration to get rid of?
+        output = output.trim();
+        if (output.startsWith("<?xml")) {
+            // can find closing '>' of xml decl...
+            output = output.substring(output.indexOf('>')+1).trim();
+        }
+        // And replace double quotes with single-quotes...
+        return output.replace('"', '\'');
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ext/TestJDK16OnlyTypes.java b/src/test/java/com/fasterxml/jackson/databind/ext/TestJDK16OnlyTypes.java
new file mode 100644
index 0000000..d7197f4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ext/TestJDK16OnlyTypes.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.databind.ext;
+
+import java.util.Deque;
+import java.util.NavigableSet;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Tests to ensure that we can handle 1.6-only types, even if
+ * registrations are done without direct refs
+ */
+public class TestJDK16OnlyTypes extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    // for [Issue#216]
+    public void test16Types() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        Deque<?> dq = mapper.readValue("[1]", Deque.class);
+        assertNotNull(dq);
+        assertEquals(1, dq.size());
+        assertTrue(dq instanceof Deque<?>);
+
+        NavigableSet<?> ns = mapper.readValue("[ true ]", NavigableSet.class);
+        assertEquals(1, ns.size());
+        assertTrue(ns instanceof NavigableSet<?>);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestIgnorePropsForSerialization.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestIgnorePropsForSerialization.java
new file mode 100644
index 0000000..51848ee
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestIgnorePropsForSerialization.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.filter;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestIgnorePropsForSerialization
+    extends BaseMapTest
+{
+    /*
+    /****************************************************************
+    /* Helper classes
+    /****************************************************************
+     */
+
+    @JsonIgnoreProperties({"b", "c"})
+    static class IgnoreSome
+    {
+        public int a = 3;
+        public String b = "x";
+
+        public int getC() { return -6; }
+        public String getD() { return "abc"; }
+    }
+
+    @SuppressWarnings("serial")
+    @JsonIgnoreProperties({"@class"})
+    static class MyMap extends HashMap<String,String> { }
+
+    // [JACKSON-787]: allow use of @JsonIgnoreProperties for properties
+    static class WrapperWithPropIgnore
+    {
+        @JsonIgnoreProperties("y")
+        public XY value = new XY();
+    }
+
+    static class XY {
+        public int x = 1;
+        public int y = 2;
+    }
+
+    static class WrapperWithPropIgnore2
+    {
+        @JsonIgnoreProperties("z")
+        public XYZ value = new XYZ();
+    }
+
+    @JsonIgnoreProperties({"x"})
+    static class XYZ {
+        public int x = 1;
+        public int y = 2;
+        public int z = 3;
+    }
+
+    static class MapWrapper {
+        @JsonIgnoreProperties({"a"})
+        public final HashMap<String,Integer> value = new HashMap<String,Integer>();
+        {
+            value.put("a", 1);
+            value.put("b", 2);
+        }
+    }
+    
+    /*
+    /****************************************************************
+    /* Unit tests
+    /****************************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testExplicitIgnoralWithBean() throws Exception
+    {
+        IgnoreSome value = new IgnoreSome();
+        Map<String,Object> result = writeAndMap(MAPPER, value);
+        assertEquals(2, result.size());
+        // verify that specified fields are ignored
+        assertFalse(result.containsKey("b"));
+        assertFalse(result.containsKey("c"));
+        // and that others are not
+        assertEquals(Integer.valueOf(value.a), result.get("a"));
+        assertEquals(value.getD(), result.get("d"));
+    }
+
+    public void testExplicitIgnoralWithMap() throws Exception
+    {
+        // test simulating need to filter out metadata like class name
+        MyMap value = new MyMap();
+        value.put("a", "b");
+        value.put("@class", MyMap.class.getName());
+        Map<String,Object> result = writeAndMap(MAPPER, value);
+        assertEquals(1, result.size());
+        // verify that specified field is ignored
+        assertFalse(result.containsKey("@class"));
+        // and that others are not
+        assertEquals(value.get("a"), result.get("a"));
+    }
+
+    public void testIgnoreViaOnlyProps() throws Exception
+    {
+        assertEquals("{\"value\":{\"x\":1}}",
+                MAPPER.writeValueAsString(new WrapperWithPropIgnore()));
+    }
+
+    public void testIgnoreWithMapProperty() throws Exception
+    {
+        assertEquals("{\"value\":{\"b\":2}}", MAPPER.writeValueAsString(new MapWrapper()));
+    }
+    
+    public void testIgnoreViaPropsAndClass() throws Exception
+    {
+        assertEquals("{\"value\":{\"y\":2}}",
+                MAPPER.writeValueAsString(new WrapperWithPropIgnore2()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java
new file mode 100644
index 0000000..c292fbc
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestSimpleSerializationIgnore.java
@@ -0,0 +1,128 @@
+package com.fasterxml.jackson.databind.filter;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite tests use of {@link JsonIgnore} annotations
+ * with  bean serialization; as well as (since 1.7)
+ * {@link JsonIgnoreType}.
+ */
+public class TestSimpleSerializationIgnore
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    /// Class for testing enabled {@link JsonIgnore} annotation
+    final static class SizeClassEnabledIgnore
+    {
+        @JsonIgnore public int getY() { return 9; }
+
+        // note: must be public to be seen
+        public int getX() { return 1; }
+
+        @JsonIgnore public int getY2() { return 1; }
+        @JsonIgnore public int getY3() { return 2; }
+    }
+
+    /// Class for testing disabled {@link JsonIgnore} annotation
+    final static class SizeClassDisabledIgnore
+    {
+        // note: must be public to be seen
+        public int getX() { return 3; }
+        @JsonIgnore(false) public int getY() { return 4; }
+    }
+
+    static class BaseClassIgnore
+    {
+        @JsonProperty("x")
+        @JsonIgnore
+        public int x() { return 1; }
+
+        public int getY() { return 2; }
+    }
+
+    static class SubClassNonIgnore
+        extends BaseClassIgnore
+    {
+        /* Annotations to disable ignorance, in sub-class; note that
+         * we must still get "JsonProperty" fro super class
+         */
+        @Override
+        @JsonIgnore(false)
+        public int x() { return 3; }
+    }
+
+    @JsonIgnoreType
+    static class IgnoredType { }
+
+    @JsonIgnoreType(false)
+    static class NonIgnoredType
+    {
+        public int value = 13;
+        
+        public IgnoredType ignored = new IgnoredType();
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimpleIgnore() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // Should see "x", not "y"
+        Map<String,Object> result = writeAndMap(m, new SizeClassEnabledIgnore());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(1), result.get("x"));
+        assertNull(result.get("y"));
+    }
+
+    public void testDisabledIgnore() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // Should see "x" and "y"
+        Map<String,Object> result = writeAndMap(m, new SizeClassDisabledIgnore());
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+        assertEquals(Integer.valueOf(4), result.get("y"));
+    }
+
+    /**
+     * Test case to verify that ignore tag can also be disabled
+     * via inheritance
+     */
+    public void testIgnoreOver() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+
+        // should only see "y"
+        Map<String,Object> result = writeAndMap(m, new BaseClassIgnore());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(2), result.get("y"));
+
+        // Should see "x" and "y"
+        result = writeAndMap(m, new SubClassNonIgnore());
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+        assertEquals(Integer.valueOf(2), result.get("y"));
+    }
+
+    /**
+     * @since 1.7
+     */
+    public void testIgnoreType() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals("{\"value\":13}", m.writeValueAsString(new NonIgnoredType()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
new file mode 100644
index 0000000..c3465d3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/filter/TestUnknownPropertyDeserialization.java
@@ -0,0 +1,272 @@
+package com.fasterxml.jackson.databind.filter;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
+
+/**
+ * Unit tests for checking handling of unknown properties
+ */
+public class TestUnknownPropertyDeserialization
+    extends BaseMapTest
+{
+    final static String JSON_UNKNOWN_FIELD = "{ \"a\" : 1, \"foo\" : [ 1, 2, 3], \"b\" : -1 }";
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    final static class TestBean
+    {
+        String _unknown;
+
+        int _a, _b;
+
+        public TestBean() { }
+
+        public void setA(int a) { _a = a; }
+        public void setB(int b) { _b = b; }
+
+        public void markUnknown(String unk) { _unknown = unk; }
+    }
+
+    /**
+     * Simple {@link DeserializationProblemHandler} sub-class that
+     * just marks unknown property/ies when encountered, along with
+     * Json value of the property.
+     */
+    final static class MyHandler
+        extends DeserializationProblemHandler
+    {
+        @Override
+        public boolean handleUnknownProperty(DeserializationContext ctxt,
+                JsonParser jp, JsonDeserializer<?> deserializer,
+                Object bean, String propertyName)
+            throws IOException, JsonProcessingException
+        {
+            // very simple, just to verify that we do see correct token type
+            ((TestBean) bean).markUnknown(propertyName+":"+jp.getCurrentToken().toString());
+            // Yup, we are good to go; must skip whatever value we'd have:
+            jp.skipChildren();
+            return true;
+        }
+    }
+
+    @JsonIgnoreProperties({"b", "c"})
+    static class IgnoreSome
+    {
+        public int a, b;
+        private String c, d;
+
+        public IgnoreSome() { }
+
+        public String c() { return c; }
+        public void setC(String value) { c = value; }
+        public String d() { return d; }
+        public void setD(String value) { d = value; }
+    }
+
+    @JsonIgnoreProperties(ignoreUnknown=true)
+    static class IgnoreUnknown {
+        public int a;
+    }
+
+    @SuppressWarnings("serial")
+    @JsonIgnoreProperties({"a", "d"})
+    static class IgnoreMap extends HashMap<String,Object> { }
+
+    static class ImplicitIgnores {
+        @JsonIgnore public int a;
+        @JsonIgnore public void setB(int b) { }
+        public int c;
+    }
+
+    // // Ignored as per [JACKSON-787]
+
+    static class XYZWrapper1 {
+        @JsonIgnoreProperties({"x"})
+        public YZ value;
+    }
+
+    static class YZ {
+        public int y, z;
+    }
+
+    static class XYZWrapper2 {
+        @JsonIgnoreProperties({"y"})
+        public X value;
+    }
+
+    @JsonIgnoreProperties({"z"})
+    static class X {
+        public int x;
+    }
+
+    static class MapWithoutX {
+        @JsonIgnoreProperties("x")
+        public Map<String,Integer> values;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * By default we should just get an exception if an unknown property
+     * is encountered
+     */
+    public void testUnknownHandlingDefault()
+        throws Exception
+    {
+        try {
+            MAPPER.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
+        } catch (JsonMappingException jex) {
+            verifyException(jex, "Unrecognized field \"foo\"");
+        }
+    }
+
+    /**
+     * Test that verifies that it is possible to ignore unknown properties using
+     * {@link DeserializationProblemHandler}.
+     */
+    public void testUnknownHandlingIgnoreWithHandler()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.clearProblemHandlers();
+        mapper.addHandler(new MyHandler());
+        TestBean result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
+        assertNotNull(result);
+        assertEquals(1, result._a);
+        assertEquals(-1, result._b);
+        assertEquals("foo:START_ARRAY", result._unknown);
+    }
+
+    /**
+     * Test that verifies that it is possible to ignore unknown properties using
+     * {@link DeserializationProblemHandler} and an ObjectReader.
+     */
+    public void testUnknownHandlingIgnoreWithHandlerAndObjectReader()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.clearProblemHandlers();
+        TestBean result = mapper.reader(TestBean.class).withHandler(new MyHandler()).readValue(new StringReader(JSON_UNKNOWN_FIELD));
+        assertNotNull(result);
+        assertEquals(1, result._a);
+        assertEquals(-1, result._b);
+        assertEquals("foo:START_ARRAY", result._unknown);
+    }
+
+    /**
+     * Test for checking that it is also possible to simply suppress
+     * error reporting for unknown properties.
+     */
+    public void testUnknownHandlingIgnoreWithFeature()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        TestBean result = null;
+        try {
+            result = mapper.readValue(new StringReader(JSON_UNKNOWN_FIELD), TestBean.class);
+        } catch (JsonMappingException jex) {
+            fail("Did not expect a problem, got: "+jex.getMessage());
+        }
+        assertNotNull(result);
+        assertEquals(1, result._a);
+        assertNull(result._unknown);
+        assertEquals(-1, result._b);
+    }
+
+    public void testWithClassIgnore()
+        throws Exception
+    {
+        IgnoreSome result = MAPPER.readValue("{ \"a\":1,\"b\":2,\"c\":\"x\",\"d\":\"y\"}",
+                IgnoreSome.class);
+        // first: should deserialize 2 of properties normally
+        assertEquals(1, result.a);
+        assertEquals("y", result.d());
+        // and not take other 2
+        assertEquals(0, result.b);
+        assertNull(result.c());
+    }
+
+    /// @since 1.4
+    public void testClassIgnoreWithMap() throws Exception
+    {
+        // Let's actually use incompatible types for "a" and "d"; should not matter when ignored
+        IgnoreMap result = MAPPER.readValue
+            ("{ \"a\":[ 1],\n"
+                +"\"b\":2,\n"
+                +"\"c\": \"x\",\n"
+                +"\"d\":false }", IgnoreMap.class);
+        assertEquals(2, result.size());
+        Object ob = result.get("b");
+        assertEquals(Integer.class, ob.getClass());
+        assertEquals(Integer.valueOf(2), ob);
+        assertEquals("x", result.get("c"));
+        assertFalse(result.containsKey("a"));
+        assertFalse(result.containsKey("d"));
+    }
+
+    public void testClassWithIgnoreUnknown() throws Exception
+    {
+        IgnoreUnknown result = MAPPER.readValue
+            ("{\"b\":3,\"c\":[1,2],\"x\":{ },\"a\":-3}", IgnoreUnknown.class);
+        assertEquals(-3, result.a);
+    }
+
+    /**
+     * Test that verifies that use of {@link JsonIgnore} will add implicit
+     * skipping of matching properties.
+     */
+    public void testClassWithUnknownAndIgnore() throws Exception
+    {
+        // should be ok: "a" and "b" ignored, "c" mapped:
+        ImplicitIgnores result = MAPPER.readValue
+            ("{\"a\":1,\"b\":2,\"c\":3 }", ImplicitIgnores.class);
+        assertEquals(3, result.c);
+
+        // but "d" is not defined, so should still error
+        try {
+            MAPPER.readValue("{\"a\":1,\"b\":2,\"c\":3,\"d\":4 }", ImplicitIgnores.class);            
+        } catch (JsonMappingException e) {
+            verifyException(e, "Unrecognized field \"d\"");
+        }
+    }
+
+    public void testPropertyIgnoral() throws Exception
+    {
+        XYZWrapper1 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}", XYZWrapper1.class);
+        assertEquals(2, result.value.y);
+        assertEquals(3, result.value.z);
+    }
+
+    public void testPropertyIgnoralWithClass() throws Exception
+    {
+        XYZWrapper2 result = MAPPER.readValue("{\"value\":{\"y\":2,\"x\":1,\"z\":3}}", XYZWrapper2.class);
+        assertEquals(1, result.value.x);
+    }
+
+    public void testPropertyIgnoralForMap() throws Exception
+    {
+        MapWithoutX result = MAPPER.readValue("{\"values\":{\"x\":1,\"y\":2}}", MapWithoutX.class);
+        assertNotNull(result.values);
+        assertEquals(1, result.values.size());
+        assertEquals(Integer.valueOf(2), result.values.get("y"));
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java
new file mode 100644
index 0000000..f890325
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestCglibUsage.java
@@ -0,0 +1,75 @@
+package com.fasterxml.jackson.databind.interop;
+
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * Unit test for checking that we can serialize CGLib generated proxies.
+ */
+public class TestCglibUsage
+    extends BaseTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    interface BeanInterface {
+        public int getX();
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    public void testSimpleProxied() throws Exception
+    {
+        Enhancer enh = new Enhancer();
+        enh.setInterfaces(new Class[] { BeanInterface.class });
+        enh.setCallback(new MethodInterceptor() {
+            @Override
+            public Object intercept(Object obj, Method method,
+                                        Object[] args, MethodProxy proxy)
+                    throws Throwable
+                {
+                    if ("getX".equals(method.getName ())) {
+                        return Integer.valueOf(13);
+                    }
+                    return proxy.invokeSuper(obj, args);
+                }
+            });
+        BeanInterface bean = (BeanInterface) enh.create();
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(13), result.get("x"));
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+	private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
+        throws IOException
+    {
+        StringWriter sw = new StringWriter();
+        m.writeValue(sw, value);
+        return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestExternalizable.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestExternalizable.java
new file mode 100644
index 0000000..6645df2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestExternalizable.java
@@ -0,0 +1,245 @@
+package com.fasterxml.jackson.databind.interop;
+
+import java.io.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Simple test to ensure that we can make POJOs use Jackson
+ * for JDK serialization, via {@link Externalizable}
+ * 
+ * @since 2.1
+ */
+public class TestExternalizable extends BaseMapTest
+{
+    /* Not pretty, but needed to make ObjectMapper accessible from
+     * static context (alternatively could use ThreadLocal).
+     */
+    static class MapperHolder {
+        private final ObjectMapper mapper = new ObjectMapper();
+        private final static MapperHolder instance = new MapperHolder();
+        public static ObjectMapper mapper() { return instance.mapper; }
+    }
+
+    /**
+     * Helper class we need to adapt {@link ObjectOutput} as
+     * {@link OutputStream}
+     */
+    final static class ExternalizableInput extends InputStream
+    {
+        private final ObjectInput in;
+
+        public ExternalizableInput(ObjectInput in) {
+            this.in = in;
+        }
+
+        @Override
+        public int available() throws IOException {
+            return in.available();
+        }
+        
+        @Override
+        public void close() throws IOException {
+            in.close();
+        }
+        
+        @Override
+        public boolean  markSupported() {
+            return false;
+        }
+
+        @Override
+        public int read() throws IOException {
+            return in.read();
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return in.read(buffer);
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int len) throws IOException {
+            return in.read(buffer, offset, len);
+        }
+        
+        @Override
+        public long skip(long n) throws IOException {
+            return in.skip(n);
+        }
+    }        
+
+    /**
+     * Helper class we need to adapt {@link ObjectOutput} as
+     * {@link OutputStream}
+     */
+    final static class ExternalizableOutput extends OutputStream
+    {
+        private final ObjectOutput out;
+
+        public ExternalizableOutput(ObjectOutput out) {
+            this.out = out;
+        }
+        
+        @Override
+        public void flush() throws IOException {
+            out.flush();
+        }
+
+        @Override
+        public void close() throws IOException {
+            out.close();
+        }
+        
+        @Override
+        public void write(int ch) throws IOException {
+            out.write(ch);
+        }
+
+        @Override
+        public void write(byte[] data) throws IOException {
+            out.write(data);
+        }
+        
+        @Override
+        public void write(byte[] data, int offset, int len) throws IOException {
+            out.write(data, offset, len);
+        }
+    }
+    
+//    @com.fasterxml.jackson.annotation.JsonFormat(shape=com.fasterxml.jackson.annotation.JsonFormat.Shape.ARRAY)
+    static class MyPojo implements Externalizable
+    {
+        public int id;
+        public String name;
+        public int[] values;
+
+        public MyPojo() { } // for deserialization
+        public MyPojo(int id, String name, int[] values)
+        {
+            this.id = id;
+            this.name = name;
+            this.values = values;
+        }
+
+        @Override
+        public void readExternal(ObjectInput in) throws IOException
+        {
+//            MapperHolder.mapper().readValue(
+            MapperHolder.mapper().readerForUpdating(this).readValue(new ExternalizableInput(in));
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput oo) throws IOException
+        {
+            MapperHolder.mapper().writeValue(new ExternalizableOutput(oo), this);
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o == this) return true;
+            if (o == null) return false;
+            if (o.getClass() != getClass()) return false;
+            
+            MyPojo other = (MyPojo) o;
+            
+            if (other.id != id) return false;
+            if (!other.name.equals(name)) return false;
+            
+            if (other.values.length != values.length) return false;
+            for (int i = 0, end = values.length; i < end; ++i) {
+                if (values[i] != other.values[i]) return false;
+            }
+            return true;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Actual tests
+    /**********************************************************
+     */
+
+    // Comparison, using JDK native
+    static class MyPojoNative implements Serializable
+    {
+        private static final long serialVersionUID = 1L;
+
+        public int id;
+        public String name;
+        public int[] values;
+
+        public MyPojoNative(int id, String name, int[] values)
+        {
+            this.id = id;
+            this.name = name;
+            this.values = values;
+        }
+    }
+    
+    @SuppressWarnings("unused")
+    public void testSerializeAsExternalizable() throws Exception
+    {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        ObjectOutputStream obs = new ObjectOutputStream(bytes);
+        final MyPojo input = new MyPojo(13, "Foobar", new int[] { 1, 2, 3 } );
+        obs.writeObject(input);
+        obs.close();
+        byte[] ser = bytes.toByteArray();
+
+        // Ok: just verify it contains stuff it should
+        byte[] json = MapperHolder.mapper().writeValueAsBytes(input);
+
+        int ix = indexOf(ser, json);
+        if (ix < 0) {
+            fail("Serialization ("+ser.length+") does NOT contain JSON (of "+json.length+")");
+        }
+        
+        // Sanity check:
+        if (false) {
+            bytes = new ByteArrayOutputStream();
+            obs = new ObjectOutputStream(bytes);
+            MyPojoNative p = new MyPojoNative(13, "Foobar", new int[] { 1, 2, 3 } );
+            obs.writeObject(p);
+            obs.close();
+            System.out.println("Native size: "+bytes.size()+", vs JSON: "+ser.length);
+        }
+        
+        // then read back!
+        ObjectInputStream ins = new ObjectInputStream(new ByteArrayInputStream(ser));
+        MyPojo output = (MyPojo) ins.readObject();
+        ins.close();
+        assertNotNull(output);
+        
+        assertEquals(input, output);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private int indexOf(byte[] full, byte[] fragment)
+    {
+        final byte first = fragment[0];
+        for (int i = 0, end = full.length-fragment.length; i < end; ++i) {
+            if (full[i] != first) continue;
+            if (matches(full, i, fragment)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private boolean matches(byte[] full, int index, byte[] fragment)
+    {
+        for (int i = 1, end = fragment.length; i < end; ++i) {
+            if (fragment[i] != full[index+i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
new file mode 100644
index 0000000..b6234f2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestFormatDetection.java
@@ -0,0 +1,47 @@
+package com.fasterxml.jackson.databind.interop;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestFormatDetection extends BaseMapTest
+{
+    private final ObjectReader READER = objectReader();
+
+    static class POJO {
+        public int x, y;
+        
+        public POJO() { }
+        public POJO(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testSimpleWithJSON() throws Exception
+    {
+        ObjectReader detecting = READER.withType(POJO.class);
+        detecting = detecting.withFormatDetection(detecting);
+        POJO pojo = detecting.readValue(utf8Bytes("{\"x\":1}"));
+        assertNotNull(pojo);
+        assertEquals(1, pojo.x);
+    }
+
+    public void testInvalid() throws Exception
+    {
+        ObjectReader detecting = READER.withType(POJO.class);
+        detecting = detecting.withFormatDetection(detecting);
+        try {
+            detecting.readValue(utf8Bytes("<POJO><x>1</x></POJO>"));
+            fail("Should have failed");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Can not detect format from input");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestGroovyBeans.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestGroovyBeans.java
new file mode 100644
index 0000000..d0a874b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestGroovyBeans.java
@@ -0,0 +1,76 @@
+package com.fasterxml.jackson.databind.interop;
+
+import java.util.*;
+
+import groovy.lang.GroovyClassLoader;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Basic tests to see that simple Groovy beans can be serialized
+ * and deserialized
+ */
+public class TestGroovyBeans
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    final static String SIMPLE_POGO = 
+        "public class GBean {\n"
+        +"long id = 3;\n"
+        +"String name = \"whome\";\n"
+        +"}";
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimpleSerialization() throws Exception
+    {
+        Object ob = newGroovyObject(SIMPLE_POGO);
+        Map<String,Object> result = writeAndMap(MAPPER, ob);
+        assertEquals(2, result.size());
+        assertEquals("whome", result.get("name"));
+        /* 26-Nov-2009, tatu: Strange... Groovy seems to decide
+         *    'long' means 'int'... Oh well.
+         */
+        Object num = result.get("id");
+        assertNotNull(num);
+        assertTrue(num instanceof Number);
+        assertEquals(3, ((Number) num).intValue());
+    }
+
+    public void testSimpleDeserialization() throws Exception
+    {
+        Class<?> cls = defineGroovyClass(SIMPLE_POGO);
+        // First: deserialize from data
+        Object pogo = MAPPER.readValue("{\"id\":9,\"name\":\"Bob\"}", cls);
+        assertNotNull(pogo);
+        /* Hmmh. Could try to access using Reflection, or by defining
+         * a Java interface it implements. Or, maybe simplest, just
+         * re-serialize and see what we got.
+         */
+        Map<String,Object> result = writeAndMap(MAPPER, pogo);
+        assertEquals(2, result.size());
+        assertEquals("Bob", result.get("name"));
+        // as per earlier, we just get a number...
+        Object num = result.get("id");
+        assertNotNull(num);
+        assertTrue(num instanceof Number);
+        assertEquals(9, ((Number) num).intValue());
+    }
+
+    /*
+    *************************************************
+    * Helper methods
+    *************************************************
+    */
+
+    protected Class<?> defineGroovyClass(String src) throws Exception
+    {
+        return new GroovyClassLoader(getClass().getClassLoader()).parseClass(src);
+
+    }
+
+    protected Object newGroovyObject(String src) throws Exception
+    {
+        Class<?> cls = defineGroovyClass(src);
+        return cls.newInstance();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java
new file mode 100644
index 0000000..67bdbdb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestHibernate.java
@@ -0,0 +1,85 @@
+package com.fasterxml.jackson.databind.interop;
+
+
+import java.io.*;
+import java.lang.reflect.Method;
+import java.util.*;
+
+import org.hibernate.repackage.cglib.proxy.Enhancer;
+import org.hibernate.repackage.cglib.proxy.MethodInterceptor;
+import org.hibernate.repackage.cglib.proxy.MethodProxy;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * Basic tests covering Hibernate-compatibility features.
+ */
+public class TestHibernate
+    extends BaseTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    interface BeanInterfaceHib {
+        public int getX();
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    /*
+     * Unit test to test [JACKSON-177]
+     */
+    public void testHibernateCglib() throws Exception
+    {
+        /* 03-Sep-2010, tatu: This often fails form Eclipse (on some platforms like Mac OS X),
+         *   so let's only run it from Ant/CLI
+         */
+        if (!runsFromAnt()) {
+            return;
+        }
+
+        Enhancer enh = new Enhancer();
+        enh.setInterfaces(new Class[] { BeanInterfaceHib.class });
+        enh.setCallback(new MethodInterceptor() {
+            @Override
+            public Object intercept(Object obj, Method method,
+                    Object[] args, MethodProxy proxy)
+                            throws Throwable
+            {
+                if ("getX".equals(method.getName ())) {
+                    return Integer.valueOf(13);
+                }
+                return proxy.invokeSuper(obj, args);
+            }
+        });
+        BeanInterfaceHib bean = (BeanInterfaceHib) enh.create();
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(13), result.get("x"));
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
+        throws IOException
+    {
+        StringWriter sw = new StringWriter();
+        m.writeValue(sw, value);
+        return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/interop/TestJDKProxy.java b/src/test/java/com/fasterxml/jackson/databind/interop/TestJDKProxy.java
new file mode 100644
index 0000000..99a7694
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/interop/TestJDKProxy.java
@@ -0,0 +1,73 @@
+package com.fasterxml.jackson.databind.interop;
+
+import java.lang.reflect.*;
+
+import com.fasterxml.jackson.databind.*;
+
+// mostly for [Issue#57]
+public class TestJDKProxy extends BaseMapTest
+{
+    final ObjectMapper MAPPER = new ObjectMapper();
+
+    public interface IPlanet {
+        String getName();
+        String setName(String s);
+    }
+
+    // bit silly example; usually wouldn't implement interface (no need to proxy if it did)
+    static class Planet implements IPlanet {
+        private String name;
+
+        public Planet() { }
+        public Planet(String s) { name = s; }
+        
+        @Override
+        public String getName(){return name;}
+        @Override
+        public String setName(String iName) {name = iName;
+            return name;
+        }
+    }    
+    
+    /*
+    /********************************************************
+    /* Test methods
+    /********************************************************
+     */
+    
+    public void testSimple() throws Exception
+    {
+        IPlanet input = getProxy(IPlanet.class, new Planet("Foo"));
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals("{\"name\":\"Foo\"}", json);
+        
+        // and just for good measure
+        Planet output = MAPPER.readValue(json, Planet.class);
+        assertEquals("Foo", output.getName());
+    }
+
+    /*
+    /********************************************************
+    /* Helper methods
+    /********************************************************
+     */
+
+    public static <T> T getProxy(Class<T> type, Object obj) {
+        class ProxyUtil implements InvocationHandler {
+            Object obj;
+            public ProxyUtil(Object o) {
+                obj = o;
+            }
+            @Override
+            public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+                Object result = null;
+                result = m.invoke(obj, args);
+                return result;
+            }
+        }
+        @SuppressWarnings("unchecked")
+        T proxy = (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[] { type },
+                new ProxyUtil(obj));
+        return proxy;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationMerging.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationMerging.java
new file mode 100644
index 0000000..097cc4c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotationMerging.java
@@ -0,0 +1,93 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Tests to verify that annotations are shared and merged between members
+ * of a property (getter and setter and so on)
+ */
+public class TestAnnotationMerging extends BaseMapTest
+{
+    static class Wrapper
+    {
+        protected Object value;
+
+        public Wrapper() { }
+        public Wrapper(Object o) { value = o; }
+        
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+        public Object getValue() { return value; }
+
+        public void setValue(Object o) { value = o; }
+    }
+
+    static class SharedName {
+        @JsonProperty("x")
+        protected int value;
+
+        public SharedName(int v) { value = v; }
+        
+        public int getValue() { return value; }
+    }
+
+    static class SharedName2
+    {
+        @JsonProperty("x")
+        public int getValue() { return 1; }
+        public void setValue(int x) { }
+    }
+
+    // Testing to ensure that ctor param and getter can "share" @JsonTypeInfo stuff
+    static class TypeWrapper
+    {
+        protected Object value;
+
+        @JsonCreator
+        public TypeWrapper(
+                @JsonProperty("value")
+                @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) Object o) {
+            value = o;
+        }
+        public Object getValue() { return value; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSharedNames() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("{\"x\":6}", mapper.writeValueAsString(new SharedName(6)));
+    }
+
+    public void testSharedNamesFromGetterToSetter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new SharedName2());
+        assertEquals("{\"x\":1}", json);
+        SharedName2 result = mapper.readValue(json, SharedName2.class);
+        assertNotNull(result);
+    }
+    
+    public void testSharedTypeInfo() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new Wrapper(13L));
+        Wrapper result = mapper.readValue(json, Wrapper.class);
+        assertEquals(Long.class, result.value.getClass());
+    }
+
+    public void testSharedTypeInfoWithCtor() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new TypeWrapper(13L));
+        TypeWrapper result = mapper.readValue(json, TypeWrapper.class);
+        assertEquals(Long.class, result.value.getClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
new file mode 100644
index 0000000..ddb7736
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestAnnotionBundles.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/* Tests mostly for [JACKSON-754]: ability to create "annotation bundles"
+ */
+public class TestAnnotionBundles extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotationsInside
+    @JsonIgnore
+    private @interface MyIgnoral { }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotationsInside
+    @JsonProperty("foobar")
+    private @interface MyRename { }
+
+    protected final static class Bean {
+        @MyIgnoral
+        public String getIgnored() { return "foo"; }
+ 
+        @MyRename
+        public int renamed = 13;
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @JsonAutoDetect(fieldVisibility=Visibility.NONE,
+            getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
+    @JacksonAnnotationsInside
+    public @interface JsonAutoDetectOff {}
+
+    @JsonAutoDetectOff
+    public class NoAutoDetect {
+      public int getA() { return 13; }
+      
+      @JsonProperty
+      public int getB() { return 5; }
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @JacksonAnnotationsInside
+    @JsonProperty("_id")
+    public @interface Bundle92 {}
+
+    public class Bean92 {
+        @Bundle92
+        protected String id = "abc";
+    }    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testBundledIgnore() throws Exception
+    {
+        assertEquals("{\"foobar\":13}", MAPPER.writeValueAsString(new Bean()));
+    }
+
+    public void testVisibilityBundle() throws Exception
+    {
+        assertEquals("{\"b\":5}", MAPPER.writeValueAsString(new NoAutoDetect()));
+    }
+    
+    public void testIssue92() throws Exception
+    {
+        assertEquals("{\"_id\":\"abc\"}", MAPPER.writeValueAsString(new Bean92()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java
new file mode 100644
index 0000000..ca0dd80
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestBuilderMethods.java
@@ -0,0 +1,56 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
+import com.fasterxml.jackson.databind.introspect.POJOPropertiesCollector;
+import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
+
+public class TestBuilderMethods extends BaseMapTest
+{
+    static class SimpleBuilder
+    {
+    	public int x;
+    	
+    	public SimpleBuilder withX(int x) {
+    		this.x = x;
+    		return this;
+    	}
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimple()
+    {
+        POJOPropertiesCollector coll = collector(SimpleBuilder.class, "with");
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("x");
+        assertNotNull(prop);
+        assertTrue(prop.hasField());
+        assertFalse(prop.hasGetter());
+        assertTrue(prop.hasSetter());
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected POJOPropertiesCollector collector(Class<?> cls, String prefix)
+    {
+        BasicClassIntrospector bci = new BasicClassIntrospector();
+        // no real difference between serialization, deserialization, at least here
+        return bci.collectProperties(mapper.getSerializationConfig(),
+                mapper.constructType(cls), null, false, prefix);
+    }
+    
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java
new file mode 100644
index 0000000..0960c79
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestInferredMutators.java
@@ -0,0 +1,65 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestInferredMutators extends BaseMapTest
+{
+    public static class Point {
+        private int x;
+        
+        public int getX() { return x; }
+    }
+
+    public static class FixedPoint {
+        private final int x;
+
+        public FixedPoint() { x = 0; }
+
+        public int getX() { return x; }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    // for #190
+    public void testFinalFieldIgnoral() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // default value is 'enabled', for backwards compatibility
+        assertTrue(mapper.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS));
+        mapper.disable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS);
+        try {
+            /*p =*/ mapper.readValue("{\"x\":2}", FixedPoint.class);
+            fail("Should not try to use final field");
+        } catch (JsonMappingException e) {
+            verifyException(e, "unrecognized field \"x\"");
+        }
+    }
+    
+    // for #195
+    public void testDeserializationInference() throws Exception
+    {
+        final String JSON = "{\"x\":2}";
+        ObjectMapper mapper = new ObjectMapper();
+        // First: default case, inference enabled:
+        assertTrue(mapper.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS));
+        Point p = mapper.readValue(JSON,  Point.class);
+        assertEquals(2, p.x);
+
+        // but without it, should fail:
+        mapper = new ObjectMapper();
+        mapper.disable(MapperFeature.INFER_PROPERTY_MUTATORS);
+        try {
+            p = mapper.readValue(JSON,  Point.class);
+            fail("Should not succeeed");
+        } catch (JsonMappingException e) {
+            verifyException(e, "unrecognized field \"x\"");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
new file mode 100644
index 0000000..8a436a2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestJacksonAnnotationIntrospector.java
@@ -0,0 +1,216 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.*;
+import javax.xml.namespace.QName;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.*;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+ at SuppressWarnings("serial")
+public class TestJacksonAnnotationIntrospector
+    extends BaseMapTest
+{
+    public static enum EnumExample {
+        VALUE1;
+    }
+
+    public static class JacksonExample
+    {
+        private String attributeProperty;
+        private String elementProperty;
+        private List<String> wrappedElementProperty;
+        private EnumExample enumProperty;
+        private QName qname;
+
+        @JsonSerialize(using=QNameSerializer.class)
+        public QName getQname()
+        {
+            return qname;
+        }
+
+        @JsonDeserialize(using=QNameDeserializer.class)
+        public void setQname(QName qname)
+        {
+            this.qname = qname;
+        }
+
+        @JsonProperty("myattribute")
+        public String getAttributeProperty()
+        {
+            return attributeProperty;
+        }
+
+        @JsonProperty("myattribute")
+        public void setAttributeProperty(String attributeProperty)
+        {
+            this.attributeProperty = attributeProperty;
+        }
+
+        @JsonProperty("myelement")
+        public String getElementProperty()
+        {
+            return elementProperty;
+        }
+
+        @JsonProperty("myelement")
+        public void setElementProperty(String elementProperty)
+        {
+            this.elementProperty = elementProperty;
+        }
+
+        @JsonProperty("mywrapped")
+        public List<String> getWrappedElementProperty()
+        {
+            return wrappedElementProperty;
+        }
+
+        @JsonProperty("mywrapped")
+        public void setWrappedElementProperty(List<String> wrappedElementProperty)
+        {
+            this.wrappedElementProperty = wrappedElementProperty;
+        }
+
+        public EnumExample getEnumProperty()
+        {
+            return enumProperty;
+        }
+
+        public void setEnumProperty(EnumExample enumProperty)
+        {
+            this.enumProperty = enumProperty;
+        }
+    }
+
+    public static class QNameSerializer extends JsonSerializer<QName> {
+
+        @Override
+        public void serialize(QName value, JsonGenerator jgen, SerializerProvider provider)
+                throws IOException, JsonProcessingException
+        {
+            jgen.writeString(value.toString());
+        }
+    }
+
+
+    public static class QNameDeserializer extends StdDeserializer<QName>
+    {
+        public QNameDeserializer() { super(QName.class); }
+        @Override
+        public QName deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            return QName.valueOf(jp.readValueAs(String.class));
+        }
+    }
+
+    public static class DummyBuilder extends StdTypeResolverBuilder
+    //<DummyBuilder>
+    {
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS)
+    @JsonTypeResolver(DummyBuilder.class)
+    static class TypeResolverBean { }
+
+    // @since 1.7
+    @JsonIgnoreType
+    static class IgnoredType { }
+
+    static class IgnoredSubType extends IgnoredType { }
+
+    // Test to ensure we can override enum settings
+    static class LcEnumIntrospector extends JacksonAnnotationIntrospector
+    {
+        private static final long serialVersionUID = 1L;
+        @Override
+        public String findEnumValue(Enum<?> value)
+        {
+            return value.name().toLowerCase();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    /**
+     * tests getting serializer/deserializer instances.
+     */
+    public void testSerializeDeserializeWithJaxbAnnotations() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(SerializationFeature.INDENT_OUTPUT);
+        JacksonExample ex = new JacksonExample();
+        QName qname = new QName("urn:hi", "hello");
+        ex.setQname(qname);
+        ex.setAttributeProperty("attributeValue");
+        ex.setElementProperty("elementValue");
+        ex.setWrappedElementProperty(Arrays.asList("wrappedElementValue"));
+        ex.setEnumProperty(EnumExample.VALUE1);
+        StringWriter writer = new StringWriter();
+        mapper.writeValue(writer, ex);
+        writer.flush();
+        writer.close();
+
+        String json = writer.toString();
+        JacksonExample readEx = mapper.readValue(json, JacksonExample.class);
+
+        assertEquals(ex.qname, readEx.qname);
+        assertEquals(ex.attributeProperty, readEx.attributeProperty);
+        assertEquals(ex.elementProperty, readEx.elementProperty);
+        assertEquals(ex.wrappedElementProperty, readEx.wrappedElementProperty);
+        assertEquals(ex.enumProperty, readEx.enumProperty);
+    }
+
+    public void testJsonTypeResolver() throws Exception
+    {
+        JacksonAnnotationIntrospector ai = new JacksonAnnotationIntrospector();
+        AnnotatedClass ac = AnnotatedClass.constructWithoutSuperTypes(TypeResolverBean.class, ai, null);
+        JavaType baseType = TypeFactory.defaultInstance().constructType(TypeResolverBean.class);
+        ObjectMapper mapper = new ObjectMapper();
+        TypeResolverBuilder<?> rb = ai.findTypeResolver(mapper.getDeserializationConfig(), ac, baseType);
+        assertNotNull(rb);
+        assertSame(DummyBuilder.class, rb.getClass());
+    }    
+
+    /**
+     * Tests to ensure that {@link JsonIgnoreType} is detected as expected
+     * by the standard introspector.
+     * 
+     * @since 1.7
+     */
+    public void testIgnoredType() throws Exception
+    {
+        JacksonAnnotationIntrospector ai = new JacksonAnnotationIntrospector();
+        AnnotatedClass ac = AnnotatedClass.construct(IgnoredType.class, ai, null);
+        assertEquals(Boolean.TRUE, ai.isIgnorableType(ac));
+
+        // also, should inherit as expected
+        ac = AnnotatedClass.construct(IgnoredSubType.class, ai, null);
+        assertEquals(Boolean.TRUE, ai.isIgnorableType(ac));
+    }
+
+    public void testEnumHandling() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setAnnotationIntrospector(new LcEnumIntrospector());
+        assertEquals("\"value1\"", mapper.writeValueAsString(EnumExample.VALUE1));
+        EnumExample result = mapper.readValue(quote("value1"), EnumExample.class);
+        assertEquals(EnumExample.VALUE1, result);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java b/src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java
new file mode 100644
index 0000000..0a6b25a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/introspect/TestPOJOPropertiesCollector.java
@@ -0,0 +1,417 @@
+package com.fasterxml.jackson.databind.introspect;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class TestPOJOPropertiesCollector
+    extends BaseMapTest
+{
+    static class Simple {
+        public int value;
+        
+        @JsonProperty("value")
+        public void valueSetter(int v) { value = v; }
+
+        @JsonProperty("value")
+        public int getFoobar() { return value; }
+    }
+
+    static class SimpleFieldDeser
+    {
+        @JsonDeserialize String[] values;
+    }
+    
+    static class SimpleGetterVisibility {
+        public int getA() { return 0; }
+        protected int getB() { return 1; }
+        @SuppressWarnings("unused")
+        private int getC() { return 2; }
+    }
+    
+    // Class for testing 'shared ignore'
+    static class Empty {
+        public int value;
+        
+        public void setValue(int v) { value = v; }
+
+        @JsonIgnore
+        public int getValue() { return value; }
+    }
+
+    static class IgnoredSetter {
+        @JsonProperty
+        public int value;
+        
+        @JsonIgnore
+        public void setValue(int v) { value = v; }
+
+        public int getValue() { return value; }
+    }
+
+    static class ImplicitIgnores {
+        @JsonIgnore public int a;
+        @JsonIgnore public void setB(int b) { }
+        public int c;
+    }
+    
+    // Should find just one setter for "y", due to partial ignore
+    static class IgnoredRenamedSetter {
+        @JsonIgnore public void setY(int value) { }
+        @JsonProperty("y") void foobar(int value) { }
+    }
+    
+    // should produce a single property, "x"
+    static class RenamedProperties {
+        @JsonProperty("x")
+        public int value;
+        
+        public void setValue(int v) { value = v; }
+
+        public int getX() { return value; }
+    }
+
+    static class RenamedProperties2
+    {
+        @JsonProperty("renamed")
+        public int getValue() { return 1; }
+        public void setValue(int x) { }
+    }
+    
+    // Testing that we can "merge" properties with renaming
+    static class MergedProperties {
+        public int x;
+        
+        @JsonProperty("x")
+        public void setFoobar(int v) { x = v; }
+    }
+
+    // Testing that property order is obeyed, even for deserialization purposes
+    @JsonPropertyOrder({"a", "b", "c", "d"})
+    static class SortedProperties
+    {
+        public int b;
+        public int c;
+        
+        public void setD(int value) { }
+        public void setA(int value) { }
+    }
+
+    // [JACKSON-700]: test property type detection, selection
+    static class TypeTestBean
+    {
+        protected Long value;
+
+        @JsonCreator
+        public TypeTestBean(@JsonProperty("value") String value) { }
+
+        // If you remove this method, the test will pass
+        public Integer getValue() { return 0; }
+    }
+
+    static class Jackson703
+    {
+        private List<FoodOrgLocation> location = new ArrayList<FoodOrgLocation>();
+
+        {
+            location.add(new FoodOrgLocation());
+        }
+
+        public List<FoodOrgLocation> getLocation() { return location; } 
+    }
+    
+    static class FoodOrgLocation
+    {
+        protected Long id;
+        public String name;
+        protected Location location;
+
+        public FoodOrgLocation() {
+            location = new Location();
+        }
+
+        public FoodOrgLocation(final Location foodOrg) { }
+                
+        public FoodOrgLocation(final Long id, final String name, final Location location) { }
+
+        public Location getLocation() { return location; }
+    }
+
+    static class Location {
+        public BigDecimal lattitude;
+        public BigDecimal longitude;
+
+        public Location() { }
+
+        public Location(final BigDecimal lattitude, final BigDecimal longitude) { }
+    }
+
+    class Issue701Bean { // important: non-static!
+        private int i;
+
+        // annotation does not matter -- just need one on the last argument
+        public Issue701Bean(@JsonProperty int i) { this.i = i; }
+
+        public int getX() { return i; }
+    }
+
+    static class Issue744Bean
+    {
+        protected Map<String,Object> additionalProperties;
+        
+        @JsonAnySetter
+        public void addAdditionalProperty(String key, Object value) {
+            if (additionalProperties == null) additionalProperties = new HashMap<String, Object>();
+            additionalProperties.put(key,value);
+        }
+        
+        public void setAdditionalProperties(Map<String, Object> additionalProperties) {
+            this.additionalProperties = additionalProperties;
+        }
+
+        @JsonAnyGetter
+        public Map<String,Object> getAdditionalProperties() { return additionalProperties; }
+
+        @JsonIgnore
+        public String getName() {
+           return (String) additionalProperties.get("name");
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimple()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		Simple.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("value");
+        assertNotNull(prop);
+        assertTrue(prop.hasSetter());
+        assertTrue(prop.hasGetter());
+        assertTrue(prop.hasField());
+    }
+
+    public void testSimpleFieldVisibility()
+    {
+        // false -> deserialization
+        POJOPropertiesCollector coll = collector(mapper,
+        		SimpleFieldDeser.class, false);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("values");
+        assertNotNull(prop);
+        assertFalse(prop.hasSetter());
+        assertFalse(prop.hasGetter());
+        assertTrue(prop.hasField());
+    }
+
+    public void testSimpleGetterVisibility()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		SimpleGetterVisibility.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("a");
+        assertNotNull(prop);
+        assertFalse(prop.hasSetter());
+        assertTrue(prop.hasGetter());
+        assertFalse(prop.hasField());
+    }
+    
+    // Unit test for verifying that a single @JsonIgnore can remove the
+    // whole property, unless explicit property marker exists
+    public void testEmpty()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		Empty.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(0, props.size());
+    }
+
+    // Unit test for verifying handling of 'partial' @JsonIgnore; that is,
+    // if there is at least one explicit annotation to indicate property,
+    // only parts that are ignored are, well, ignored
+    public void testPartialIgnore()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		IgnoredSetter.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("value");
+        assertNotNull(prop);
+        assertFalse(prop.hasSetter());
+        assertTrue(prop.hasGetter());
+        assertTrue(prop.hasField());
+    }
+
+    public void testSimpleRenamed()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		RenamedProperties.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("x");
+        assertNotNull(prop);
+        assertTrue(prop.hasSetter());
+        assertTrue(prop.hasGetter());
+        assertTrue(prop.hasField());
+    }
+
+    public void testSimpleRenamed2()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		RenamedProperties2.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("renamed");
+        assertNotNull(prop);
+        assertTrue(prop.hasSetter());
+        assertTrue(prop.hasGetter());
+        assertFalse(prop.hasField());
+    }
+
+    public void testMergeWithRename()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		MergedProperties.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("x");
+        assertNotNull(prop);
+        assertTrue(prop.hasSetter());
+        assertFalse(prop.hasGetter());
+        assertTrue(prop.hasField());
+    }
+    
+    public void testSimpleIgnoreAndRename()
+    {
+        POJOPropertiesCollector coll = collector(mapper,
+        		IgnoredRenamedSetter.class, true);
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        POJOPropertyBuilder prop = props.get("y");
+        assertNotNull(prop);
+        assertTrue(prop.hasSetter());
+        assertFalse(prop.hasGetter());
+        assertFalse(prop.hasField());
+    }
+
+    public void testGlobalVisibilityForGetters()
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.AUTO_DETECT_GETTERS, false);
+        POJOPropertiesCollector coll = collector(m, SimpleGetterVisibility.class, true);
+        // should be 1, expect that we disabled getter auto-detection, so
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(0, props.size());
+    }
+
+    public void testCollectionOfIgnored()
+    {
+        POJOPropertiesCollector coll = collector(mapper, ImplicitIgnores.class, false);
+        // should be 1, due to ignorals
+        Map<String, POJOPropertyBuilder> props = coll.getPropertyMap();
+        assertEquals(1, props.size());
+        // but also have 2 ignored properties
+        Collection<String> ign = coll.getIgnoredPropertyNames();
+        assertEquals(2, ign.size());
+        assertTrue(ign.contains("a"));
+        assertTrue(ign.contains("b"));
+    }
+
+    public void testSimpleOrderingForDeserialization()
+    {
+        POJOPropertiesCollector coll = collector(mapper, SortedProperties.class, false);
+        List<BeanPropertyDefinition> props = coll.getProperties();
+        assertEquals(4, props.size());
+        assertEquals("a", props.get(0).getName());
+        assertEquals("b", props.get(1).getName());
+        assertEquals("c", props.get(2).getName());
+        assertEquals("d", props.get(3).getName());
+    }
+
+    public void testSimpleWithType()
+    {
+        // first for serialization; should base choice on getter
+        POJOPropertiesCollector coll = collector(mapper, TypeTestBean.class, true);
+        List<BeanPropertyDefinition> props = coll.getProperties();
+        assertEquals(1, props.size());
+        assertEquals("value", props.get(0).getName());
+        AnnotatedMember m = props.get(0).getAccessor();
+        assertTrue(m instanceof AnnotatedMethod);
+        assertEquals(Integer.class, m.getRawType());
+
+        // then for deserialization; prefer ctor param
+        coll = collector(mapper, TypeTestBean.class, false);
+        props = coll.getProperties();
+        assertEquals(1, props.size());
+        assertEquals("value", props.get(0).getName());
+        m = props.get(0).getMutator();
+        assertEquals(AnnotatedParameter.class, m.getClass());
+        assertEquals(String.class, m.getRawType());
+    }
+
+    // for [JACKSON-701]
+    public void testInnerClassWithAnnotationsInCreator() throws Exception
+    {
+        BasicBeanDescription beanDesc;
+        // first with serialization
+        beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Issue701Bean.class));
+        assertNotNull(beanDesc);
+        // then with deserialization
+        beanDesc = mapper.getDeserializationConfig().introspect(mapper.constructType(Issue701Bean.class));
+        assertNotNull(beanDesc);
+    }
+
+    public void testJackson703() throws Exception
+    {
+    	// note: need a separate mapper, need to reconfigure
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(MapperFeature.USE_ANNOTATIONS, false);
+        BasicBeanDescription beanDesc = mapper.getSerializationConfig().introspect(mapper.constructType(Jackson703.class));
+        assertNotNull(beanDesc);
+
+        Jackson703 bean = new Jackson703();
+        String json = mapper.writeValueAsString(bean);
+        assertNotNull(json);
+    }
+
+    public void testJackson744() throws Exception
+    {
+        BasicBeanDescription beanDesc = mapper.getDeserializationConfig().introspect(mapper.constructType(Issue744Bean.class));
+        assertNotNull(beanDesc);
+        AnnotatedMethod setter = beanDesc.findAnySetter();
+        assertNotNull(setter);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected POJOPropertiesCollector collector(ObjectMapper mapper,
+            Class<?> cls, boolean forSerialization)
+    {
+        BasicClassIntrospector bci = new BasicClassIntrospector();
+        // no real difference between serialization, deserialization, at least here
+        if (forSerialization) {
+            return bci.collectProperties(mapper.getSerializationConfig(),
+                    mapper.constructType(cls), null, true, "set");
+        }
+        return bci.collectProperties(mapper.getDeserializationConfig(),
+                mapper.constructType(cls), null, false, "set");
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java
new file mode 100644
index 0000000..6f7c0da
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestGenerateJsonSchema.java
@@ -0,0 +1,226 @@
+package com.fasterxml.jackson.databind.jsonschema;
+
+import java.util.Collection;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonFilter;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
+
+/**
+ * @author Ryan Heaton
+ */
+ at SuppressWarnings("deprecation")
+public class TestGenerateJsonSchema
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    public static class SimpleBean
+    {
+        private int property1;
+        private String property2;
+        private String[] property3;
+        private Collection<Float> property4;
+        @JsonProperty(required=true)
+        private String property5;
+        
+        public int getProperty1()
+        {
+            return property1;
+        }
+
+        public void setProperty1(int property1)
+        {
+            this.property1 = property1;
+        }
+
+        public String getProperty2()
+        {
+            return property2;
+        }
+
+        public void setProperty2(String property2)
+        {
+            this.property2 = property2;
+        }
+
+        public String[] getProperty3()
+        {
+            return property3;
+        }
+
+        public void setProperty3(String[] property3)
+        {
+            this.property3 = property3;
+        }
+
+        public Collection<Float> getProperty4()
+        {
+            return property4;
+        }
+
+        public void setProperty4(Collection<Float> property4)
+        {
+            this.property4 = property4;
+        }
+        
+        public String getProperty5()
+        {
+            return property5;
+        }
+
+        public void setProperty5(String property5)
+        {
+            this.property5 = property5;
+        }
+    }
+
+    public class TrivialBean {
+        public String name;
+    }
+
+    @JsonSerializableSchema(id="myType")
+    public class BeanWithId {
+        public String value;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * tests generating json-schema stuff.
+     */
+    public void testGeneratingJsonSchema()
+        throws Exception
+    {
+        JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class);
+        
+        assertNotNull(jsonSchema);
+
+        // test basic equality, and that equals() handles null, other obs
+        assertTrue(jsonSchema.equals(jsonSchema));
+        assertFalse(jsonSchema.equals(null));
+        assertFalse(jsonSchema.equals("foo"));
+
+        // other basic things
+        assertNotNull(jsonSchema.toString());
+        assertNotNull(JsonSchema.getDefaultSchemaNode());
+
+	ObjectNode root = jsonSchema.getSchemaNode();
+        assertEquals("object", root.get("type").asText());
+        assertEquals(false, root.path("required").booleanValue());
+        JsonNode propertiesSchema = root.get("properties");
+        assertNotNull(propertiesSchema);
+        JsonNode property1Schema = propertiesSchema.get("property1");
+        assertNotNull(property1Schema);
+        assertEquals("integer", property1Schema.get("type").asText());
+        assertEquals(false, property1Schema.path("required").booleanValue());
+        JsonNode property2Schema = propertiesSchema.get("property2");
+        assertNotNull(property2Schema);
+        assertEquals("string", property2Schema.get("type").asText());
+        assertEquals(false, property2Schema.path("required").booleanValue());
+        JsonNode property3Schema = propertiesSchema.get("property3");
+        assertNotNull(property3Schema);
+        assertEquals("array", property3Schema.get("type").asText());
+        assertEquals(false, property3Schema.path("required").booleanValue());
+        assertEquals("string", property3Schema.get("items").get("type").asText());
+        JsonNode property4Schema = propertiesSchema.get("property4");
+        assertNotNull(property4Schema);
+        assertEquals("array", property4Schema.get("type").asText());
+        assertEquals(false, property4Schema.path("required").booleanValue());
+        assertEquals("number", property4Schema.get("items").get("type").asText());
+    }
+    
+    @JsonFilter("filteredBean")
+    protected static class FilteredBean {
+    	
+    	@JsonProperty
+    	private String secret = "secret";
+    	
+    	@JsonProperty
+    	private String obvious = "obvious";
+    	
+    	public String getSecret() { return secret; }
+    	public void setSecret(String s) { secret = s; }
+    	
+    	public String getObvious() { return obvious; }
+    	public void setObvious(String s) {obvious = s; }
+    }
+    
+    public static FilterProvider secretFilterProvider = new SimpleFilterProvider()
+        .addFilter("filteredBean", SimpleBeanPropertyFilter.filterOutAllExcept(new String[]{"obvious"}));
+
+    public void testGeneratingJsonSchemaWithFilters() throws Exception {
+    	ObjectMapper mapper = new ObjectMapper();
+    	mapper.setFilters(secretFilterProvider);
+    	JsonSchema schema = mapper.generateJsonSchema(FilteredBean.class);
+    	JsonNode node = schema.getSchemaNode().get("properties");
+    	assertTrue(node.has("obvious"));
+    	assertFalse(node.has("secret"));
+    }
+
+    /**
+     * Additional unit test for verifying that schema object itself
+     * can be properly serialized
+     */
+    public void testSchemaSerialization()
+            throws Exception
+    {
+        JsonSchema jsonSchema = MAPPER.generateJsonSchema(SimpleBean.class);
+	Map<String,Object> result = writeAndMap(MAPPER, jsonSchema);
+	assertNotNull(result);
+	// no need to check out full structure, just basics...
+	assertEquals("object", result.get("type"));
+	// only add 'required' if it is true...
+	assertNull(result.get("required"));
+	assertNotNull(result.get("properties"));
+    }
+
+    public void testInvalidCall()
+        throws Exception
+    {
+        // not ok to pass null
+        try {
+            MAPPER.generateJsonSchema(null);
+            fail("Should have failed");
+        } catch (IllegalArgumentException iae) {
+            verifyException(iae, "class must be provided");
+        }
+    }
+
+    /**
+     * Test for [JACKSON-454]
+     */
+    public void testThatObjectsHaveNoItems() throws Exception
+    {
+        JsonSchema jsonSchema = MAPPER.generateJsonSchema(TrivialBean.class);
+        String json = jsonSchema.toString().replaceAll("\"", "'");
+        // can we count on ordering being stable? I think this is true with current ObjectNode impl
+        // as perh [JACKSON-563]; 'required' is only included if true
+        assertEquals("{'type':'object','properties':{'name':{'type':'string'}}}",
+                json);
+    }
+
+    public void testSchemaId() throws Exception
+    {
+        JsonSchema jsonSchema = MAPPER.generateJsonSchema(BeanWithId.class);
+        String json = jsonSchema.toString().replaceAll("\"", "'");
+        assertEquals("{'type':'object','id':'myType','properties':{'value':{'type':'string'}}}",
+                json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java
new file mode 100644
index 0000000..774e785
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsonschema/TestReadJsonSchema.java
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.databind.jsonschema;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsonschema.JsonSchema;
+
+/**
+ * Trivial test to ensure {@link JsonSchema} can be also deserialized
+ */
+ at SuppressWarnings("deprecation")
+public class TestReadJsonSchema
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    enum SchemaEnum { YES, NO; }
+
+    static class Schemable {
+        public String name;
+        public char[] nameBuffer;
+
+        // We'll include tons of stuff, just to force generation of schema
+        public boolean[] states;
+        public byte[] binaryData;
+        public short[] shorts;
+        public int[] ints;
+        public long[] longs;
+
+        public float[] floats;
+        public double[] doubles;
+
+        public Object[] objects;
+        public JsonSerializable someSerializable;
+
+        public Iterable<Object> iterableOhYeahBaby;
+
+        public List<String> extra;
+        public ArrayList<String> extra2;
+        public Iterator<String[]> extra3;
+
+        public Map<String,Double> sizes;
+        public EnumMap<SchemaEnum,List<String>> whatever;
+
+        SchemaEnum testEnum;
+        public EnumSet<SchemaEnum> testEnums;
+    }
+
+    /**
+     * Verifies that a simple schema that is serialized can be
+     * deserialized back to equal schema instance
+     */
+    public void testDeserializeSimple() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonSchema schema = mapper.generateJsonSchema(Schemable.class);
+        assertNotNull(schema);
+
+        String schemaStr = mapper.writeValueAsString(schema);
+        assertNotNull(schemaStr);
+        JsonSchema result = mapper.readValue(schemaStr, JsonSchema.class);
+        assertEquals("Trying to read from '"+schemaStr+"'", schema, result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java
new file mode 100644
index 0000000..2635b12
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractTypeNames.java
@@ -0,0 +1,136 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+
+/**
+ * Unit tests for checking how combination of interfaces, implementation
+ * classes are handled, with respect to type names.
+ */
+public class TestAbstractTypeNames  extends BaseMapTest
+{
+    @JsonTypeName("Employee")
+    public interface Employee extends User {
+        public abstract String getEmployer();
+    }
+
+    @JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="userType")
+    @JsonTypeName("User")
+    @JsonSubTypes({ @JsonSubTypes.Type(value=Employee.class,name="Employee") })
+    public interface User {
+            public abstract String getName();
+            public abstract List<User> getFriends();
+    }
+
+    @JsonTypeName("Employee")
+    static class DefaultEmployee extends DefaultUser implements Employee
+    {
+        private String _employer;
+
+        @JsonCreator
+        public DefaultEmployee(@JsonProperty("name") String name,
+                @JsonProperty("friends") List<User> friends,
+                @JsonProperty("employer") String employer) {
+            super(name, friends);
+            _employer = employer;
+        }
+
+        @Override
+        public String getEmployer() {
+            return _employer;
+        }
+    }
+
+    @JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="userType")
+    @JsonTypeName("User")
+    @JsonSubTypes({ @JsonSubTypes.Type(value=DefaultEmployee.class,name="Employee") })
+    static class DefaultUser implements User
+    {
+        private String _name;
+        private List<User> _friends;
+
+        @JsonCreator
+        public DefaultUser(@JsonProperty("name") String name,
+                @JsonProperty("friends") List<User> friends)
+        {
+            super();
+            _name = name;
+            _friends = friends;
+        }
+
+        @Override
+        public String getName() {
+            return _name;
+        }
+
+        @Override
+        public List<User> getFriends() {
+            return _friends;
+        }
+    }
+
+    static class BaseValue {
+        public int value = 42;
+
+        public int getValue() { return value; }
+    }
+
+    final static class BeanWithAnon {
+        public BaseValue bean = new BaseValue() {
+            @Override
+            public String toString() { return "sub!"; }
+        };
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    // Testing [JACKSON-498], partial fix
+    public void testEmptyCollection() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+        List<User>friends = new ArrayList<User>();
+        friends.add(new DefaultUser("Joe Hildebrandt", null));
+        friends.add(new DefaultEmployee("Richard Nasr",null,"MDA"));
+
+        User user = new DefaultEmployee("John Vanspronssen", friends, "MDA");
+        String json = mapper.writeValueAsString(user);
+
+        /* 24-Feb-2011, tatu: For now let's simply require registration of
+         *   concrete subtypes; can't think of a way to avoid that for now
+         */
+        mapper = new ObjectMapper();
+        mapper.registerSubtypes(DefaultEmployee.class);
+        mapper.registerSubtypes(DefaultUser.class);
+        
+        User result = mapper.readValue(json, User.class);
+        assertNotNull(result);
+        assertEquals(DefaultEmployee.class, result.getClass());
+
+        friends = result.getFriends();
+        assertEquals(2, friends.size());
+        assertEquals(DefaultUser.class, friends.get(0).getClass());
+        assertEquals(DefaultEmployee.class, friends.get(1).getClass());
+    }
+    
+    // [JACKSON-584]: change anonymous non-static inner type into static type:
+    public void testInnerClassWithType() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
+        String json = mapper.writeValueAsString(new BeanWithAnon());
+        BeanWithAnon result = mapper.readValue(json, BeanWithAnon.class);
+        assertEquals(BeanWithAnon.class, result.getClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractWithObjectId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractWithObjectId.java
new file mode 100644
index 0000000..3ddc140
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestAbstractWithObjectId.java
@@ -0,0 +1,68 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+import java.util.*;
+
+public class TestAbstractWithObjectId extends BaseMapTest
+{
+    interface BaseInterface { }
+
+    @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "@id")
+    static class BaseInterfaceImpl implements BaseInterface {
+
+        @JsonProperty
+        private List<BaseInterfaceImpl> myInstances = new ArrayList<BaseInterfaceImpl>();
+
+        void addInstance(BaseInterfaceImpl instance) {
+            myInstances.add(instance);
+        }
+    }
+
+    static class ListWrapper<T extends BaseInterface> {
+
+        @JsonProperty
+        private List<T> myList = new ArrayList<T>();
+
+        void add(T t) {
+            myList.add(t);
+        }
+
+        int size() {
+            return myList.size();
+        }
+    }
+
+    public void testIssue877() throws Exception
+    {
+        // make two instances
+        BaseInterfaceImpl one = new BaseInterfaceImpl();
+        BaseInterfaceImpl two = new BaseInterfaceImpl();
+
+        // add them to each other's list to show identify info being used
+        one.addInstance(two);
+        two.addInstance(one);
+
+        // make a typed version of the list and add the 2 instances to it
+        ListWrapper<BaseInterfaceImpl> myList = new ListWrapper<BaseInterfaceImpl>();
+        myList.add(one);
+        myList.add(two);
+
+        // make an object mapper that will add class info in so deserialisation works
+        ObjectMapper om = new ObjectMapper();
+        om.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class");
+
+        // write and print the JSON
+        String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(myList);
+        ListWrapper<BaseInterfaceImpl> result;
+        
+        result = om.readValue(json, new TypeReference<ListWrapper<BaseInterfaceImpl>>() { });
+
+        assertNotNull(result);
+        // see what we get back
+        System.out.println("deserialised list size = " + result.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
new file mode 100644
index 0000000..0a4cadd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestCustomTypeIdResolver.java
@@ -0,0 +1,100 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestCustomTypeIdResolver extends BaseMapTest
+{
+    @JsonTypeInfo(use=Id.CUSTOM, include=As.WRAPPER_OBJECT)
+    @JsonTypeIdResolver(CustomResolver.class)
+    static class CustomBean {
+        public int x;
+        
+        public CustomBean() { }
+        public CustomBean(int x) { this.x = x; }
+    }
+    
+    static class CustomResolver implements TypeIdResolver
+    {
+        static List<JavaType> initTypes;
+
+        public CustomResolver() { }
+        
+        @Override
+        public Id getMechanism() {
+            return Id.CUSTOM;
+        }
+
+        @Override
+        public String idFromValue(Object value)
+        {
+            if (value.getClass() == CustomBean.class) {
+                return "*";
+            }
+            return "unknown";
+        }
+
+        @Override
+        public String idFromValueAndType(Object value, Class<?> type) {
+            return idFromValue(value);
+        }
+
+        @Override
+        public void init(JavaType baseType) {
+            if (initTypes != null) {
+                initTypes.add(baseType);
+            }
+        }
+
+        @Override
+        public JavaType typeFromId(String id)
+        {
+            if ("*".equals(id)) {
+                return TypeFactory.defaultInstance().constructType(CustomBean.class);
+            }
+            return null;
+        }
+
+        @Override
+        public String idFromBaseType() {
+            return "xxx";
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    // for [JACKSON-359]
+    public void testCustomTypeIdResolver() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        List<JavaType> types = new ArrayList<JavaType>();
+        CustomResolver.initTypes = types;
+        String json = m.writeValueAsString(new CustomBean[] { new CustomBean(28) });
+        assertEquals("[{\"*\":{\"x\":28}}]", json);
+        assertEquals(1, types.size());
+        assertEquals(CustomBean.class, types.get(0).getRawClass());
+
+        types = new ArrayList<JavaType>();
+        CustomResolver.initTypes = types;
+        CustomBean[] result = m.readValue(json, CustomBean[].class);
+        assertNotNull(result);
+        assertEquals(1, result.length);
+        assertEquals(28, result[0].x);
+        assertEquals(1, types.size());
+        assertEquals(CustomBean.class, types.get(0).getRawClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
new file mode 100644
index 0000000..f7ce743
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForArrays.java
@@ -0,0 +1,104 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+
+public class TestDefaultForArrays extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static class ArrayBean {
+        public Object[] values;
+
+        public ArrayBean() { this(null); }
+        public ArrayBean(Object[] v) { values = v; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    /**
+     * Simple unit test for verifying that we get String array
+     * back, even though declared type is Object array
+     */
+    public void testArrayTypingSimple() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS);
+        ArrayBean bean = new ArrayBean(new String[0]);
+        String json = m.writeValueAsString(bean);
+        ArrayBean result = m.readValue(json, ArrayBean.class);
+        assertNotNull(result.values);
+        assertEquals(String[].class, result.values.getClass());
+    }
+
+    // And let's try it with deeper array as well
+    public void testArrayTypingNested() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(DefaultTyping.NON_CONCRETE_AND_ARRAYS);
+        ArrayBean bean = new ArrayBean(new String[0][0]);
+        String json = m.writeValueAsString(bean);
+        ArrayBean result = m.readValue(json, ArrayBean.class);
+        assertNotNull(result.values);
+        assertEquals(String[][].class, result.values.getClass());
+    }
+
+    // @since 1.8
+    public void testNodeInArray() throws Exception
+    {
+        JsonNode node = new ObjectMapper().readTree("{\"a\":3}");
+
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT);
+        Object[] obs = new Object[] { node };
+        String json = m.writeValueAsString(obs);
+        Object[] result = m.readValue(json, Object[].class);
+        assertEquals(1, result.length);
+        Object ob = result[0];
+        assertTrue(ob instanceof JsonNode);
+    }
+
+    // test for [JACKSON-845]
+    public void testArraysOfArrays() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+
+        Object value = new Object[][] { new Object[] {} };
+        String json = mapper.writeValueAsString(value);
+
+        // try with different (but valid) nominal types:
+        _testArraysAs(mapper, json, Object[][].class);
+        _testArraysAs(mapper, json, Object[].class);
+        _testArraysAs(mapper, json, Object.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected void _testArraysAs(ObjectMapper mapper, String json, Class<?> type)
+        throws Exception
+    {
+        Object o = mapper.readValue(json, type);
+        assertNotNull(o);
+        assertTrue(o instanceof Object[]);
+        Object[] main = (Object[]) o;
+        assertEquals(1, main.length);
+        Object element = main[0];
+        assertNotNull(element);
+        assertTrue(element instanceof Object[]);
+        assertEquals(0, ((Object[]) element).length);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java
new file mode 100644
index 0000000..645530c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForEnums.java
@@ -0,0 +1,77 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.concurrent.TimeUnit;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestDefaultForEnums
+    extends BaseMapTest
+{
+    public enum TestEnum {
+        A, B;
+    }
+
+    static final class EnumHolder
+    {
+        public Object value; // "untyped"
+        
+        public EnumHolder() { }
+        public EnumHolder(TestEnum e) { value = e; }
+    }
+
+    protected static class TimeUnitBean {
+        public TimeUnit timeUnit;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testSimpleEnumBean() throws Exception
+    {
+        TimeUnitBean bean = new TimeUnitBean();
+        bean.timeUnit = TimeUnit.SECONDS;
+        
+        // First, without type info
+        ObjectMapper m = new ObjectMapper();
+        String json = m.writeValueAsString(bean);
+        TimeUnitBean result = m.readValue(json, TimeUnitBean.class);
+        assertEquals(TimeUnit.SECONDS, result.timeUnit);
+        
+        // then with type info
+        m = new ObjectMapper();
+        m.enableDefaultTyping();
+        json = m.writeValueAsString(bean);
+        result = m.readValue(json, TimeUnitBean.class);
+
+        assertEquals(TimeUnit.SECONDS, result.timeUnit);
+    }
+    
+    public void testSimpleEnumsInObjectArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        
+        // Typing is needed for enums
+        String json = m.writeValueAsString(new Object[] { TestEnum.A });
+        assertEquals("[[\"com.fasterxml.jackson.databind.jsontype.TestDefaultForEnums$TestEnum\",\"A\"]]", json);
+
+        // and let's verify we get it back ok as well:
+        Object[] value = m.readValue(json, Object[].class);
+        assertEquals(1, value.length);
+        assertSame(TestEnum.A, value[0]);
+    }
+
+    public void testSimpleEnumsAsField() throws Exception
+    {
+        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);
+        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/TestDefaultForLists.java
new file mode 100644
index 0000000..df55697
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForLists.java
@@ -0,0 +1,146 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
+
+public class TestDefaultForLists
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Wrapper bean needed before there is a way to force
+     * type of the root value. Long is used because it is a final
+     * type, but not one of "untypeable" ones.
+     */
+    static class ListOfLongs {
+        public List<Long> longs;
+
+        public ListOfLongs() { }
+        public ListOfLongs(Long ... ls) {
+            longs = new ArrayList<Long>();
+            for (Long l: ls) {
+                longs.add(l);
+            }
+        }
+    }
+
+    static class ListOfNumbers {
+        public List<Number> nums;
+
+        public ListOfNumbers() { }
+        public ListOfNumbers(Number ... numbers) {
+            nums = new ArrayList<Number>();
+            for (Number n : numbers) {
+                nums.add(n);
+            }
+        }
+    }
+
+    static class ObjectListBean {
+        public List<Object> values;
+    }
+
+    interface Foo { }
+
+    static class SetBean {
+        public Set<String> names;
+        
+        public SetBean() { }
+        public SetBean(String str) {
+            names = new HashSet<String>();
+            names.add(str);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testListOfLongs() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        ListOfLongs input = new ListOfLongs(1L, 2L, 3L);
+        String json = m.writeValueAsString(input);
+        assertEquals("{\"longs\":[\"java.util.ArrayList\",[1,2,3]]}", json);
+        ListOfLongs output = m.readValue(json, ListOfLongs.class);
+
+        assertNotNull(output.longs);
+        assertEquals(3, output.longs.size());
+        assertEquals(Long.valueOf(1L), output.longs.get(0));
+        assertEquals(Long.valueOf(2L), output.longs.get(1));
+        assertEquals(Long.valueOf(3L), output.longs.get(2));
+    }
+
+    /**
+     * Then bit more heterogenous list; also tests mixing of
+     * regular scalar types, and non-typed ones (int and double
+     * will never have type info added; other numbers will if
+     * necessary)
+     */
+    public void testListOfNumbers() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        ListOfNumbers input = new ListOfNumbers(Long.valueOf(1L), Integer.valueOf(2), Double.valueOf(3.0));
+        String json = m.writeValueAsString(input);
+        assertEquals("{\"nums\":[\"java.util.ArrayList\",[[\"java.lang.Long\",1],2,3.0]]}", json);
+        ListOfNumbers output = m.readValue(json, ListOfNumbers.class);
+
+        assertNotNull(output.nums);
+        assertEquals(3, output.nums.size());
+        assertEquals(Long.valueOf(1L), output.nums.get(0));
+        assertEquals(Integer.valueOf(2), output.nums.get(1));
+        assertEquals(Double.valueOf(3.0), output.nums.get(2));
+    }
+
+    public void testDateTypes() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        ObjectListBean input = new ObjectListBean();
+        List<Object> inputList = new ArrayList<Object>();
+        inputList.add(TimeZone.getTimeZone("EST"));
+        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());
+        assertTrue(outputList.get(0) instanceof TimeZone);
+        assertTrue(outputList.get(1) instanceof Locale);
+    }
+    
+    public void testJackson628() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(DefaultTyping.NON_FINAL);
+        ArrayList<Foo> data = new ArrayList<Foo>();
+        String json = mapper.writeValueAsString(data);
+        List<?> output = mapper.readValue(json, List.class);
+        assertTrue(output.isEmpty());
+    }
+
+    public void testJackson667() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL,
+                JsonTypeInfo.As.PROPERTY);
+        String json = mapper.writeValueAsString(new SetBean("abc"));
+        SetBean bean = mapper.readValue(json, SetBean.class);
+        assertNotNull(bean);
+        assertTrue(bean.names instanceof HashSet);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java
new file mode 100644
index 0000000..8465ef2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForMaps.java
@@ -0,0 +1,100 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
+import com.fasterxml.jackson.databind.jsontype.impl.TypeNameIdResolver;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestDefaultForMaps 
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static class MapKey {
+        public String key;
+
+        public MapKey(String k) { key = k; }
+
+        @Override public String toString() { return key; }
+    }
+
+    static class MapKeyDeserializer extends KeyDeserializer
+    {
+        @Override
+        public Object deserializeKey(String key, DeserializationContext ctxt) {
+            return new MapKey(key);
+        }
+    }
+    
+    static class MapHolder
+    {
+        @JsonDeserialize(keyAs=MapKey.class, keyUsing=MapKeyDeserializer.class)
+        public Map<MapKey,List<Object>> map;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testJackson428() throws Exception
+    {
+        ObjectMapper serMapper = new ObjectMapper();
+
+        TypeResolverBuilder<?> serializerTyper = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
+        serializerTyper = serializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(true));
+        serializerTyper = serializerTyper.inclusion(JsonTypeInfo.As.PROPERTY);
+        serMapper.setDefaultTyping(serializerTyper);
+
+        // Let's start by constructing something to serialize first
+        MapHolder holder = new MapHolder();
+        holder.map = new HashMap<MapKey,List<Object>>();
+        List<Object> ints = new ArrayList<Object>();
+        ints.add(Integer.valueOf(3));
+        holder.map.put(new MapKey("key"), ints);
+        String json = serMapper.writeValueAsString(holder);
+
+        // Then deserialize: need separate mapper to initialize type id resolver appropriately
+        ObjectMapper deserMapper = new ObjectMapper();
+        TypeResolverBuilder<?> deserializerTyper = new ObjectMapper.DefaultTypeResolverBuilder(ObjectMapper.DefaultTyping.NON_FINAL);
+        deserializerTyper = deserializerTyper.init(JsonTypeInfo.Id.NAME, createTypeNameIdResolver(false));
+        deserializerTyper = deserializerTyper.inclusion(JsonTypeInfo.As.PROPERTY);
+        deserMapper.setDefaultTyping(deserializerTyper);
+
+        MapHolder result = deserMapper.readValue(json, MapHolder.class);
+        assertNotNull(result);
+        Map<?,?> map = result.map;
+        assertEquals(1, map.size());
+        Map.Entry<?,?> entry = map.entrySet().iterator().next();
+        Object key = entry.getKey();
+        assertEquals(MapKey.class, key.getClass());
+        Object value = entry.getValue();
+        assertTrue(value instanceof List<?>);
+        List<?> list = (List<?>) value;
+        assertEquals(1, list.size());
+        assertEquals(Integer.class, list.get(0).getClass());
+        assertEquals(Integer.valueOf(3), list.get(0));
+    }
+
+    protected TypeNameIdResolver createTypeNameIdResolver(boolean forSerialization)
+    {
+        Collection<NamedType> subtypes = new ArrayList<NamedType>();
+        subtypes.add(new NamedType(MapHolder.class, "mapHolder"));
+        subtypes.add(new NamedType(ArrayList.class, "AList"));
+        subtypes.add(new NamedType(HashMap.class, "HMap"));
+        ObjectMapper mapper = new ObjectMapper();
+        return TypeNameIdResolver.construct(mapper.getDeserializationConfig(),
+                TypeFactory.defaultInstance().constructType(Object.class), subtypes, forSerialization, !forSerialization);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
new file mode 100644
index 0000000..2e736ab
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForObject.java
@@ -0,0 +1,360 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+public class TestDefaultForObject
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static abstract class AbstractBean { }
+    
+    static class StringBean extends AbstractBean { // ha, punny!
+        public String name;
+
+        public StringBean() { this(null); }
+        protected StringBean(String n)  { name = n; }
+    }
+
+    enum Choice { YES, NO; }
+
+    /**
+     * Another enum type, but this time forcing sub-classing
+     */
+    enum ComplexChoice {
+    	MAYBE(true), PROBABLY_NOT(false);
+
+    	private boolean state;
+    	
+    	private ComplexChoice(boolean b) { state = b; }
+    	
+        @Override
+    	public String toString() { return String.valueOf(state); }
+    }
+
+    // [JACKSON-311]
+    static class PolymorphicType {
+        public String foo;
+        public Object bar;
+        
+        public PolymorphicType() { }
+        public PolymorphicType(String foo, int bar) {
+            this.foo = foo;
+            this.bar = bar;
+        }
+    }
+
+    final static class BeanHolder
+    {
+        public AbstractBean bean;
+        
+        public BeanHolder() { }
+        public BeanHolder(AbstractBean b) { bean = b; }
+    }
+
+    final static class ObjectHolder
+    {
+        public Object value;
+
+        public ObjectHolder() { }
+        public ObjectHolder(Object v) { value = v; }
+    }
+
+    // [JACKSON-352]
+    static class DomainBean {
+        public int weight;
+    }
+
+    static class DiscussBean extends DomainBean {
+        public String subject;
+    }
+
+    static public class DomainBeanWrapper {
+        public String name;
+        public Object myBean;
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    /**
+     * Unit test that verifies that a bean is stored with type information,
+     * when declared type is <code>Object.class</code> (since it is within
+     * Object[]), and default type information is enabled.
+     */
+    public void testBeanAsObject() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        // note: need to wrap, to get declared as Object
+        String str = m.writeValueAsString(new Object[] { new StringBean("abc") });
+
+        _verifySerializationAsMap(str);
+        
+        // Ok: serialization seems to work as expected. Now deserialize:
+        Object ob = m.readValue(str, Object[].class);
+        assertNotNull(ob);
+        Object[] result = (Object[]) ob;
+        assertNotNull(result[0]);
+        assertEquals(StringBean.class, result[0].getClass());
+        assertEquals("abc", ((StringBean) result[0]).name);
+    }
+
+    /**
+     * Unit test that verifies that an abstract bean is stored with type information
+     * if default type information is enabled for non-concrete types.
+     */
+    public void testAbstractBean() throws Exception
+    {
+        // First, let's verify that we'd fail without enabling default type info
+        ObjectMapper m = new ObjectMapper();
+        AbstractBean[] input = new AbstractBean[] { new StringBean("xyz") };
+        String serial = m.writeValueAsString(input);
+        try {
+            m.readValue(serial, AbstractBean[].class);
+            fail("Should have failed");
+        } catch (JsonMappingException e) {
+            // let's use whatever is currently thrown exception... may change tho
+            verifyException(e, "can not construct");
+        }
+        
+        // and then that we will succeed with default type info
+        m = new ObjectMapper();
+        m.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
+        serial = m.writeValueAsString(input);
+        AbstractBean[] beans = m.readValue(serial, AbstractBean[].class);
+        assertEquals(1, beans.length);
+        assertEquals(StringBean.class, beans[0].getClass());
+        assertEquals("xyz", ((StringBean) beans[0]).name);
+    }
+
+    /**
+     * Unit test to verify that type information is included for
+     * all non-final types, if default typing suitably configured
+     */
+    public void testNonFinalBean() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // first: use "object or abstract" typing: should produce no type info:        
+        m.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
+        StringBean bean = new StringBean("x");
+        assertEquals("{\"name\":\"x\"}", m.writeValueAsString(bean));
+        // then non-final, and voila:
+        m = new ObjectMapper();
+        m.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        assertEquals("[\""+StringBean.class.getName()+"\",{\"name\":\"x\"}]",
+            m.writeValueAsString(bean));
+    }
+
+    public void testNullValue() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        BeanHolder h = new BeanHolder();
+        String json = m.writeValueAsString(h);
+        assertNotNull(json);
+        BeanHolder result = m.readValue(json, BeanHolder.class);
+        assertNotNull(result);
+        assertNull(result.bean);
+    }
+    
+    public void testEnumAsObject() throws Exception
+    {
+        // wrapping to be declared as object
+        Object[] input = new Object[] { Choice.YES };
+        Object[] input2 = new Object[] { ComplexChoice.MAYBE};
+        // first, without type info:
+        assertEquals("[\"YES\"]", serializeAsString(input));
+        assertEquals("[\"MAYBE\"]", serializeAsString(input2));
+
+        // and then with it
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+
+        String json = m.writeValueAsString(input);
+        assertEquals("[[\""+Choice.class.getName()+"\",\"YES\"]]", json);
+
+        // which we should get back same way
+        Object[] output = m.readValue(json, Object[].class);
+        assertEquals(1, output.length);
+        assertEquals(Choice.YES, output[0]);
+
+        // ditto for more complicated enum
+        json = m.writeValueAsString(input2);
+        assertEquals("[[\""+ComplexChoice.class.getName()+"\",\"MAYBE\"]]", json);
+        output = m.readValue(json, Object[].class);
+        assertEquals(1, output.length);
+        assertEquals(ComplexChoice.MAYBE, output[0]);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testEnumSet() throws Exception
+    {
+        EnumSet<Choice> set = EnumSet.of(Choice.NO);
+        Object[] input = new Object[] { set };
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        String json = m.writeValueAsString(input);
+        Object[] output = m.readValue(json, Object[].class);
+        assertEquals(1, output.length);
+        Object ob = output[0];
+        assertTrue(ob instanceof EnumSet<?>);
+        EnumSet<Choice> set2 = (EnumSet<Choice>) ob;
+        assertEquals(1, set2.size());
+        assertTrue(set2.contains(Choice.NO));
+        assertFalse(set2.contains(Choice.YES));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testEnumMap() throws Exception
+    {
+        EnumMap<Choice,String> map = new EnumMap<Choice,String>(Choice.class);
+        map.put(Choice.NO, "maybe");
+        Object[] input = new Object[] { map };
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        String json = m.writeValueAsString(input);
+        Object[] output = m.readValue(json, Object[].class);
+        assertEquals(1, output.length);
+        Object ob = output[0];
+        assertTrue(ob instanceof EnumMap<?,?>);
+        EnumMap<Choice,String> map2 = (EnumMap<Choice,String>) ob;
+        assertEquals(1, map2.size());
+        assertEquals("maybe", map2.get(Choice.NO));
+        assertNull(map2.get(Choice.YES));
+    }
+
+    public void testJackson311() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        String json = mapper.writeValueAsString(new PolymorphicType("hello", 2));
+        PolymorphicType value = mapper.readValue(json, PolymorphicType.class);
+        assertEquals("hello", value.foo);
+        assertEquals(Integer.valueOf(2), value.bar);
+    }
+
+    // Also, let's ensure TokenBuffer gets properly handled
+    public void testTokenBuffer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+
+        // Ok, first test JSON Object containing buffer:
+        TokenBuffer buf = new TokenBuffer(mapper);
+        buf.writeStartObject();
+        buf.writeNumberField("num", 42);
+        buf.writeEndObject();
+        String json = mapper.writeValueAsString(new ObjectHolder(buf));
+        ObjectHolder holder = mapper.readValue(json, ObjectHolder.class);
+        assertNotNull(holder.value);
+        assertSame(TokenBuffer.class, holder.value.getClass());
+        JsonParser jp = ((TokenBuffer) holder.value).asParser();
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.nextToken());
+        jp.close();
+
+        // then as an array:
+        buf = new TokenBuffer(mapper);
+        buf.writeStartArray();
+        buf.writeBoolean(true);
+        buf.writeEndArray();
+        json = mapper.writeValueAsString(new ObjectHolder(buf));
+        holder = mapper.readValue(json, ObjectHolder.class);
+        assertNotNull(holder.value);
+        assertSame(TokenBuffer.class, holder.value.getClass());
+        jp = ((TokenBuffer) holder.value).asParser();
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+        jp.close();
+
+        // and finally as scalar
+        buf = new TokenBuffer(mapper);
+        buf.writeNumber(321);
+        json = mapper.writeValueAsString(new ObjectHolder(buf));
+        holder = mapper.readValue(json, ObjectHolder.class);
+        assertNotNull(holder.value);
+        assertSame(TokenBuffer.class, holder.value.getClass());
+        jp = ((TokenBuffer) holder.value).asParser();
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(321, jp.getIntValue());
+        assertNull(jp.nextToken());
+        jp.close();
+    }
+
+    /**
+     * Test for [JACKSON-352]
+     */
+    public void testIssue352() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping (ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE, JsonTypeInfo.As.PROPERTY);
+        DiscussBean d1 = new DiscussBean();
+        d1.subject = "mouse";
+        d1.weight=88;
+        DomainBeanWrapper wrapper = new DomainBeanWrapper();
+        wrapper.name = "mickey";
+        wrapper.myBean = d1;
+        String json = mapper.writeValueAsString(wrapper);
+        DomainBeanWrapper result = mapper.readValue(json, DomainBeanWrapper.class);
+        assertNotNull(result);
+        assertNotNull(wrapper.myBean);
+        assertSame(DiscussBean.class, wrapper.myBean.getClass());
+    }    
+
+    // Test to ensure we can also use "As.PROPERTY" inclusion and custom property name
+    public void testFeature432() throws Exception
+    {
+        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);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    private void _verifySerializationAsMap(String str) throws Exception
+    {
+        // First: validate that structure looks correct (as Map etc)
+        // note: should look something like:
+        // "[["org.codehaus.jackson.map.jsontype.TestDefaultForObject$StringBean",{"name":"abc"}]]")
+
+        // note: must have default mapper, default typer NOT enabled (to get 'plain' map)
+        ObjectMapper m = new ObjectMapper();
+        List<Object> list = m.readValue(str, List.class);
+        assertEquals(1, list.size()); // no type for main List, just single entry
+        Object entryOb = list.get(0);
+        assertTrue(entryOb instanceof List<?>);
+        // but then type wrapper for bean
+        List<?> entryList = (List<?>)entryOb;
+        assertEquals(2, entryList.size());
+        assertEquals(StringBean.class.getName(), entryList.get(0));
+        assertTrue(entryList.get(1) instanceof Map);
+        Map<?,?> map = (Map<?,?>) entryList.get(1);
+        assertEquals(1, map.size());
+        assertEquals("abc", map.get("name"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java
new file mode 100644
index 0000000..edbdc2f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultForScalars.java
@@ -0,0 +1,112 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests to verify that Java/JSON scalar values (non-structured values)
+ * are handled properly with respect to additional type information.
+ * 
+ * @since 1.5
+ * @author tatu
+ */
+public class TestDefaultForScalars
+    extends BaseMapTest
+{
+    static class Jackson417Bean {
+        public String foo = "bar";
+        public java.io.Serializable bar = new Integer(13);
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    /**
+     * Unit test to verify that limited number of core types do NOT include
+     * type information, even if declared as Object. This is only done for types
+     * that JSON scalar values natively map to: String, Integer and Boolean (and
+     * nulls never have type information)
+     */
+    public void testNumericScalars() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+
+        // no typing for Integer, Double, yes for others
+        assertEquals("[123]", m.writeValueAsString(new Object[] { Integer.valueOf(123) }));
+        assertEquals("[[\"java.lang.Long\",37]]", m.writeValueAsString(new Object[] { Long.valueOf(37) }));
+        assertEquals("[0.25]", m.writeValueAsString(new Object[] { Double.valueOf(0.25) }));
+        assertEquals("[[\"java.lang.Float\",0.5]]", m.writeValueAsString(new Object[] { Float.valueOf(0.5f) }));
+    }
+
+    public void testDateScalars() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+
+        long ts = 12345678L;
+        assertEquals("[[\"java.util.Date\","+ts+"]]",
+                m.writeValueAsString(new Object[] { new Date(ts) }));
+
+        // Calendar is trickier... hmmh. Need to ensure round-tripping
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(ts);
+        String json = m.writeValueAsString(new Object[] { c });
+        assertEquals("[[\""+c.getClass().getName()+"\","+ts+"]]", json);
+        // and let's make sure it also comes back same way:
+        Object[] result = m.readValue(json, Object[].class);
+        assertEquals(1, result.length);
+        assertTrue(result[0] instanceof Calendar);
+        assertEquals(ts, ((Calendar) result[0]).getTimeInMillis());
+    }
+
+    public void testMiscScalars() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+
+        // no typing for Strings, booleans
+        assertEquals("[\"abc\"]", m.writeValueAsString(new Object[] { "abc" }));
+        assertEquals("[true,null,false]", m.writeValueAsString(new Boolean[] { true, null, false }));
+    }
+
+    /**
+     * Test for verifying that contents of "untyped" homogenous arrays are properly
+     * handled,
+     */
+    public void testScalarArrays() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
+        Object[] input = new Object[] {
+                "abc", new Date(1234567), null, Integer.valueOf(456)
+        };
+        String json = m.writeValueAsString(input);
+        assertEquals("[\"abc\",[\"java.util.Date\",1234567],null,456]", json);
+
+        // and should deserialize back as well:
+        Object[] output = m.readValue(json, Object[].class);
+        assertArrayEquals(input, output);
+    }
+
+    /**
+     * Loosely scalar; for [JACKSON-417]
+     */
+    public void test417() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.enableDefaultTyping();
+        Jackson417Bean input = new Jackson417Bean();
+        String json = m.writeValueAsString(input);
+        Jackson417Bean result = m.readValue(json, Jackson417Bean.class);
+        assertEquals(input.foo, result.foo);
+        assertEquals(input.bar, result.bar);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java
new file mode 100644
index 0000000..31f84b5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestDefaultWithCreators.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestDefaultWithCreators
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static abstract class Job
+    {
+        public long id;
+    }
+
+    static class UrlJob extends Job
+    {
+        private final String url;
+        private final int count;
+        
+        @JsonCreator
+        public UrlJob(@JsonProperty("id") long id, @JsonProperty("url") String url,
+                @JsonProperty("count") int count)
+        {
+            this.id = id;
+            this.url = url;
+            this.count = count;
+        }
+
+        public String getUrl() { return url; }
+        public int getCount() { return count; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testWithCreators() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        UrlJob input = new UrlJob(123L, "http://foo", 3);
+        String json = mapper.writeValueAsString(input);
+        assertNotNull(json);
+        Job output = mapper.readValue(json, Job.class);
+        assertNotNull(output);
+        assertSame(UrlJob.class, output.getClass());
+        UrlJob o2 = (UrlJob) output;
+        assertEquals(123L, o2.id);
+        assertEquals("http://foo", o2.getUrl());
+        assertEquals(3, o2.getCount());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestEnumTyping.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestEnumTyping.java
new file mode 100644
index 0000000..de615f8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestEnumTyping.java
@@ -0,0 +1,105 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+ at SuppressWarnings("serial")
+public class TestEnumTyping extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    // note: As.WRAPPER_ARRAY worked initially; but as per [JACKSON-485], As.PROPERTY had issues
+    @JsonTypeInfo(use=JsonTypeInfo.Id.MINIMAL_CLASS, include=JsonTypeInfo.As.PROPERTY)
+    public interface EnumInterface { }
+
+    public enum Tag implements EnumInterface
+    { A, B };
+    
+    static class EnumInterfaceWrapper {
+        public EnumInterface value;
+    }
+    
+    static class EnumInterfaceList extends ArrayList<EnumInterface> { }
+
+    static class TagList extends ArrayList<Tag> { }
+
+    static enum TestEnum { A, B, C; }
+    
+    static class UntypedEnumBean
+    {
+       @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="__type")
+        public Object value;
+
+        public UntypedEnumBean() { }
+        public UntypedEnumBean(TestEnum v) { value = v; }
+
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="__type")
+        public void setValue(Object o) {
+            value = o;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testTagList() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        TagList list = new TagList();
+        list.add(Tag.A);
+        list.add(Tag.B);
+        String json = m.writeValueAsString(list);
+
+        TagList result = m.readValue(json, TagList.class);
+        assertEquals(2, result.size());
+        assertSame(Tag.A, result.get(0));
+        assertSame(Tag.B, result.get(1));
+    }
+
+    public void testEnumInterface() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String json = m.writeValueAsString(Tag.B);
+        
+        EnumInterface result = m.readValue(json, EnumInterface.class);
+        assertSame(Tag.B, result);
+    }
+
+    public void testEnumInterfaceList() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        EnumInterfaceList list = new EnumInterfaceList();
+        list.add(Tag.A);
+        list.add(Tag.B);
+        String json = m.writeValueAsString(list);
+        
+        EnumInterfaceList result = m.readValue(json, EnumInterfaceList.class);
+        assertEquals(2, result.size());
+        assertSame(Tag.A, result.get(0));
+        assertSame(Tag.B, result.get(1));
+    }
+
+    public void testUntypedEnum() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String str = mapper.writeValueAsString(new UntypedEnumBean(TestEnum.B));
+        UntypedEnumBean result = mapper.readValue(str, UntypedEnumBean.class);
+        assertNotNull(result);
+        assertNotNull(result.value);
+        Object ob = result.value;
+        assertSame(TestEnum.class, ob.getClass());
+        assertEquals(TestEnum.B, result.value);
+    }
+    
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java
new file mode 100644
index 0000000..e76d177
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestExternalId.java
@@ -0,0 +1,425 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// Tests for [JACKSON-453]
+public class TestExternalId extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+    
+    static class ExternalBean
+    {
+        @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
+        public Object bean;
+
+        public ExternalBean() { }
+        public ExternalBean(int v) {
+            bean = new ValueBean(v);
+        }
+    }
+
+    // for [Issue#96]
+    static class ExternalBeanWithDefault
+    {
+        @JsonTypeInfo(use=Id.CLASS, include=As.EXTERNAL_PROPERTY, property="extType",
+                defaultImpl=ValueBean.class)
+        public Object bean;
+
+        public ExternalBeanWithDefault() { }
+        public ExternalBeanWithDefault(int v) {
+            bean = new ValueBean(v);
+        }
+    }
+
+    static class ExternalBean3
+    {
+        @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType1")
+        public Object value1;
+        
+        @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType2")
+        public Object value2;
+
+        public int foo;
+        
+        @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType3")
+        public Object value3;
+        
+        public ExternalBean3() { }
+        public ExternalBean3(int v) {
+            value1 = new ValueBean(v);
+            value2 = new ValueBean(v+1);
+            value3 = new ValueBean(v+2);
+            foo = v;
+        }
+    }
+
+    static class ExternalBeanWithCreator
+    {
+        @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
+        public Object value;
+
+        public int foo;
+        
+        @JsonCreator
+        public ExternalBeanWithCreator(@JsonProperty("foo") int f)
+        {
+            foo = f;
+            value = new ValueBean(f);
+        }
+    }
+    
+    @JsonTypeName("vbean")
+    static class ValueBean {
+        public int value;
+        
+        public ValueBean() { }
+        public ValueBean(int v) { value = v; }
+    }
+
+    @JsonTypeName("funk")
+    @JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
+    static class FunkyExternalBean {
+        public int i = 3;
+    }
+
+    // [JACKSON-798]: problems with polymorphic types, external prop
+
+    @JsonSubTypes(value= { @JsonSubTypes.Type(value=Derived1.class, name="d1"),
+            @JsonSubTypes.Type(value=Derived2.class, name="d2") })
+    interface Base {
+        String getBaseProperty();
+    }
+  
+    static class Derived1 implements Base {
+        private String derived1Property;
+        private String baseProperty;
+        protected  Derived1() { throw new IllegalStateException("wrong constructor called"); }
+        
+        @JsonCreator
+        public Derived1(@JsonProperty("derived1Property") String d1p,
+                        @JsonProperty("baseProperty") String bp) {
+            derived1Property = d1p;
+            baseProperty = bp;
+        }
+
+        @Override
+        @JsonProperty public String getBaseProperty() {
+            return baseProperty;
+        }
+
+        @JsonProperty public String getDerived1Property() {
+            return derived1Property;
+        }
+    }
+
+    static class Derived2 implements Base {
+        private String derived2Property;
+        private String baseProperty;
+        protected  Derived2() { throw new IllegalStateException("wrong constructor called"); }
+
+        @JsonCreator
+        public Derived2(@JsonProperty("derived2Property") String d2p,
+                        @JsonProperty("baseProperty") String bp) {
+            derived2Property = d2p;
+            baseProperty = bp;
+        }
+
+        @Override
+        @JsonProperty public String getBaseProperty() {
+            return baseProperty;
+        }
+
+        @JsonProperty public String getDerived2Property() {
+            return derived2Property;
+        }
+    }
+    
+    static class BaseContainer {
+        protected final Base base;
+        protected final String baseContainerProperty;
+        protected BaseContainer() { throw new IllegalStateException("wrong constructor called"); }
+
+        @JsonCreator
+        public BaseContainer(@JsonProperty("baseContainerProperty") String bcp, @JsonProperty("base") Base b) {
+            baseContainerProperty = bcp;
+            base = b;
+        }
+
+        @JsonProperty
+        public String getBaseContainerProperty() { return baseContainerProperty; }
+
+        @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="type")
+        @JsonProperty
+        public Base getBase() { return base; }
+    }
+
+    // [JACKSON-831]: should allow a property to map id to as well
+    
+    interface Pet {}
+
+    static class Dog implements Pet {
+        public String name;
+    }
+
+    static class House831 {
+        protected String petType;
+
+        @JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "petType")
+        @JsonSubTypes({@JsonSubTypes.Type(name = "dog", value = Dog.class)})
+        public Pet pet;
+
+        public String getPetType() {
+            return petType;
+        }
+
+        public void setPetType(String petType) {
+            this.petType = petType;
+        }
+    }    
+
+    // for [Issue#118]
+    static class ExternalTypeWithNonPOJO {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+                property = "type",
+                visible = true,
+                include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+                defaultImpl = String.class)
+        @JsonSubTypes({
+            @JsonSubTypes.Type(value = Date.class, name = "date"),
+            @JsonSubTypes.Type(value = AsValueThingy.class, name = "thingy")
+        })
+        public Object value;
+
+        public ExternalTypeWithNonPOJO() { }
+        public ExternalTypeWithNonPOJO(Object o) { value = o; }
+    }    
+
+    // for [Issue#119]
+    static class AsValueThingy {
+        public long rawDate;
+
+        public AsValueThingy(long l) { rawDate = l; }
+        public AsValueThingy() { }
+        
+        @JsonValue public Date serialization() {
+            return new Date(rawDate);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, serialization
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleSerialization() throws Exception
+    {
+        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,
+        // 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
+        // anyway.
+        assertEquals("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}",
+                mapper.writeValueAsString(new ExternalBean(11)));
+    }
+
+    // If trying to use with Class, should just become "PROPERTY" instead:
+    public void testImproperExternalIdSerialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("{\"extType\":\"funk\",\"i\":3}",
+                mapper.writeValueAsString(new FunkyExternalBean()));
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, deserialization
+    /**********************************************************
+     */
+    
+    public void testSimpleDeserialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerSubtypes(ValueBean.class);
+        ExternalBean result = mapper.readValue("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}", ExternalBean.class);
+        assertNotNull(result);
+        assertNotNull(result.bean);
+        ValueBean vb = (ValueBean) result.bean;
+        assertEquals(11, vb.value);
+    }
+
+    // Test for verifying that it's ok to have multiple (say, 3)
+    // externally typed things, mixed with other stuff...
+    public void testMultipleTypeIdsDeserialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerSubtypes(ValueBean.class);
+        String json = mapper.writeValueAsString(new ExternalBean3(3));
+        ExternalBean3 result = mapper.readValue(json, ExternalBean3.class);
+        assertNotNull(result);
+        assertNotNull(result.value1);
+        assertNotNull(result.value2);
+        assertNotNull(result.value3);
+        assertEquals(3, ((ValueBean)result.value1).value);
+        assertEquals(4, ((ValueBean)result.value2).value);
+        assertEquals(5, ((ValueBean)result.value3).value);
+        assertEquals(3, result.foo);
+    }
+
+    // Also, it should be ok to use @JsonCreator as well...
+    public void testExternalTypeWithCreator() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerSubtypes(ValueBean.class);
+        String json = mapper.writeValueAsString(new ExternalBeanWithCreator(7));
+        ExternalBeanWithCreator result = mapper.readValue(json, ExternalBeanWithCreator.class);
+        assertNotNull(result);
+        assertNotNull(result.value);
+        assertEquals(7, ((ValueBean)result.value).value);
+        assertEquals(7, result.foo);
+    }
+    
+    // If trying to use with Class, should just become "PROPERTY" instead:
+    public void testImproperExternalIdDeserialization() throws Exception
+    {
+        FunkyExternalBean result = MAPPER.readValue("{\"extType\":\"funk\",\"i\":3}",
+                FunkyExternalBean.class);
+        assertNotNull(result);
+        assertEquals(3, result.i);
+    }
+
+    public void testIssue798() throws Exception
+    {
+        Base base = new Derived1("derived1 prop val", "base prop val");
+        BaseContainer baseContainer = new BaseContainer("bc prop val", base);
+        String generatedJson = MAPPER.writeValueAsString(baseContainer);
+        BaseContainer baseContainer2 = MAPPER.readValue(generatedJson,BaseContainer.class);
+        assertEquals("bc prop val", baseContainer.getBaseContainerProperty());
+
+        Base b = baseContainer2.getBase();
+        assertNotNull(b);
+        if (b.getClass() != Derived1.class) {
+            fail("Should have type Derived1, was "+b.getClass().getName());
+        }
+
+        Derived1 derived1 = (Derived1) b;
+        assertEquals("base prop val", derived1.getBaseProperty());
+        assertEquals("derived1 prop val", derived1.getDerived1Property());
+    }
+
+    // There seems to be some problems if type is also visible...
+    public void testIssue831() throws Exception
+    {
+        final String JSON = "{ \"petType\": \"dog\",\n"
+                +"\"pet\": { \"name\": \"Pluto\" }\n}";
+        House831 result = MAPPER.readValue(JSON, House831.class);
+        assertNotNull(result);
+        assertNotNull(result.pet);
+        assertSame(Dog.class, result.pet.getClass());
+        assertEquals("dog", result.petType);
+    }
+
+    // For [Issue#96]: should allow use of default impl, if property missing
+    /* 18-Jan-2013, tatu: Unfortunately this collides with [Issue#118], and I don't
+     *   know what the best resolution is. For now at least 
+     */
+    /*
+    public void testWithDefaultAndMissing() throws Exception
+    {
+        ExternalBeanWithDefault input = new ExternalBeanWithDefault(13);
+        // baseline: include type, verify things work:
+        String fullJson = MAPPER.writeValueAsString(input);
+        ExternalBeanWithDefault output = MAPPER.readValue(fullJson, ExternalBeanWithDefault.class);
+        assertNotNull(output);
+        assertNotNull(output.bean);
+        // and then try without type info...
+        ExternalBeanWithDefault defaulted = MAPPER.readValue("{\"bean\":{\"value\":13}}",
+                ExternalBeanWithDefault.class);
+        assertNotNull(defaulted);
+        assertNotNull(defaulted.bean);
+        assertSame(ValueBean.class, defaulted.bean.getClass());
+    }
+    */
+
+    // For [Issue#118]
+    // Note: String works fine, since no type id will used; other scalar types have issues
+    public void testWithScalar118() throws Exception
+    {
+        ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(new java.util.Date(123L));
+        String json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+
+        // and back just to be sure:
+        ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
+        assertNotNull(result.value);
+        assertTrue(result.value instanceof java.util.Date);
+    }
+
+    // For [Issue#118] using "natural" type(s)
+    public void testWithNaturalScalar118() throws Exception
+    {
+        ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(Integer.valueOf(13));
+        String json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+        // and back just to be sure:
+        ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
+        assertNotNull(result.value);
+        assertTrue(result.value instanceof Integer);
+
+        // ditto with others types
+        input = new ExternalTypeWithNonPOJO(Boolean.TRUE);
+        json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+        result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
+        assertNotNull(result.value);
+        assertTrue(result.value instanceof Boolean);
+
+        input = new ExternalTypeWithNonPOJO("foobar");
+        json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+        result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
+        assertNotNull(result.value);
+        assertTrue(result.value instanceof String);
+        assertEquals("foobar", result.value);
+    }
+    
+    // For [Issue#119]... and bit of [#167] as well
+    public void testWithAsValue() throws Exception
+    {
+        ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(new AsValueThingy(12345L));
+        String json = MAPPER.writeValueAsString(input);
+        assertNotNull(json);
+        assertEquals("{\"value\":12345,\"type\":\"date\"}", json);
+
+        // and get it back too:
+        ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
+        assertNotNull(result);
+        assertNotNull(result.value);
+        /* 13-Feb-2013, tatu: Urgh. I don't think this can work quite as intended...
+         *   since POJO type, and type of thing @JsonValue annotated method returns
+         *   are not related. Best we can do is thus this:
+         */
+        /*
+        assertEquals(AsValueThingy.class, result.value.getClass());
+        assertEquals(12345L, ((AsValueThingy) result.value).rawDate);
+        */
+        assertEquals(Date.class, result.value.getClass());
+        assertEquals(12345L, ((Date) result.value).getTime());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
new file mode 100644
index 0000000..365b466
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestGenericListSerialization.java
@@ -0,0 +1,93 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestGenericListSerialization
+    extends BaseMapTest
+{
+    // [JACKSON-356]
+    public static class JSONResponse<T> {
+
+        private T result;
+
+        public T getResult() {
+            return result;
+        }
+
+        public void setResult(T result) {
+            this.result = result;
+        }
+    } 
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
+    public static class Parent {
+        public String parentContent = "PARENT";
+    }
+
+    public static class Child1 extends Parent {
+        public String childContent1 = "CHILD1";
+    }
+
+    public static class Child2 extends Parent {
+        public String childContent2 = "CHILD2";
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSubTypesFor356() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        /* 06-Sep-2010, tatus: This was not fixed for 1.6; and to keep junit test
+         *   suite green, let's not run it for versions prior to 1.7...
+         */
+        Version v = mapper.version();
+        if (v.getMajorVersion() == 1 && v.getMinorVersion() == 6) {
+            System.err.println("Note: skipping test for Jackson 1.6");
+            return;
+        }
+        
+        JSONResponse<List<Parent>> input = new JSONResponse<List<Parent>>();
+
+        List<Parent> embedded = new ArrayList<Parent>();
+        embedded.add(new Child1());
+        embedded.add(new Child2());
+        input.setResult(embedded);
+        mapper.configure(MapperFeature.USE_STATIC_TYPING, true);
+
+        JavaType rootType = TypeFactory.defaultInstance().constructType(new TypeReference<JSONResponse<List<Parent>>>() { });
+        byte[] json = mapper.writerWithType(rootType).writeValueAsBytes(input);
+//        byte[] json = mapper.writeValueAsBytes(input);
+
+//        System.out.println("After Serialization: " + new String(json));
+        
+        JSONResponse<List<Parent>> out = mapper.readValue(json, 0, json.length, rootType);
+
+        List<Parent> deserializedContent = (List<Parent>) out.getResult();
+
+        assertEquals(2, deserializedContent.size());
+        assertTrue(deserializedContent.get(0) instanceof Parent);
+        assertTrue(deserializedContent.get(0) instanceof Child1);
+        assertFalse(deserializedContent.get(0) instanceof Child2);
+        assertTrue(deserializedContent.get(1) instanceof Child2);
+        assertFalse(deserializedContent.get(1) instanceof Child1);
+
+        assertEquals("PARENT", ((Child1) deserializedContent.get(0)).parentContent);
+        assertEquals("PARENT", ((Child2) deserializedContent.get(1)).parentContent);
+        assertEquals("CHILD1", ((Child1) deserializedContent.get(0)).childContent1);
+        assertEquals("CHILD2", ((Child2) deserializedContent.get(1)).childContent2);
+    }
+    
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
new file mode 100644
index 0000000..2f59cfb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestNoTypeInfo.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+public class TestNoTypeInfo extends BaseMapTest
+{
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NONE)
+    @JsonDeserialize(as=NoType.class)
+    private static interface NoTypeInterface {
+    }
+    
+    private final static class NoType implements NoTypeInterface {
+        public int a = 3;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    // for [JACKSON-746]
+    public void testWithIdNone() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.enableDefaultTyping();
+        // serialize without type info
+        String json = mapper.writeValueAsString(new NoType());
+        assertEquals("{\"a\":3}", json);
+
+        // and deserialize successfully
+        NoTypeInterface bean = mapper.readValue("{\"a\":6}", NoTypeInterface.class);
+        assertNotNull(bean);
+        NoType impl = (NoType) bean;
+        assertEquals(6, impl.a);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
new file mode 100644
index 0000000..706bdd2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestPropertyTypeInfo.java
@@ -0,0 +1,201 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Testing to verify that {@link JsonTypeInfo} works
+ * for properties as well as types (see [JACKSON-280] for details)
+ */
+ at SuppressWarnings("serial")
+public class TestPropertyTypeInfo extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    static class FieldWrapperBean
+    {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public Object value;
+
+        public FieldWrapperBean() { }
+        public FieldWrapperBean(Object o) { value = o; }
+    }
+
+    static class FieldWrapperBeanList extends ArrayList<FieldWrapperBean> { }
+    static class FieldWrapperBeanMap extends HashMap<String,FieldWrapperBean> { }
+    static class FieldWrapperBeanArray {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public FieldWrapperBean[] beans;
+
+        public FieldWrapperBeanArray() { }
+        public FieldWrapperBeanArray(FieldWrapperBean[] beans) { this.beans = beans; }
+    }
+    
+    static class MethodWrapperBean
+    {
+        private Object value;
+        
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public Object getValue() { return value; }
+
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public void setValue(Object v) { value = v; }
+        
+        public MethodWrapperBean() { }
+        public MethodWrapperBean(Object o) { value = o; }
+    }
+    
+    static class MethodWrapperBeanList extends ArrayList<MethodWrapperBean> { }
+    static class MethodWrapperBeanMap extends HashMap<String,MethodWrapperBean> { }
+    static class MethodWrapperBeanArray {
+        protected MethodWrapperBean[] beans;
+
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public MethodWrapperBean[] getValue() { return beans; }
+
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+        public void setValue(MethodWrapperBean[] v) { beans = v; }
+        
+        public MethodWrapperBeanArray() { }
+        public MethodWrapperBeanArray(MethodWrapperBean[] beans) { this.beans = beans; }
+    }
+
+    static class OtherBean {
+        public int x = 1, y = 1;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testSimpleField() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new FieldWrapperBean(new StringWrapper("foo")));
+//System.out.println("JSON/field+object == "+json);
+        FieldWrapperBean bean = mapper.readValue(json, FieldWrapperBean.class);
+        assertNotNull(bean.value);
+        assertEquals(StringWrapper.class, bean.value.getClass());
+        assertEquals(((StringWrapper) bean.value).str, "foo");
+    }
+
+    public void testSimpleMethod() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new FieldWrapperBean(new IntWrapper(37)));
+//System.out.println("JSON/method+object == "+json);
+        FieldWrapperBean bean = mapper.readValue(json, FieldWrapperBean.class);
+        assertNotNull(bean.value);
+        assertEquals(IntWrapper.class, bean.value.getClass());
+        assertEquals(((IntWrapper) bean.value).i, 37);
+    }
+
+    public void testSimpleListField() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FieldWrapperBeanList list = new FieldWrapperBeanList();
+        list.add(new FieldWrapperBean(new OtherBean()));
+        String json = mapper.writeValueAsString(list);
+//System.out.println("JSON/field+list == "+json);
+        FieldWrapperBeanList result = mapper.readValue(json, FieldWrapperBeanList.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        FieldWrapperBean bean = list.get(0);
+        assertEquals(OtherBean.class, bean.value.getClass());
+        assertEquals(((OtherBean) bean.value).x, 1);
+        assertEquals(((OtherBean) bean.value).y, 1);
+    }
+
+    public void testSimpleListMethod() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        MethodWrapperBeanList list = new MethodWrapperBeanList();
+        list.add(new MethodWrapperBean(new BooleanWrapper(true)));
+        list.add(new MethodWrapperBean(new StringWrapper("x")));
+        list.add(new MethodWrapperBean(new OtherBean()));
+        String json = mapper.writeValueAsString(list);
+        MethodWrapperBeanList result = mapper.readValue(json, MethodWrapperBeanList.class);
+        assertNotNull(result);
+        assertEquals(3, result.size());
+        MethodWrapperBean bean = result.get(0);
+        assertEquals(BooleanWrapper.class, bean.value.getClass());
+        assertEquals(((BooleanWrapper) bean.value).b, Boolean.TRUE);
+        bean = result.get(1);
+        assertEquals(StringWrapper.class, bean.value.getClass());
+        assertEquals(((StringWrapper) bean.value).str, "x");
+        bean = result.get(2);
+        assertEquals(OtherBean.class, bean.value.getClass());
+    }
+
+    public void testSimpleArrayField() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FieldWrapperBeanArray array = new FieldWrapperBeanArray(new
+                FieldWrapperBean[] { new FieldWrapperBean(new BooleanWrapper(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);
+    }
+
+    public void testSimpleArrayMethod() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        MethodWrapperBeanArray array = new MethodWrapperBeanArray(new
+                MethodWrapperBean[] { new MethodWrapperBean(new StringWrapper("A")) });
+        String json = mapper.writeValueAsString(array);
+        MethodWrapperBeanArray result = mapper.readValue(json, MethodWrapperBeanArray.class);
+        assertNotNull(result);
+        MethodWrapperBean[] beans = result.beans;
+        assertEquals(1, beans.length);
+        MethodWrapperBean bean = beans[0];
+        assertEquals(StringWrapper.class, bean.value.getClass());
+        assertEquals(((StringWrapper) bean.value).str, "A");
+    }
+    
+    public void testSimpleMapField() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FieldWrapperBeanMap map = new FieldWrapperBeanMap();
+        map.put("foop", new FieldWrapperBean(new IntWrapper(13)));
+        String json = mapper.writeValueAsString(map);
+        FieldWrapperBeanMap result = mapper.readValue(json, FieldWrapperBeanMap.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        FieldWrapperBean bean = result.get("foop");
+        assertNotNull(bean);
+        Object ob = bean.value;
+        assertEquals(IntWrapper.class, ob.getClass());
+        assertEquals(((IntWrapper) ob).i, 13);
+    }
+
+    public void testSimpleMapMethod() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        MethodWrapperBeanMap map = new MethodWrapperBeanMap();
+        map.put("xyz", new MethodWrapperBean(new BooleanWrapper(true)));
+        String json = mapper.writeValueAsString(map);
+        MethodWrapperBeanMap result = mapper.readValue(json, MethodWrapperBeanMap.class);
+        assertNotNull(result);
+        assertEquals(1, result.size());
+        MethodWrapperBean bean = result.get("xyz");
+        assertNotNull(bean);
+        Object ob = bean.value;
+        assertEquals(BooleanWrapper.class, ob.getClass());
+        assertEquals(((BooleanWrapper) 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
new file mode 100644
index 0000000..d6be8f6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestScalars.java
@@ -0,0 +1,106 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestScalars extends BaseMapTest
+{
+    private static class DynamicWrapper {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
+        public Object value;
+        
+        @SuppressWarnings("unused")
+        public DynamicWrapper() { }
+        public DynamicWrapper(Object v) { value = v; }
+    }
+
+    static enum TestEnum { A, B; }
+
+    private static class AbstractWrapper {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
+        public Serializable value;
+        
+        @SuppressWarnings("unused")
+        public AbstractWrapper() { }
+        public AbstractWrapper(Serializable v) { value = v; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    /**
+     * Ensure that per-property dynamic types work, both for "native" types
+     * and others
+     */
+    public void testScalarsWithTyping() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String json;
+        DynamicWrapper result;
+
+        // first, check "native" types
+        json = m.writeValueAsString(new DynamicWrapper(Integer.valueOf(3)));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals(Integer.valueOf(3), result.value);
+
+        json = m.writeValueAsString(new DynamicWrapper("abc"));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals("abc", result.value);
+
+        json = m.writeValueAsString(new DynamicWrapper("abc"));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals("abc", result.value);
+
+        json = m.writeValueAsString(new DynamicWrapper(Boolean.TRUE));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals(Boolean.TRUE, result.value);
+        
+        // then verify other scalars
+        json = m.writeValueAsString(new DynamicWrapper(Long.valueOf(7L)));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals(Long.valueOf(7), result.value);
+
+        json = m.writeValueAsString(new DynamicWrapper(TestEnum.B));
+        result = m.readValue(json, DynamicWrapper.class);
+        assertEquals(TestEnum.B, result.value);
+    }
+
+    public void testScalarsViaAbstractType() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String json;
+        AbstractWrapper result;
+
+        // first, check "native" types
+        json = m.writeValueAsString(new AbstractWrapper(Integer.valueOf(3)));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals(Integer.valueOf(3), result.value);
+
+        json = m.writeValueAsString(new AbstractWrapper("abc"));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals("abc", result.value);
+
+        json = m.writeValueAsString(new AbstractWrapper("abc"));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals("abc", result.value);
+
+        json = m.writeValueAsString(new AbstractWrapper(Boolean.TRUE));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals(Boolean.TRUE, result.value);
+        
+        // then verify other scalars
+        json = m.writeValueAsString(new AbstractWrapper(Long.valueOf(7L)));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals(Long.valueOf(7), result.value);
+
+        json = m.writeValueAsString(new AbstractWrapper(TestEnum.B));
+        result = m.readValue(json, AbstractWrapper.class);
+        assertEquals(TestEnum.B, result.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
new file mode 100644
index 0000000..29f83a5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestSubtypes.java
@@ -0,0 +1,215 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class TestSubtypes extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME)
+    static abstract class SuperType {
+    }
+
+    @JsonTypeName("TypeB")
+    static class SubB extends SuperType {
+        public int b = 1;
+    }
+
+    static class SubC extends SuperType {
+        public int c = 2;
+    }
+
+    static class SubD extends SuperType {
+        public int d;
+    }
+
+    // "Empty" bean, to test [JACKSON-366]
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME)
+    static abstract class BaseBean { }
+    
+    static class EmptyBean extends BaseBean { }
+
+    static class EmptyNonFinal { }
+
+    // Verify combinations with [JACKSON-510]
+
+    static class PropertyBean
+    {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.NAME)
+        public SuperType value;
+        
+        public PropertyBean() { this(null); }
+        public PropertyBean(SuperType v) { value = v; }
+    }
+
+    // And then [JACKSON-614]
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY,
+            property="#type",
+            defaultImpl=DefaultImpl.class)
+    static abstract class SuperTypeWithDefault { }
+
+    static class DefaultImpl extends SuperTypeWithDefault {
+        public int a;
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=As.PROPERTY, property="#type")
+    static abstract class SuperTypeWithoutDefault { }
+
+    static class DefaultImpl505 extends SuperTypeWithoutDefault {
+        public int a;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    // JACKSON-510
+    public void testPropertyWithSubtypes() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // must register subtypes
+        mapper.registerSubtypes(SubB.class, SubC.class, SubD.class);
+        String json = mapper.writeValueAsString(new PropertyBean(new SubC()));
+        PropertyBean result = mapper.readValue(json, PropertyBean.class);
+        assertSame(SubC.class, result.value.getClass());
+    }
+
+    // [JACKSON-748]: also works via modules
+    public void testSubtypesViaModule() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule();
+        module.registerSubtypes(SubB.class, SubC.class, SubD.class);
+        mapper.registerModule(module);
+        String json = mapper.writeValueAsString(new PropertyBean(new SubC()));
+        PropertyBean result = mapper.readValue(json, PropertyBean.class);
+        assertSame(SubC.class, result.value.getClass());
+    }
+    
+    public void testSerialization() throws Exception
+    {
+        // serialization can detect type name ok without anything extra:
+        SubB bean = new SubB();
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("{\"@type\":\"TypeB\",\"b\":1}", mapper.writeValueAsString(bean));
+
+        // but we can override type name here too
+        mapper = new ObjectMapper();
+        mapper.registerSubtypes(new NamedType(SubB.class, "typeB"));
+        assertEquals("{\"@type\":\"typeB\",\"b\":1}", mapper.writeValueAsString(bean));
+
+        // and default name ought to be simple class name; with context
+        assertEquals("{\"@type\":\"TestSubtypes$SubD\",\"d\":0}", mapper.writeValueAsString(new SubD()));  
+    }
+
+    public void testDeserializationNonNamed() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerSubtypes(SubC.class);
+
+        // default name should be unqualified class name
+        SuperType bean = mapper.readValue("{\"@type\":\"TestSubtypes$SubC\", \"c\":1}", SuperType.class);
+        assertSame(SubC.class, bean.getClass());
+        assertEquals(1, ((SubC) bean).c);
+    }
+
+    public void testDeserializatioNamed() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerSubtypes(SubB.class);
+        mapper.registerSubtypes(new NamedType(SubD.class, "TypeD"));
+
+        SuperType bean = mapper.readValue("{\"@type\":\"TypeB\", \"b\":13}", SuperType.class);
+        assertSame(SubB.class, bean.getClass());
+        assertEquals(13, ((SubB) bean).b);
+
+        // but we can also explicitly register name too
+        bean = mapper.readValue("{\"@type\":\"TypeD\", \"d\":-4}", SuperType.class);
+        assertSame(SubD.class, bean.getClass());
+        assertEquals(-4, ((SubD) bean).d);
+    }
+
+    // Trying to reproduce [JACKSON-366]
+    public void testEmptyBean() throws Exception
+    {
+        // First, with annotations
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true);
+        String json = mapper.writeValueAsString(new EmptyBean());
+        assertEquals("{\"@type\":\"TestSubtypes$EmptyBean\"}", json);
+
+        mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        json = mapper.writeValueAsString(new EmptyBean());
+        assertEquals("{\"@type\":\"TestSubtypes$EmptyBean\"}", json);
+
+        // and then with defaults
+        mapper = new ObjectMapper();
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
+        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        json = mapper.writeValueAsString(new EmptyNonFinal());
+        assertEquals("[\"com.fasterxml.jackson.databind.jsontype.TestSubtypes$EmptyNonFinal\",{}]", json);
+    }
+
+    public void testDefaultImpl() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // first, test with no type information
+        SuperTypeWithDefault bean = mapper.readValue("{\"a\":13}", SuperTypeWithDefault.class);
+        assertEquals(DefaultImpl.class, bean.getClass());
+        assertEquals(13, ((DefaultImpl) bean).a);
+
+        // and then with unmapped info
+        bean = mapper.readValue("{\"a\":14,\"#type\":\"foobar\"}", SuperTypeWithDefault.class);
+        assertEquals(DefaultImpl.class, bean.getClass());
+        assertEquals(14, ((DefaultImpl) bean).a);
+
+        bean = mapper.readValue("{\"#type\":\"foobar\",\"a\":15}", SuperTypeWithDefault.class);
+        assertEquals(DefaultImpl.class, bean.getClass());
+        assertEquals(15, ((DefaultImpl) bean).a);
+
+        bean = mapper.readValue("{\"#type\":\"foobar\"}", SuperTypeWithDefault.class);
+        assertEquals(DefaultImpl.class, bean.getClass());
+        assertEquals(0, ((DefaultImpl) bean).a);
+    }
+
+    // [JACKSON-505]: ok to also default to mapping there might be for base type
+    public void testDefaultImplViaModule() throws Exception
+    {
+        final String JSON = "{\"a\":123}";
+        
+        // first: without registration etc, epic fail:
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            mapper.readValue(JSON, SuperTypeWithoutDefault.class);
+            fail("Expected an exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, "missing property");
+        }
+
+        // but then succeed when we register default impl
+        mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addAbstractTypeMapping(SuperTypeWithoutDefault.class, DefaultImpl505.class);
+        mapper.registerModule(module);
+        SuperTypeWithoutDefault bean = mapper.readValue(JSON, SuperTypeWithoutDefault.class);
+        assertNotNull(bean);
+        assertEquals(DefaultImpl505.class, bean.getClass());
+        assertEquals(123, ((DefaultImpl505) bean).a);
+
+        bean = mapper.readValue("{\"#type\":\"foobar\"}", SuperTypeWithoutDefault.class);
+        assertEquals(DefaultImpl505.class, bean.getClass());
+        assertEquals(0, ((DefaultImpl505) bean).a);
+    
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java
new file mode 100644
index 0000000..c7ed350
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypeNames.java
@@ -0,0 +1,153 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.annotation.*;
+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.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> { }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    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) }));
+        assertEquals("[{\"MaineCoon\":{\"name\":\"Belzebub\",\"purrs\":true}}]",
+                m.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,
+                TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Animal.class));
+        assertEquals(input.length, output.size());
+        for (int i = 0, len = input.length; i < len; ++i) {
+            assertEquals("Entry #"+i+" differs, input = '"+json+"'",
+                input[i], output.get(i));
+        }
+    }
+
+    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);
+        assertEquals(input, output);
+    }
+}
+
+/*
+/**********************************************************
+/* Helper types
+/**********************************************************
+ */
+
+ at JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
+ at JsonSubTypes({
+    @Type(value=Dog.class, name="doggy"),
+    @Type(Cat.class) /* defaults to "TestTypedNames$Cat" then */
+})
+class Animal
+{
+    public String name;
+
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+        return name.equals(((Animal) o).name);
+    }
+
+}
+
+class Dog extends Animal
+{
+    public int ageInYears;
+
+    public Dog() { }
+    public Dog(String n, int y) {
+        name = n;
+        ageInYears = y;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return super.equals(o)
+            && ((Dog) o).ageInYears == ageInYears;
+    }
+}
+
+ at JsonSubTypes({
+    @Type(MaineCoon.class),
+    @Type(Persian.class)
+})
+abstract class Cat extends Animal {
+    public boolean purrs;
+    public Cat() { }
+    public Cat(String n, boolean p) {
+        name = n;
+        purrs = p;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return super.equals(o) && ((Cat) o).purrs == purrs;
+    }
+}
+
+/* uses default name ("MaineCoon") since there's no @JsonTypeName,
+ * nor did supertype specify name
+ */
+class MaineCoon extends Cat {
+    public MaineCoon() { super(); }
+    public MaineCoon(String n, boolean p) {
+        super(n, p);
+    }
+}
+
+ at JsonTypeName("persialaisKissa")
+class Persian extends Cat {
+    public Persian() { super(); }
+    public Persian(String n, boolean p) {
+        super(n, p);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java
new file mode 100644
index 0000000..f9812bb
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArrayDeserialization.java
@@ -0,0 +1,114 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestTypedArrayDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Let's claim we need type here too (although we won't
+     * really use any sub-classes)
+     */
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+    static class TypedList<T> extends ArrayList<T> { }
+
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
+    static class TypedListAsProp<T> extends ArrayList<T> { }
+    
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_OBJECT)
+    static class TypedListAsWrapper<T> extends LinkedList<T> { }
+    
+    // Mix-in to force wrapper for things like primitive arrays
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_OBJECT)
+    interface WrapperMixIn { }
+
+    /*
+    /**********************************************************
+    /* Unit tests, Lists
+    /**********************************************************
+     */
+    
+    public void testIntList() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // uses WRAPPER_OBJECT inclusion
+        String JSON = "{\""+TypedListAsWrapper.class.getName()+"\":[4,5, 6]}";
+        JavaType type = TypeFactory.defaultInstance().constructCollectionType(TypedListAsWrapper.class, Integer.class);        
+        TypedListAsWrapper<Integer> result = m.readValue(JSON, type);
+        assertNotNull(result);
+        assertEquals(3, result.size());
+        assertEquals(Integer.valueOf(4), result.get(0));
+        assertEquals(Integer.valueOf(5), result.get(1));
+        assertEquals(Integer.valueOf(6), result.get(2));
+    }
+
+    /**
+     * Similar to above, but this time let's request adding type info
+     * as property. That would not work (since there's no JSON Object to
+     * add property in), so it will basically be same as using WRAPPER_ARRAY
+     */
+    public void testBooleanListAsProp() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // tries to use PROPERTY inclusion; but for ARRAYS (and scalars) will become ARRAY_WRAPPER
+        String JSON = "[\""+TypedListAsProp.class.getName()+"\",[true, false]]";
+        JavaType type = TypeFactory.defaultInstance().constructCollectionType(TypedListAsProp.class, Boolean.class);        
+        TypedListAsProp<Object> result = m.readValue(JSON, type);
+        assertNotNull(result);
+        assertEquals(2, result.size());
+        assertEquals(Boolean.TRUE, result.get(0));
+        assertEquals(Boolean.FALSE, result.get(1));
+    }
+
+    public void testLongListAsWrapper() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // uses OBJECT_ARRAY, works just fine
+        
+        String JSON = "{\""+TypedListAsWrapper.class.getName()+"\":[1, 3]}";
+        JavaType type = TypeFactory.defaultInstance().constructCollectionType(TypedListAsWrapper.class, Long.class);        
+        TypedListAsWrapper<Object> result = m.readValue(JSON, type);
+        assertNotNull(result);
+        assertEquals(2, result.size());
+
+        assertEquals(Long.class, result.get(0).getClass());
+        assertEquals(Long.valueOf(1), result.get(0));
+        assertEquals(Long.class, result.get(1).getClass());
+        assertEquals(Long.valueOf(3), result.get(1));
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, primitive arrays
+    /**********************************************************
+     */
+
+    public void testLongArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // use class name, WRAPPER_OBJECT
+        m.addMixInAnnotations(long[].class, WrapperMixIn.class);
+        String JSON = "{\""+long[].class.getName()+"\":[5, 6, 7]}";
+        long[] value = m.readValue(JSON, long[].class);
+        assertNotNull(value);
+        assertEquals(3, value.length);
+        assertArrayEquals(new long[] { 5L, 6L, 7L} , value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
new file mode 100644
index 0000000..aed0f81
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedArraySerialization.java
@@ -0,0 +1,155 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that types that serialize as JSON Arrays
+ * get properly serialized with types (esp. for contents, and
+ * gracefully handling Lists themselves too)
+ */
+public class TestTypedArraySerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Let's claim we need type here too (although we won't
+     * really use any sub-classes)
+     */
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_ARRAY)
+    static class TypedList<T> extends ArrayList<T> { }
+
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY)
+    static class TypedListAsProp<T> extends ArrayList<T> { }
+    
+    @SuppressWarnings("serial")
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
+    static class TypedListAsWrapper<T> extends LinkedList<T> { }
+    
+    // Mix-in to force wrapper for things like primitive arrays
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.WRAPPER_OBJECT)
+    interface WrapperMixIn { }
+
+    // for [JACKSON-341]
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
+    @JsonSubTypes({ @JsonSubTypes.Type(B.class) })
+    interface A { }
+
+    @JsonTypeName("BB")
+    static class B implements A {
+        public int value = 2;
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY)
+    @JsonTypeName("bean")
+    static class Bean {
+        public int x = 0;
+    }
+
+    static class BeanListWrapper {
+        @JsonView({Object.class})
+        public List<Bean> beans = new ArrayList<Bean>();
+        {
+            beans.add(new Bean());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, Lists
+    /**********************************************************
+     */
+
+    public void testListWithPolymorphic() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        BeanListWrapper beans = new BeanListWrapper();
+        assertEquals("{\"beans\":[{\"@type\":\"bean\",\"x\":0}]}", mapper.writeValueAsString(beans));
+        // Related to [JACKSON-364]
+        ObjectWriter w = mapper.writerWithView(Object.class);
+        assertEquals("{\"beans\":[{\"@type\":\"bean\",\"x\":0}]}", w.writeValueAsString(beans));
+    }
+    
+    public void testIntList() throws Exception
+    {
+        TypedList<Integer> input = new TypedList<Integer>();
+        input.add(5);
+        input.add(13);
+        // uses WRAPPER_ARRAY inclusion:
+        assertEquals("[\""+TypedList.class.getName()+"\",[5,13]]", serializeAsString(input));
+    }
+    
+    // Similar to above, but this time let's request adding type info
+    // as property. That would not work (since there's no JSON Object to
+    // add property in), so it should revert to method used with
+    // ARRAY_WRAPPER method.
+    public void testStringListAsProp() throws Exception
+    {
+        TypedListAsProp<String> input = new TypedListAsProp<String>();
+        input.add("a");
+        input.add("b");
+        assertEquals("[\""+TypedListAsProp.class.getName()+"\",[\"a\",\"b\"]]",
+                serializeAsString(input));
+    }
+
+    public void testStringListAsObjectWrapper() throws Exception
+    {
+        TypedListAsWrapper<Boolean> input = new TypedListAsWrapper<Boolean>();
+        input.add(true);
+        input.add(null);
+        input.add(false);
+        // Can wrap in JSON Object for wrapped style... also, will use
+        // non-qualified class name as type name, since there are no
+        // annotations
+        String expName = "TestTypedArraySerialization$TypedListAsWrapper";
+        assertEquals("{\""+expName+"\":[true,null,false]}",
+                serializeAsString(input));
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, primitive arrays
+    /**********************************************************
+     */
+
+    public void testIntArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(int[].class, WrapperMixIn.class);
+        int[] input = new int[] { 1, 2, 3 };
+        String clsName = int[].class.getName();
+        assertEquals("{\""+clsName+"\":[1,2,3]}", serializeAsString(m, input));
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests, generic arrays
+    /**********************************************************
+     */
+
+    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));
+
+        // then with static typing enabled:
+        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
new file mode 100644
index 0000000..fbf4685
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedContainerSerialization.java
@@ -0,0 +1,159 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import org.junit.Assert;
+
+
+import com.fasterxml.jackson.annotation.*;
+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
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "object-type")
+    @JsonSubTypes( { @Type(value = Dog.class, name = "doggy"),
+        @Type(value = Cat.class, name = "kitty") })
+    static abstract class Animal {
+	    public String name;
+
+	    protected Animal(String n) {
+	        name = n;
+	    }
+	}
+
+	@JsonTypeName("doggie")
+	static class Dog extends Animal {
+		public int boneCount;
+
+		public Dog() {
+			super(null);
+		}
+
+		@JsonCreator
+		public Dog(@JsonProperty("name") String name) {
+			super(name);
+		}
+
+		public void setBoneCount(int i) {
+			boneCount = i;
+		}
+	}
+
+	@JsonTypeName("kitty")
+	static class Cat extends Animal {
+		public String furColor;
+
+		public Cat() {
+			super(null);
+		}
+
+		@JsonCreator
+		public Cat(@JsonProperty("furColor") String c) {
+			super(null);
+			furColor = c;
+		}
+
+		public void setName(String n) {
+			name = n;
+		}
+	}
+
+	static class Container1 {
+		Animal animal;
+
+		public Animal getAnimal() {
+			return animal;
+		}
+
+		public void setAnimal(Animal animal) {
+			this.animal = animal;
+		}
+	}
+
+	static class Container2<T extends Animal> {
+		@JsonSerialize
+		T animal;
+
+		public T getAnimal() {
+			return animal;
+		}
+
+		public void setAnimal(T animal) {
+			this.animal = animal;
+		}
+
+	}
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
+    static class Issue508A { }
+    static class Issue508B extends Issue508A { }
+
+    private final static ObjectMapper mapper = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+	
+    public void testIssue265() throws Exception
+    {
+		Dog dog = new Dog("medor");
+		dog.setBoneCount(3);
+		Container1 c1 = new Container1();
+		c1.setAnimal(dog);
+		String s1 = mapper.writeValueAsString(c1);
+		Assert.assertTrue("polymorphic type info is kept (1)", s1
+				.indexOf("\"object-type\":\"doggy\"") >= 0);
+		Container2<Animal> c2 = new Container2<Animal>();
+		c2.setAnimal(dog);
+		String s2 = mapper.writeValueAsString(c2);
+		Assert.assertTrue("polymorphic type info is kept (2)", s2
+				.indexOf("\"object-type\":\"doggy\"") >= 0);
+    }
+
+    public void testIssue329() throws Exception
+    {
+            ArrayList<Animal> animals = new ArrayList<Animal>();
+            animals.add(new Dog("Spot"));
+            JavaType rootType = TypeFactory.defaultInstance().constructParametricType(Iterator.class, Animal.class);
+            String json = mapper.writerWithType(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
+    {
+            List<List<Issue508A>> l = new ArrayList<List<Issue508A>>();
+            List<Issue508A> l2 = new ArrayList<Issue508A>();
+            l2.add(new Issue508A());
+            l.add(l2);
+            TypeReference<?> typeRef = new TypeReference<List<List<Issue508A>>>() {};
+            String json = mapper.writerWithType(typeRef).writeValueAsString(l);
+
+            List<?> output = mapper.readValue(json, typeRef);
+            assertEquals(1, output.size());
+            Object ob = output.get(0);
+            assertTrue(ob instanceof List<?>);
+            List<?> list2 = (List<?>) ob;
+            assertEquals(1, list2.size());
+            ob = list2.get(0);
+            assertSame(Issue508A.class, ob.getClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java
new file mode 100644
index 0000000..4ba8381
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserialization.java
@@ -0,0 +1,244 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+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.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestTypedDeserialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Polymorphic base class
+     */
+    @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="@classy")
+    static abstract class Animal {
+        public String name;
+        
+        protected Animal(String n)  { name = n; }
+    }
+
+    @JsonTypeName("doggie")
+    static class Dog extends Animal
+    {
+        public int boneCount;
+        
+        @JsonCreator
+        public Dog(@JsonProperty("name") String name) {
+            super(name);
+        }
+
+        public void setBoneCount(int i) { boneCount = i; }
+    }
+    
+    @JsonTypeName("kitty")
+    static class Cat extends Animal
+    {
+        public String furColor;
+
+        @JsonCreator
+        public Cat(@JsonProperty("furColor") String c) {
+            super(null);
+            furColor = c;
+        }
+
+        public void setName(String n) { name = n; }
+    }
+
+    // for [JACKSON-319] -- allow "empty" beans
+    @JsonTypeName("fishy")
+    static class Fish extends Animal
+    {
+        @JsonCreator
+        public Fish()
+        {
+            super(null);
+        }
+    }
+
+    static class AnimalContainer {
+        public Animal animal;
+    }
+
+    // base class with no useful info
+    @JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY)
+    static abstract class DummyBase {
+        protected DummyBase(boolean foo) { }
+    }
+
+    static class DummyImpl extends DummyBase {
+        public int x;
+
+        public DummyImpl() { super(true); }
+    }
+    
+    @JsonTypeInfo(use=Id.MINIMAL_CLASS, include=As.WRAPPER_OBJECT)
+    interface TypeWithWrapper { }
+
+    @JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY)
+    interface TypeWithArray { }
+
+    static class Issue506DateBean {
+        @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type2")
+        public Date date;
+    }
+        
+    static class Issue506NumberBean
+    {
+        @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type3")
+        @JsonSubTypes({ @Type(Long.class),
+            @Type(Integer.class) })
+        public Number number;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    /**
+     * First things first, let's ensure we can serialize using
+     * class name, written as main-level property name
+     */
+    public void testSimpleClassAsProperty() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Animal a = m.readValue(asJSONObjectValueString("@classy", Cat.class.getName(),
+                "furColor", "tabby", "name", "Garfield"), Animal.class);
+        assertNotNull(a);
+        assertEquals(Cat.class, a.getClass());
+        Cat c = (Cat) a;
+        assertEquals("Garfield", c.name);
+        assertEquals("tabby", c.furColor);
+    }
+
+    // Test inclusion using wrapper style
+    public void testTypeAsWrapper() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(Animal.class, TypeWithWrapper.class);
+        String JSON = "{\".TestTypedDeserialization$Dog\" : "
+            +asJSONObjectValueString(m, "name", "Scooby", "boneCount", "6")+" }";
+        Animal a = m.readValue(JSON, Animal.class);
+        assertTrue(a instanceof Animal);
+        assertEquals(Dog.class, a.getClass());
+        Dog d = (Dog) a;
+        assertEquals("Scooby", d.name);
+        assertEquals(6, d.boneCount);
+    }
+
+    // Test inclusion using 2-element array
+    public void testTypeAsArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(Animal.class, TypeWithArray.class);
+        // hmmh. Not good idea to rely on exact output, order may change. But...
+        String JSON = "[\""+Dog.class.getName()+"\", "
+            +asJSONObjectValueString(m, "name", "Martti", "boneCount", "11")+" ]";
+        Animal a = m.readValue(JSON, Animal.class);
+        assertEquals(Dog.class, a.getClass());
+        Dog d = (Dog) a;
+        assertEquals("Martti", d.name);
+        assertEquals(11, d.boneCount);
+    }
+
+    // Use basic Animal as contents of a regular List
+    public void testListAsArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // This time using PROPERTY style (default) again
+        String JSON = "[\n"
+            +asJSONObjectValueString(m, "@classy", Cat.class.getName(), "name", "Hello", "furColor", "white")
+            +",\n"
+            // let's shuffle doggy's fields a bit for testing
+            +asJSONObjectValueString(m,
+                                     "boneCount", Integer.valueOf(1),
+                                     "@classy", Dog.class.getName(),
+                                     "name", "Bob"
+                                     )
+            +",\n"
+            +asJSONObjectValueString(m, "@classy", Fish.class.getName())
+            +", null\n]";
+        
+        JavaType expType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, Animal.class);
+        List<Animal> animals = m.readValue(JSON, expType);
+        assertNotNull(animals);
+        assertEquals(4, animals.size());
+        Cat c = (Cat) animals.get(0);
+        assertEquals("Hello", c.name);
+        assertEquals("white", c.furColor);
+        Dog d = (Dog) animals.get(1);
+        assertEquals("Bob", d.name);
+        assertEquals(1, d.boneCount);
+        Fish f = (Fish) animals.get(2);
+        assertNotNull(f);
+        assertNull(animals.get(3));
+    }
+
+    public void testCagedAnimal() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String jsonCat = asJSONObjectValueString(m, "@classy", Cat.class.getName(), "name", "Nilson", "furColor", "black");
+        String JSON = "{\"animal\":"+jsonCat+"}";
+
+        AnimalContainer cont = m.readValue(JSON, AnimalContainer.class);
+        assertNotNull(cont);
+        Animal a = cont.animal;
+        assertNotNull(a);
+        Cat c = (Cat) a;
+        assertEquals("Nilson", c.name);
+        assertEquals("black", c.furColor);
+    }
+
+    /**
+     * Test that verifies that there are few limitations on polymorphic
+     * base class.
+     */
+    public void testAbstractEmptyBaseClass() throws Exception
+    {
+        DummyBase result = new ObjectMapper().readValue(
+                "[\""+DummyImpl.class.getName()+"\",{\"x\":3}]", DummyBase.class);
+        assertNotNull(result);
+        assertEquals(DummyImpl.class, result.getClass());
+        assertEquals(3, ((DummyImpl) result).x);
+    }
+
+    // [JACKSON-506], wrt Date
+    public void testIssue506WithDate() throws Exception
+    {
+        Issue506DateBean input = new Issue506DateBean();
+        input.date = new Date(1234L);
+
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(input);
+
+        Issue506DateBean output = mapper.readValue(json, Issue506DateBean.class);
+        assertEquals(input.date, output.date);
+    }
+    
+    // [JACKSON-506], wrt Number
+    public void testIssue506WithNumber() throws Exception
+    {
+        Issue506NumberBean input = new Issue506NumberBean();
+        input.number = Long.valueOf(4567L);
+
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(input);
+
+        Issue506NumberBean output = mapper.readValue(json, Issue506NumberBean.class);
+        assertEquals(input.number, output.number);
+    }
+}
+
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserializationWithDefault.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserializationWithDefault.java
new file mode 100644
index 0000000..f651c42
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedDeserializationWithDefault.java
@@ -0,0 +1,107 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+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.annotation.NoClass;
+
+/**
+ * Unit tests related to [JACKSON-712]; specialized handling of
+ * otherwise invalid type id embedding cases.
+ */
+public class TestTypedDeserializationWithDefault extends BaseMapTest
+{
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = LegacyInter.class)
+    @JsonSubTypes(value = {@JsonSubTypes.Type(name = "mine", value = MyInter.class)})
+    public static interface Inter { }
+
+    public static class MyInter implements Inter {
+        @JsonProperty("blah") public List<String> blah;
+    }
+
+    public static class LegacyInter extends MyInter
+    {
+        @JsonCreator
+        LegacyInter(Object obj)
+        {
+            if (obj instanceof List) {
+                blah = new ArrayList<String>();
+                for (Object o : (List<?>) obj) {
+                    blah.add(o.toString());
+                }
+            }
+            else if (obj instanceof String) {
+                blah = Arrays.asList(((String) obj).split(","));
+            }
+            else {
+                throw new IllegalArgumentException("Unknown type: " + obj.getClass());
+            }
+        }
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type",
+            defaultImpl = NoClass.class)
+    public static class DefaultWithNoClass { }
+
+    // and then one with no defaultImpl nor listed subtypes
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
+    abstract static class MysteryPolymorphic { }
+    
+    /*
+    /**********************************************************
+    /* Unit tests, deserialization
+    /**********************************************************
+     */
+    
+    public void testDeserializationWithObject() throws Exception
+    {
+        Inter inter = objectReader(Inter.class).readValue("{\"type\": \"mine\", \"blah\": [\"a\", \"b\", \"c\"]}");
+        assertTrue(inter instanceof MyInter);
+        assertFalse(inter instanceof LegacyInter);
+        assertEquals(Arrays.asList("a", "b", "c"), ((MyInter) inter).blah);
+    }
+
+    public void testDeserializationWithString() throws Exception
+    {
+        Inter inter = objectReader(Inter.class).readValue("\"a,b,c,d\"");
+        assertTrue(inter instanceof LegacyInter);
+        assertEquals(Arrays.asList("a", "b", "c", "d"), ((MyInter) inter).blah);
+    }
+
+    public void testDeserializationWithArray() throws Exception
+    {
+        Inter inter = objectReader(Inter.class).readValue("[\"a\", \"b\", \"c\", \"d\"]");
+        assertTrue(inter instanceof LegacyInter);
+        assertEquals(Arrays.asList("a", "b", "c", "d"), ((MyInter) inter).blah);
+    }
+
+    public void testDeserializationWithArrayOfSize2() throws Exception
+    {
+        Inter inter = objectReader(Inter.class).readValue("[\"a\", \"b\"]");
+        assertTrue(inter instanceof LegacyInter);
+        assertEquals(Arrays.asList("a", "b"), ((MyInter) inter).blah);
+    }
+
+    // [Issue#148]
+    public void testDefaultAsNoClass() throws Exception
+    {
+        Object ob = objectReader(DefaultWithNoClass.class).readValue("{ }");
+        assertNull(ob);
+        ob = objectReader(DefaultWithNoClass.class).readValue("{ \"bogus\":3 }");
+        assertNull(ob);
+    }
+
+    // [Issue#148]
+    public void testBadTypeAsNull() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.disable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
+        Object ob = mapper.readValue("{}", MysteryPolymorphic.class);
+        assertNull(ob);
+        ob = mapper.readValue("{ \"whatever\":13}", MysteryPolymorphic.class);
+        assertNull(ob);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java
new file mode 100644
index 0000000..cd857f9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestTypedSerialization.java
@@ -0,0 +1,204 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.*;
+
+import static com.fasterxml.jackson.annotation.JsonTypeInfo.*;
+
+public class TestTypedSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    /**
+     * Polymorphic base class
+     */
+    @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY)
+    static abstract class Animal {
+        public String name;
+        
+        protected Animal(String n)  { name = n; }
+    }
+
+    @JsonTypeName("doggie")
+    static class Dog extends Animal
+    {
+        public int boneCount;
+        
+        private Dog() { super(null); }
+        public Dog(String name, int b) {
+            super(name);
+            boneCount = b;
+        }
+    }
+    
+    @JsonTypeName("kitty")
+    static class Cat extends Animal
+    {
+        public String furColor;
+        
+        private Cat() { super(null); }
+        public Cat(String name, String c) {
+            super(name);
+            furColor = c;
+        }
+    }
+
+    public class AnimalWrapper {
+        public Animal animal;
+        
+        public AnimalWrapper(Animal a) { animal = a; }
+    }
+
+    @JsonTypeInfo(use=Id.MINIMAL_CLASS, include=As.WRAPPER_OBJECT)
+    interface TypeWithWrapper { }
+
+    @JsonTypeInfo(use=Id.CLASS, include=As.WRAPPER_ARRAY)
+    interface TypeWithArray { }
+
+    @JsonTypeInfo(use=Id.NAME)
+    @JsonTypeName("empty")
+    public class Empty { }
+
+    @JsonTypeInfo(include=As.PROPERTY, use=Id.CLASS)
+    public class Super {}
+    public class A extends Super {}
+    public class B extends Super {}
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * First things first, let's ensure we can serialize using
+     * class name, written as main-level property name
+     */
+    public void testSimpleClassAsProperty() throws Exception
+    {
+        Map<String,Object> result = writeAndMap(MAPPER, new Cat("Beelzebub", "tabby"));
+        assertEquals(3, result.size());
+        assertEquals("Beelzebub", result.get("name"));
+        assertEquals("tabby", result.get("furColor"));
+        // should we try customized class name?
+        String classProp = Id.CLASS.getDefaultPropertyName();
+        assertEquals(Cat.class.getName(), result.get(classProp));
+    }
+
+    /**
+     * Test inclusion using wrapper style
+     */
+    public void testTypeAsWrapper() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(Animal.class, TypeWithWrapper.class);
+        Map<String,Object> result = writeAndMap(m, new Cat("Venla", "black"));
+        // should get a wrapper; keyed by minimal class name ("Cat" here)
+        assertEquals(1, result.size());
+        // minimal class name is prefixed by dot, and for inner classes it's bit longer
+        Map<?,?> cat = (Map<?,?>) result.get(".TestTypedSerialization$Cat");
+        assertNotNull(cat);
+        assertEquals(2, cat.size());
+        assertEquals("Venla", cat.get("name"));
+        assertEquals("black", cat.get("furColor"));
+    }
+
+    /**
+     * Test inclusion using 2-element array
+     */
+    public void testTypeAsArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(Animal.class, TypeWithArray.class);
+        // hmmh. Not good idea to rely on exact output, order may change. But...
+        Map<String,Object> result = writeAndMap(m, new AnimalWrapper(new Dog("Amadeus", 7)));
+        // First level, wrapper
+        assertEquals(1, result.size());
+        List<?> l = (List<?>) result.get("animal");
+        assertNotNull(l);
+        assertEquals(2, l.size());
+        assertEquals(Dog.class.getName(), l.get(0));
+        Map<?,?> doggie = (Map<?,?>) l.get(1);
+        assertNotNull(doggie);
+        assertEquals(2, doggie.size());
+        assertEquals("Amadeus", doggie.get("name"));
+        assertEquals(Integer.valueOf(7), doggie.get("boneCount"));
+    }
+
+    /* !!! 30-Jan-2010, tatus: I am not completely sure below works as it should
+     *    Problem is, context of "untyped" map should prevent type information
+     *    being added to Animal entries, because Object.class has no type.
+     *    If type information is included, it will not be useful for deserialization,
+     *    since static type does not carry through (unlike in serialization).
+     *    
+     *    But it is not quite clear how type information should be pushed through
+     *    array types...
+     */
+    @SuppressWarnings("unchecked")
+    public void testInArray() throws Exception
+    {
+        // ensure we'll use mapper with default configs
+        ObjectMapper m = new ObjectMapper();
+        // ... so this should NOT be needed...
+        m.disableDefaultTyping();
+        
+        Animal[] animals = new Animal[] { new Cat("Miuku", "white"), new Dog("Murre", 9) };
+        Map<String,Object> map = new HashMap<String,Object>();
+        map.put("a", animals);
+        String json = m.writeValueAsString(map);
+        Map<String,Object> result = m.readValue(json, Map.class);
+        assertEquals(1, result.size());
+        Object ob = result.get("a");
+        if (!(ob instanceof List<?>)) {
+            // 03-Feb-2010, tatu: Weird; seems to fail sometimes...
+            fail("Did not map to entry with 'a' as List (but as "+ob.getClass().getName()+"): JSON == '"+json+"'");
+        }
+        List<?> l = (List<?>)ob;
+        assertNotNull(l);
+        assertEquals(2, l.size());
+        Map<?,?> a1 = (Map<?,?>) l.get(0);
+        assertEquals(3, a1.size());
+        String classProp = Id.CLASS.getDefaultPropertyName();
+        assertEquals(Cat.class.getName(), a1.get(classProp));
+        Map<?,?> a2 = (Map<?,?>) l.get(1);
+        assertEquals(3, a2.size());
+        assertEquals(Dog.class.getName(), a2.get(classProp));
+    }
+
+    /**
+     * Simple unit test to verify that serializing "empty" beans is ok
+     */
+    public void testEmptyBean() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        assertEquals("{\"@type\":\"empty\"}", m.writeValueAsString(new Empty()));
+    }
+
+    /**
+     * Unit test for [JACKSON-543]
+     */
+    public void testTypedMaps() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+
+        Map<Long, Collection<Super>> map = new HashMap<Long, Collection<Super>>();
+        List<Super> list = new ArrayList<Super>();
+        list.add(new A());
+        map.put(1L, list);
+        String json = mapper.writerWithType(new TypeReference<Map<Long, Collection<Super>>>() {}).writeValueAsString(map);
+        assertTrue("JSON does not contain '@class': "+json, json.contains("@class"));
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
new file mode 100644
index 0000000..0cdbed9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestVisibleTypeId.java
@@ -0,0 +1,210 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import com.fasterxml.jackson.annotation.JsonTypeId;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Tests to verify [JACKSON-437], [JACKSON-762]
+ */
+public class TestVisibleTypeId extends BaseMapTest
+{
+    // type id as property, exposed
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,
+            property="type", visible=true)
+    @JsonTypeName("BaseType")
+    static class PropertyBean {
+        public int a = 3;
+
+        protected String type;
+
+        public void setType(String t) { type = t; }
+    }
+
+    // as wrapper-array
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_ARRAY,
+            property="type", visible=true)
+    @JsonTypeName("ArrayType")
+    static class WrapperArrayBean {
+        public int a = 1;
+
+        protected String type;
+
+        public void setType(String t) { type = t; }
+    }
+
+    // as wrapper-object
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT,
+            property="type", visible=true)
+    @JsonTypeName("ObjectType")
+    static class WrapperObjectBean {
+        public int a = 2;
+
+        protected String type;
+
+        public void setType(String t) { type = t; }
+    }
+
+    // as external id, bit trickier
+    static class ExternalIdWrapper {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY,
+                property="type", visible=true)
+        public ExternalIdBean bean = new ExternalIdBean();
+    }
+    
+    @JsonTypeName("ExternalType")
+    static class ExternalIdBean {
+        public int a = 2;
+
+        protected String type;
+
+        public void setType(String t) { type = t; }
+    }
+
+    // // // [JACKSON-762]: type id from property
+    
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY,
+            property="type")
+    static class TypeIdFromFieldProperty {
+        public int a = 3;
+
+        @JsonTypeId
+        public String type = "SomeType";
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_ARRAY,
+            property="type")
+    static class TypeIdFromFieldArray {
+        public int a = 3;
+        @JsonTypeId
+        public String type = "SomeType";
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT,
+            property="type")
+    static class TypeIdFromMethodObject {
+        public int a = 3;
+        
+        @JsonTypeId
+        public String getType() { return "SomeType"; }
+    }
+
+    static class ExternalIdWrapper2 {
+        @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY,
+                property="type", visible=true)
+        public ExternalIdBean2 bean = new ExternalIdBean2();
+    }
+
+    static class ExternalIdBean2 {
+        public int a = 2;
+
+        /* Type id property itself can not be external, as it is conceptually
+         * part of the bean for which info is written:
+         */
+        @JsonTypeId
+        public String getType() { return "SomeType"; }
+    }
+
+    // Invalid definition: multiple type ids
+    static class MultipleIds {
+        @JsonTypeId
+        public String type1 = "type1";
+
+        @JsonTypeId
+        public String getType2() { return "type2"; };
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testVisibleWithProperty() throws Exception
+    {
+        String json = mapper.writeValueAsString(new PropertyBean());
+        // just default behavior:
+        assertEquals("{\"type\":\"BaseType\",\"a\":3}", json);
+        // but then expect to read it back
+        PropertyBean result = mapper.readValue(json, PropertyBean.class);
+        assertEquals("BaseType", result.type);
+
+        // also, should work with order reversed
+        result = mapper.readValue("{\"a\":7, \"type\":\"BaseType\"}", PropertyBean.class);
+        assertEquals(7, result.a);
+        assertEquals("BaseType", result.type);
+    }
+
+    public void testVisibleWithWrapperArray() throws Exception
+    {
+        String json = mapper.writeValueAsString(new WrapperArrayBean());
+        // just default behavior:
+        assertEquals("[\"ArrayType\",{\"a\":1}]", json);
+        // but then expect to read it back
+        WrapperArrayBean result = mapper.readValue(json, WrapperArrayBean.class);
+        assertEquals("ArrayType", result.type);
+        assertEquals(1, result.a);
+    }
+
+    public void testVisibleWithWrapperObject() throws Exception
+    {
+        String json = mapper.writeValueAsString(new WrapperObjectBean());
+        assertEquals("{\"ObjectType\":{\"a\":2}}", json);
+        // but then expect to read it back
+        WrapperObjectBean result = mapper.readValue(json, WrapperObjectBean.class);
+        assertEquals("ObjectType", result.type);
+    }
+
+    public void testVisibleWithExternalId() throws Exception
+    {
+        String json = mapper.writeValueAsString(new ExternalIdWrapper());
+        // but then expect to read it back
+        ExternalIdWrapper result = mapper.readValue(json, ExternalIdWrapper.class);
+        assertEquals("ExternalType", result.bean.type);
+        assertEquals(2, result.bean.a);
+    }
+
+    // [JACKSON-762]
+
+    public void testTypeIdFromProperty() throws Exception
+    {
+        assertEquals("{\"type\":\"SomeType\",\"a\":3}",
+                mapper.writeValueAsString(new TypeIdFromFieldProperty()));
+    }
+
+    public void testTypeIdFromArray() throws Exception
+    {
+        assertEquals("[\"SomeType\",{\"a\":3}]",
+                mapper.writeValueAsString(new TypeIdFromFieldArray()));
+    }
+
+    public void testTypeIdFromObject() throws Exception
+    {
+        assertEquals("{\"SomeType\":{\"a\":3}}",
+                mapper.writeValueAsString(new TypeIdFromMethodObject()));
+    }
+
+    public void testTypeIdFromExternal() throws Exception
+    {
+        String json = mapper.writeValueAsString(new ExternalIdWrapper2());
+        // Implementation detail: type id written AFTER value, due to constraints
+        assertEquals("{\"bean\":{\"a\":2},\"type\":\"SomeType\"}", json);
+        
+    }
+
+    // Failing cases
+
+    public void testInvalidMultipleTypeIds() throws Exception
+    {
+        try {
+            mapper.writeValueAsString(new MultipleIds());
+            fail("Should have failed");
+        } catch (JsonMappingException e) {
+            verifyException(e, "multiple type ids");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
new file mode 100644
index 0000000..08214a0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/jsontype/TestWithGenerics.java
@@ -0,0 +1,204 @@
+package com.fasterxml.jackson.databind.jsontype;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.annotation.*;
+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.databind.*;
+import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
+import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+public class TestWithGenerics extends BaseMapTest
+{
+    @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "object-type")
+    @JsonSubTypes( { @Type(value = Dog.class, name = "doggy") })
+    static abstract class Animal {
+        public String name;
+    }    
+
+    static class Dog extends Animal {
+        public int boneCount;
+
+        public Dog(String name, int b) {
+            super();
+            this.name = name;
+            boneCount = b;
+        }
+    }
+
+    static class ContainerWithGetter<T extends Animal> {
+        private T animal;
+
+        public ContainerWithGetter(T a) { animal = a; }
+
+        public T getAnimal() { return animal; }
+    }
+
+    static class ContainerWithField<T extends Animal> {
+        public T animal;
+
+        public ContainerWithField(T a) { animal = a; }
+    }
+    
+    // Beans for [JACKSON-387], [JACKSON-430]
+    
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@classAttr1")
+    static class MyClass {
+        public List<MyParam<?>> params = new ArrayList<MyParam<?>>();
+    }
+
+    @JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@classAttr2")
+    static class MyParam<T>{
+        public T value;
+
+        public MyParam() { }
+        public MyParam(T v) { value = v; }
+    }
+
+    private static class SomeObject {
+        @SuppressWarnings("unused")
+        public String someValue = UUID.randomUUID().toString();
+    }
+    
+    // Beans for [JACKSON-430]
+    
+    static class CustomJsonSerializer extends JsonSerializer<Object>
+        implements ResolvableSerializer
+    {
+        private final JsonSerializer<Object> beanSerializer;
+    
+        public CustomJsonSerializer( JsonSerializer<Object> beanSerializer ) { this.beanSerializer = beanSerializer; }
+    
+        @Override
+        public void serialize( Object value, JsonGenerator jgen, SerializerProvider provider )
+            throws IOException, JsonProcessingException
+        {
+            beanSerializer.serialize( value, jgen, provider );
+        }
+    
+        @Override
+        public Class<Object> handledType() { return beanSerializer.handledType(); }
+    
+        @Override
+        public void serializeWithType( Object value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer )
+            throws IOException, JsonProcessingException
+        {
+            beanSerializer.serializeWithType( value, jgen, provider, typeSer );
+        }
+
+        @Override
+        public void resolve(SerializerProvider provider) throws JsonMappingException
+        {
+            if (beanSerializer instanceof ResolvableSerializer) {
+                ((ResolvableSerializer) beanSerializer).resolve(provider);
+            }
+        }
+    }
+    
+    @SuppressWarnings("serial")
+    protected static class CustomJsonSerializerFactory extends BeanSerializerFactory
+    {
+        public CustomJsonSerializerFactory() { super(null); }
+
+        @Override
+        protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
+                BeanDescription beanDesc)
+            throws JsonMappingException
+        {                
+            return new CustomJsonSerializer(super.constructBeanSerializer(prov, beanDesc) );
+        }
+    }
+
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testWrapperWithGetter() throws Exception
+    {
+        Dog dog = new Dog("Fluffy", 3);
+        String json = new ObjectMapper().writeValueAsString(new ContainerWithGetter<Animal>(dog));
+        if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
+            fail("polymorphic type not kept, result == "+json+"; should contain 'object-type':'...'");
+        }
+    }
+
+    public void testWrapperWithField() throws Exception
+    {
+        Dog dog = new Dog("Fluffy", 3);
+        String json = new ObjectMapper().writeValueAsString(new ContainerWithField<Animal>(dog));
+        if (json.indexOf("\"object-type\":\"doggy\"") < 0) {
+            fail("polymorphic type not kept, result == "+json+"; should contain 'object-type':'...'");
+        }
+    }
+    
+    public void testWrapperWithExplicitType() throws Exception
+    {
+        Dog dog = new Dog("Fluffy", 3);
+        ContainerWithGetter<Animal> c2 = new ContainerWithGetter<Animal>(dog);
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writerWithType(TypeFactory.defaultInstance().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':'...'");
+        }
+    }
+    
+    public void testJackson387() throws Exception
+    {
+        ObjectMapper om = new ObjectMapper();
+        om.enableDefaultTyping( ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT, JsonTypeInfo.As.PROPERTY );
+        om.setSerializationInclusion(JsonInclude.Include.NON_NULL );
+        om.enable( SerializationFeature.INDENT_OUTPUT);
+
+        MyClass mc = new MyClass();
+
+        MyParam<Integer> moc1 = new MyParam<Integer>(1);
+        MyParam<String> moc2 = new MyParam<String>("valueX");
+
+        SomeObject so = new SomeObject();
+        so.someValue = "xxxxxx"; 
+        MyParam<SomeObject> moc3 = new MyParam<SomeObject>(so);
+
+        List<SomeObject> colist = new ArrayList<SomeObject>();
+        colist.add( new SomeObject() );
+        colist.add( new SomeObject() );
+        colist.add( new SomeObject() );
+        MyParam<List<SomeObject>> moc4 = new MyParam<List<SomeObject>>(colist);
+
+        mc.params.add( moc1 );
+        mc.params.add( moc2 );
+        mc.params.add( moc3 );
+        mc.params.add( moc4 );
+
+        String json = om.writeValueAsString( mc );
+        
+        MyClass mc2 = om.readValue(json, MyClass.class );
+        assertNotNull(mc2);
+        assertNotNull(mc2.params);
+        assertEquals(4, mc2.params.size());
+    }
+
+    public void testJackson430() throws Exception
+    {
+        ObjectMapper om = new ObjectMapper();
+//        om.getSerializationConfig().setSerializationInclusion( Inclusion.NON_NULL );
+        om.setSerializerFactory( new CustomJsonSerializerFactory() );
+        MyClass mc = new MyClass();
+        mc.params.add(new MyParam<Integer>(1));
+
+        String str = om.writeValueAsString( mc );
+//        System.out.println( str );
+        
+        MyClass mc2 = om.readValue( str, MyClass.class );
+        assertNotNull(mc2);
+        assertNotNull(mc2.params);
+        assertEquals(1, mc2.params.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
new file mode 100644
index 0000000..6bfedad
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForClass.java
@@ -0,0 +1,98 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinDeserForClass
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class BaseClass
+    {
+        /* property that is always found; but has lower priority than
+         * setter method if both found
+         */
+        @JsonProperty
+        public String a;
+
+        // setter that may or may not be auto-detected
+        public void setA(String v) { a = "XXX"+v; }
+    }
+
+    @JsonAutoDetect(setterVisibility=Visibility.ANY, fieldVisibility=Visibility.ANY)
+    static class LeafClass
+        extends BaseClass { }
+
+    @JsonAutoDetect(setterVisibility=Visibility.NONE, fieldVisibility=Visibility.NONE)
+    interface MixIn { }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testClassMixInsTopLevel() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        // First: test default behavior: should use setter
+        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
+         */
+        m = new ObjectMapper();
+        m.addMixInAnnotations(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)
+     */
+    public void testClassMixInsMidLevel() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(BaseClass.class, MixIn.class);
+        {
+            BaseClass result = m.readValue("{\"a\":\"value\"}", BaseClass.class);
+            assertEquals("value", result.a);
+        }
+
+        // whereas with leaf class, reverts to default
+        {
+            LeafClass result = m.readValue("{\"a\":\"value\"}", LeafClass.class);
+            assertEquals("XXXvalue", result.a);
+        }
+    }
+
+    /* Also: when mix-in attached to Object.class, will work, if
+     * visible (similar to mid-level, basically)
+     */
+    public void testClassMixInsForObjectClass() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(Object.class, MixIn.class);
+        // will be seen for BaseClass
+        {
+            BaseClass result = m.readValue("{\"a\":\"\"}", BaseClass.class);
+            assertEquals("", result.a);
+        }
+
+        // but LeafClass still overrides
+        {
+            LeafClass result = m.readValue("{\"a\":\"\"}", LeafClass.class);
+            assertEquals("XXX", result.a);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
new file mode 100644
index 0000000..df1eef5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForCreators.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinDeserForCreators
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class BaseClass
+    {
+        protected String _a;
+
+        public BaseClass(String a) {
+            _a = a+"...";
+        }
+
+        private BaseClass(String value, boolean dummy) {
+            _a = value;
+        }
+
+        public static BaseClass myFactory(String a) {
+            return new BaseClass(a+"X", true);
+        }
+    }
+
+    static class BaseClassWithPrivateCtor
+    {
+        protected String _a;
+        private BaseClassWithPrivateCtor(String a) {
+            _a = a+"...";
+        }
+
+    }
+
+    /**
+     * Mix-in class that will effectively suppresses String constructor,
+     * and marks a non-auto-detectable static method as factory method
+     * as a creator.
+     *<p>
+     * Note that method implementations are not used for anything; but
+     * we have to a class: interface won't do, as they can't have
+     * constructors or static methods.
+     */
+    static class MixIn
+    {
+        @JsonIgnore protected MixIn(String s) { }
+
+        @JsonCreator
+        static BaseClass myFactory(String a) { return null; }
+    }
+
+    static class MixInForPrivate
+    {
+        @JsonCreator MixInForPrivate(String s) { }
+    }
+
+    static class StringWrapper {
+        String _value;
+        private StringWrapper(String s, boolean foo) { _value = s; }
+
+        @SuppressWarnings("unused")
+		private static StringWrapper create(String str) {
+            return new StringWrapper(str, false);
+        }
+    }
+
+    abstract static class StringWrapperMixIn {
+        @JsonCreator static StringWrapper create(String str) { return null; }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testForConstructor() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(BaseClassWithPrivateCtor.class, MixInForPrivate.class);
+        BaseClassWithPrivateCtor result = m.readValue("\"?\"", BaseClassWithPrivateCtor.class);
+        assertEquals("?...", result._a);
+    }
+
+    public void testForFactoryAndCtor() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        BaseClass result;
+
+        // First: test default behavior: should use constructor
+        result = m.readValue("\"string\"", BaseClass.class);
+        assertEquals("string...", result._a);
+
+        // Then with simple mix-in: should change to use the factory method
+        m = new ObjectMapper();
+        m.addMixInAnnotations(BaseClass.class, MixIn.class);
+        result = m.readValue("\"string\"", BaseClass.class);
+        assertEquals("stringX", result._a);
+    }
+
+    public void testFactoryMixIn() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(StringWrapper.class, StringWrapperMixIn.class);
+        StringWrapper result = m.readValue("\"a\"", StringWrapper.class);
+        assertEquals("a", result._value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java
new file mode 100644
index 0000000..2fcd1d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinDeserForMethods.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinDeserForMethods
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class BaseClass
+    {
+        protected HashMap<String,Object> values = new HashMap<String,Object>();
+
+        public BaseClass() { }
+
+        protected void addValue(String key, Object value) {
+            values.put(key, value);
+        }
+    }
+
+    interface MixIn
+    {
+        @JsonAnySetter void addValue(String key, Object value);
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    /**
+     * Unit test that verifies that we can mix in @JsonAnySetter
+     * annotation, as expected.
+     */
+    public void testWithAnySetter() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(BaseClass.class, MixIn.class);
+        BaseClass result = m.readValue("{ \"a\" : 3, \"b\" : true }", BaseClass.class);
+        assertNotNull(result);
+        assertEquals(2, result.values.size());
+        assertEquals(Integer.valueOf(3), result.values.get("a"));
+        assertEquals(Boolean.TRUE, result.values.get("b"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java
new file mode 100644
index 0000000..6aa8871
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinInheritance.java
@@ -0,0 +1,73 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.IOException;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestMixinInheritance
+    extends BaseMapTest
+{
+    // [Issue-14]
+    static class Beano {
+        public int ido = 42;
+        public String nameo = "Bob";
+    }
+
+    static class BeanoMixinSuper {
+        @JsonProperty("name")
+        public String nameo;
+    }
+
+    static class BeanoMixinSub extends BeanoMixinSuper {
+        @JsonProperty("id")
+        public int ido;
+    }
+
+    static class Beano2 {
+        public int getIdo() { return 13; }
+        public String getNameo() { return "Bill"; }
+    }
+
+    static abstract class BeanoMixinSuper2 extends Beano2 {
+        @Override
+        @JsonProperty("name")
+        public abstract String getNameo();
+    }
+
+    static abstract class BeanoMixinSub2 extends BeanoMixinSuper2 {
+        @Override
+        @JsonProperty("id")
+        public abstract int getIdo();
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testMixinFieldInheritance() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(Beano.class, BeanoMixinSub.class);
+        Map<String,Object> result;
+        result = writeAndMap(mapper, new Beano());
+        assertEquals(2, result.size());
+        assertTrue(result.containsKey("id"));
+        assertTrue(result.containsKey("name"));
+    }
+
+    public void testMixinMethodInheritance() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(Beano2.class, BeanoMixinSub2.class);
+        Map<String,Object> result;
+        result = writeAndMap(mapper, new Beano2());
+        assertEquals(2, result.size());
+        assertTrue(result.containsKey("id"));
+        assertTrue(result.containsKey("name"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java
new file mode 100644
index 0000000..d714580
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForClass.java
@@ -0,0 +1,118 @@
+package com.fasterxml.jackson.databind.mixins;
+
+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.annotation.JsonSerialize;
+
+public class TestMixinSerForClass
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    @JsonSerialize(include=JsonSerialize.Inclusion.ALWAYS)
+    static class BaseClass
+    {
+        protected String _a, _b;
+        protected String _c = "c";
+
+        protected BaseClass() { }
+
+        public BaseClass(String a) {
+            _a = a;
+        }
+
+        // will be auto-detectable unless disabled:
+        public String getA() { return _a; }
+
+        @JsonProperty
+        public String getB() { return _b; }
+
+        @JsonProperty
+        public String getC() { return _c; }
+    }
+
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
+    static class LeafClass
+        extends BaseClass
+    {
+        public LeafClass() { super(null); }
+
+        public LeafClass(String a) {
+            super(a);
+        }
+    }
+
+    /**
+     * This interface only exists to add "mix-in annotations": that is, any
+     * annotations it has can be virtually added to mask annotations
+     * of other classes
+     */
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+    interface MixIn { }
+
+    // test disabling of autodetect...
+    @JsonAutoDetect(getterVisibility=Visibility.NONE, fieldVisibility=Visibility.NONE)
+    interface MixInAutoDetect { }
+
+    /*
+    /**********************************************************
+    /( Unit tests
+    /**********************************************************
+     */
+
+    public void testClassMixInsTopLevel() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result;
+
+        // first: with no mix-ins:
+        result = writeAndMap(mapper, new LeafClass("abc"));
+        assertEquals(1, result.size());
+        assertEquals("abc", result.get("a"));
+
+        // then with top-level override
+        mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(LeafClass.class, MixIn.class);
+        result = writeAndMap(mapper, new LeafClass("abc"));
+        assertEquals(2, result.size());
+        assertEquals("abc", result.get("a"));
+        assertEquals("c", result.get("c"));
+
+        // mid-level override; should not have any effect
+        mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+        result = writeAndMap(mapper, new LeafClass("abc"));
+        assertEquals(1, result.size());
+        assertEquals("abc", result.get("a"));
+    }
+
+    public void testClassMixInsMidLevel() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result;
+        LeafClass bean = new LeafClass("xyz");
+        bean._c = "c2";
+
+        // with no mix-ins first...
+        result = writeAndMap(mapper, bean);
+        assertEquals(2, result.size());
+        assertEquals("xyz", result.get("a"));
+        assertEquals("c2", result.get("c"));
+
+        // then with working mid-level override, which effectively suppresses 'a'
+        mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(BaseClass.class, MixInAutoDetect.class);
+        result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals("c2", result.get("c"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java
new file mode 100644
index 0000000..08eed33
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForFields.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinSerForFields
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class BaseClass
+    {
+        public String a;
+        protected String b;
+
+        public BaseClass(String a, String b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+
+    static class SubClass
+        extends BaseClass
+    {
+        public SubClass(String a, String b) {
+            super(a, b);
+        }
+    }
+
+    abstract class MixIn {
+        // Let's add 'b' as "banana"
+        @JsonProperty("banana")
+        public String b;
+    }
+
+    abstract class MixIn2 {
+        // Let's remove 'a'
+        @JsonIgnore
+        public String a;
+
+        // also: add a dummy field that is NOT to match anything
+        @JsonProperty public String xyz;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testFieldMixInsTopLevel() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result;
+        BaseClass bean = new BaseClass("1", "2");
+
+        // first: with no mix-ins:
+        result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals("1", result.get("a"));
+
+        // and then with simple mix-in
+        mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+        result = writeAndMap(mapper, bean);
+        assertEquals(2, result.size());
+        assertEquals("1", result.get("a"));
+        assertEquals("2", result.get("banana"));
+    }
+
+    public void testMultipleFieldMixIns() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // ordering here shouldn't matter really...
+        HashMap<Class<?>,Class<?>> mixins = new HashMap<Class<?>,Class<?>>();
+        mixins.put(SubClass.class, MixIn.class);
+        mixins.put(BaseClass.class, MixIn2.class);
+        mapper.setMixInAnnotations(mixins);
+
+        Map<String,Object> result;
+        result = writeAndMap(mapper, new SubClass("1", "2"));
+        assertEquals(1, result.size());
+        // 'a' should be suppressed; 'b' mapped to 'banana'
+        assertEquals("2", result.get("banana"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
new file mode 100644
index 0000000..a20867f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerForMethods.java
@@ -0,0 +1,191 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinSerForMethods
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    // base class: just one visible property ('b')
+    static class BaseClass
+    {
+        @SuppressWarnings("unused") private String a;
+        private String b;
+
+        protected BaseClass() { }
+
+        public BaseClass(String a, String b) {
+            this.a = a;
+            this.b = b;
+        }
+
+        @JsonProperty("b")
+        public String takeB() { return b; }
+    }
+
+    /* extends, just for fun; and to show possible benefit of being
+     * able to declare that a method is overridden (compile-time check
+     * that our intended mix-in override will match a method)
+     */
+    abstract static class MixIn
+        extends BaseClass
+    {
+        // let's make 'a' visible
+        @JsonProperty String a;
+
+        @Override
+            @JsonProperty("b2")
+            public abstract String takeB();
+
+        // also: just for fun; add a "red herring"... unmatched method
+        @JsonProperty abstract String getFoobar();
+    }
+
+    static class LeafClass
+        extends BaseClass
+    {
+        public LeafClass(String a, String b) { super(a, b); }
+
+        @Override
+        @JsonIgnore
+        public String takeB() { return null; }
+    }
+               
+    interface ObjectMixIn
+    {
+        // and then ditto for hashCode..
+        @Override
+        @JsonProperty public int hashCode();
+    }
+
+    static class EmptyBean { }
+
+    static class SimpleBean extends EmptyBean
+    {
+        int x() { return 42; }
+    }
+
+    /**
+     * This mix-in is to be attached to EmptyBean, but really modify
+     * methods that its subclass, SimpleBean, has.
+     */
+    abstract class MixInForSimple
+    {
+        // This should apply to sub-class
+        @JsonProperty("x") abstract int x();
+
+        // and this matches nothing, should be ignored
+        @JsonProperty("notreally") public int xxx() { return 3; }
+
+        // nor this
+        public abstract int getIt();
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    /**
+     * Unit test for verifying that leaf-level mix-ins work ok; 
+     * that is, any annotations added properly override all annotations
+     * that masked methods (fields etc) have.
+     */
+    public void testLeafMixin() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result;
+        BaseClass bean = new BaseClass("a1", "b2");
+
+        // first: with no mix-ins:
+        result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals("b2", result.get("b"));
+
+        // then with leaf-level mix-in
+        mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+        result = writeAndMap(mapper, bean);
+        assertEquals(2, result.size());
+        assertEquals("b2", result.get("b2"));
+        assertEquals("a1", result.get("a"));
+    }
+
+    /**
+     * Unit test for verifying that having a mix-in "between" classes
+     * (overriding annotations of a base class, but being overridden
+     * further by a sub-class) works as expected
+     */
+    public void testIntermediateMixin() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result;
+        LeafClass bean = new LeafClass("XXX", "b2");
+
+        mapper.addMixInAnnotations(BaseClass.class, MixIn.class);
+        result = writeAndMap(mapper, bean);
+        assertEquals(1, result.size());
+        assertEquals("XXX", result.get("a"));
+    }
+
+    /**
+     * Another intermediate mix-in, to verify that annotations
+     * properly "trickle up"
+     */
+    public void testIntermediateMixin2() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(EmptyBean.class, MixInForSimple.class);
+        Map<String,Object> result = writeAndMap(mapper, new SimpleBean());
+        assertEquals(1, result.size());
+        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.addMixInAnnotations(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 (attemps) 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 Object());
+        assertEquals(2, result.size());
+        ob = result.get("hashCode");
+        assertNotNull(ob);
+        assertEquals(Integer.class, ob.getClass());
+        assertEquals("java.lang.Object", result.get("class"));
+        */
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java
new file mode 100644
index 0000000..03a8089
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/mixins/TestMixinSerWithViews.java
@@ -0,0 +1,205 @@
+package com.fasterxml.jackson.databind.mixins;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestMixinSerWithViews
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class SimpleTestData
+    {
+        private String name = "shown";
+        private String nameHidden = "hidden";
+
+        public String getName() { return name; }
+        public String getNameHidden( ) { return nameHidden; }
+
+      public void setName( String name ) {
+        this.name = name;
+      }
+
+      public void setNameHidden( String nameHidden ) {
+        this.nameHidden = nameHidden;
+      }
+    }
+
+    static class ComplexTestData
+    {
+      String nameNull = null;
+
+      String nameComplex = "complexValue";
+
+      String nameComplexHidden = "nameComplexHiddenValue";
+
+      SimpleTestData testData = new SimpleTestData( );
+
+      SimpleTestData[] testDataArray = new SimpleTestData[] { new SimpleTestData( ), null };
+
+      public String getNameNull()
+      {
+        return nameNull;
+      }
+
+      public void setNameNull( String nameNull )
+      {
+        this.nameNull = nameNull;
+      }
+
+      public String getNameComplex()
+      {
+        return nameComplex;
+      }
+
+      public void setNameComplex( String nameComplex )
+      {
+        this.nameComplex = nameComplex;
+      }
+
+      public String getNameComplexHidden()
+      {
+        return nameComplexHidden;
+      }
+
+      public void setNameComplexHidden( String nameComplexHidden )
+      {
+        this.nameComplexHidden = nameComplexHidden;
+      }
+
+      public SimpleTestData getTestData()
+      {
+        return testData;
+      }
+
+      public void setTestData( SimpleTestData testData )
+      {
+        this.testData = testData;
+      }
+
+      public SimpleTestData[] getTestDataArray()
+      {
+        return testDataArray;
+      }
+
+      public void setTestDataArray( SimpleTestData[] testDataArray )
+      {
+        this.testDataArray = testDataArray;
+      }
+    }    
+
+    public interface TestDataJAXBMixin
+    {
+      @JsonView( Views.View.class )
+      String getName( );
+    }
+
+    public interface TestComplexDataJAXBMixin
+    {
+      @JsonView( Views.View.class )
+      String getNameNull();
+
+      @JsonView( Views.View.class )
+      String getNameComplex();
+
+      @JsonView( Views.View.class )
+      String getNameComplexHidden();
+
+      @JsonView( Views.View.class )
+      SimpleTestData getTestData();
+
+      @JsonView( Views.View.class )
+      SimpleTestData[] getTestDataArray( );
+    }
+
+    static class Views {
+        static class View { }
+    }
+    
+    public class A {
+        private String name;
+        private int age;
+        private String surname;
+
+        public A(String name, int age, String surname) { super(); this.name = name; this.age = age; this.surname = surname; }
+
+        public String getName() { return name; }
+
+        public void setName(String name) { this.name = name; }
+
+        public int getAge() { return age; }
+
+        public void setAge(int age) { this.age = age; }
+
+        public String getSurname() { return surname; }
+
+        public void setSurname(String surname) { this.surname = surname; }
+    }
+
+    public interface AView { }
+
+    public abstract class AMixInAnnotation {
+        @JsonProperty("name")
+        @JsonView(AView.class)
+        abstract String getName();
+        @JsonProperty("age") @JsonView(AView.class)
+        abstract int getAge();
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+    
+    public void testDataBindingUsage( ) throws Exception
+    {
+      ObjectMapper objectMapper = createObjectMapper();
+      ObjectWriter objectWriter = objectMapper.writerWithView(Views.View.class).withDefaultPrettyPrinter();
+      Object object = new ComplexTestData();
+      String json = objectWriter.writeValueAsString(object);
+      assertTrue( json.indexOf( "nameHidden" ) == -1 );
+      assertTrue( json.indexOf( "\"name\" : \"shown\"" ) > 0 );
+    }    
+
+    public void testIssue560() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        A a = new A("myname", 29, "mysurname");
+
+        // Property SerializationConfig.SerializationFeature.DEFAULT_VIEW_INCLUSION set to false
+        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, Boolean.FALSE);
+        mapper.addMixInAnnotations(A.class, AMixInAnnotation.class);
+        String json = mapper.writerWithView(AView.class).writeValueAsString(a);
+
+        assertTrue(json.indexOf("\"name\"") > 0);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private ObjectMapper createObjectMapper( )
+    {
+      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 );
+
+      Map<Class<?>, Class<?>> sourceMixins = new HashMap<Class<?>, Class<?>>( );
+      sourceMixins.put( SimpleTestData.class, TestDataJAXBMixin.class );
+      sourceMixins.put( ComplexTestData.class, TestComplexDataJAXBMixin.class );
+      
+      objectMapper.setMixInAnnotations(sourceMixins);
+      return objectMapper;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
new file mode 100644
index 0000000..35525a8
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestAbstractTypes.java
@@ -0,0 +1,79 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class TestAbstractTypes extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes; simple beans and their handlers
+    /**********************************************************
+     */
+
+    static class MyString implements CharSequence
+    {
+        protected String value;
+        
+        public MyString(String s) { value = s; }
+
+        @Override
+        public char charAt(int index) {
+            return value.charAt(index);
+        }
+
+        @Override
+        public int length() {
+            return value.length();
+        }
+
+        @Override
+        public CharSequence subSequence(int arg0, int arg1) { return this; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testCollectionDefaulting() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        // let's ensure we get hierarchic mapping
+        mod.addAbstractTypeMapping(Collection.class, List.class);
+        mod.addAbstractTypeMapping(List.class, LinkedList.class);
+        mapper.registerModule(mod);
+        Collection<?> result = mapper.readValue("[]", Collection.class);
+        assertEquals(LinkedList.class, result.getClass());
+    }
+
+    public void testMapDefaulting() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        // default is HashMap, so:
+        mod.addAbstractTypeMapping(Map.class, TreeMap.class);
+        mapper.registerModule(mod);
+        Map<?,?> result = mapper.readValue("{}", Map.class);
+        assertEquals(TreeMap.class, result.getClass());
+    }
+    
+    public void testInterfaceDefaulting() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        // let's ensure we get hierarchic mapping
+        mod.addAbstractTypeMapping(CharSequence.class, MyString.class);
+        mapper.registerModule(mod);
+        Object result = mapper.readValue(quote("abc"), CharSequence.class);
+        assertEquals(MyString.class, result.getClass());
+        assertEquals("abc", ((MyString) result).value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java
new file mode 100644
index 0000000..a3f2227
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestKeyDeserializers.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.util.Map;
+
+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;
+
+public class TestKeyDeserializers extends BaseMapTest
+{
+    static class FooKeyDeserializer extends KeyDeserializer
+    {
+        @Override
+        public Foo deserializeKey(String key, DeserializationContext ctxt)
+        {
+            return new Foo(key);
+        }
+    }
+    
+    static class Foo {
+        public String value;
+        
+        public Foo(String v) { value = v; }
+    }
+    
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testKeyDeserializers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        mod.addKeyDeserializer(Foo.class, new FooKeyDeserializer());
+        mapper.registerModule(mod);
+        Map<Foo,Integer> map = mapper.readValue("{\"a\":3}",
+                new TypeReference<Map<Foo,Integer>>() {} );
+        assertNotNull(map);
+        assertEquals(1, map.size());
+        Foo foo = map.keySet().iterator().next();
+        assertEquals("a", foo.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java b/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java
new file mode 100644
index 0000000..96f0840
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestSimpleModule.java
@@ -0,0 +1,319 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.databind.*;
+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;
+
+ at SuppressWarnings("serial")
+public class TestSimpleModule extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes; simple beans and their handlers
+    /**********************************************************
+     */
+    
+    /**
+     * Trivial bean that requires custom serializer and deserializer
+     */
+    final static class CustomBean
+    {
+        protected String str;
+        protected int num;
+        
+        public CustomBean(String s, int i) {
+            str = s;
+            num = i;
+        }
+    }
+
+    static enum SimpleEnum { A, B; }
+    
+    // Extend SerializerBase to get access to declared handledType
+    static class CustomBeanSerializer extends StdSerializer<CustomBean>
+    {
+        public CustomBeanSerializer() { super(CustomBean.class); }
+
+        @Override
+        public void serialize(CustomBean value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+        {
+            // We will write it as a String, with '|' as delimiter
+            jgen.writeString(value.str + "|" + value.num);
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
+            return null;
+        }
+    }
+    
+    static class CustomBeanDeserializer extends JsonDeserializer<CustomBean>
+    {
+        @Override
+        public CustomBean deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            String text = jp.getText();
+            int ix = text.indexOf('|');
+            if (ix < 0) {
+                throw new IOException("Failed to parse String value of \""+text+"\"");
+            }
+            String str = text.substring(0, ix);
+            int num = Integer.parseInt(text.substring(ix+1));
+            return new CustomBean(str, num);
+        }
+    }
+
+    static class SimpleEnumSerializer extends StdSerializer<SimpleEnum>
+    {
+        public SimpleEnumSerializer() { super(SimpleEnum.class); }
+
+        @Override
+        public void serialize(SimpleEnum value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+        {
+            jgen.writeString(value.name().toLowerCase());
+        }
+
+        @Override
+        public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
+            return null;
+        }
+    }
+
+    static class SimpleEnumDeserializer extends JsonDeserializer<SimpleEnum>
+    {
+        @Override
+        public SimpleEnum deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+        {
+            return SimpleEnum.valueOf(jp.getText().toUpperCase());
+        }
+    }
+
+    interface Base {
+        public String getText();
+    }
+    
+    static class Impl1 implements Base {
+        @Override
+        public String getText() { return "1"; }
+    }
+
+    static class Impl2 extends Impl1 {
+        @Override
+        public String getText() { return "2"; }
+    }
+
+    static class BaseSerializer extends StdScalarSerializer<Base>
+    {
+        public BaseSerializer() { super(Base.class); }
+        
+        @Override
+        public void serialize(Base value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeString("Base:"+value.getText());
+        }
+    }
+
+    static class MixableBean {
+        public int a = 1;
+        public int b = 2;
+        public int c = 3;
+    }
+
+    @JsonPropertyOrder({"c", "a", "b"})
+    static class MixInForOrder { }
+    
+    protected static class MySimpleSerializers extends SimpleSerializers { }
+    protected static class MySimpleDeserializers extends SimpleDeserializers { }
+
+    /**
+     * Test module which uses custom 'serializers' and 'deserializers' container; used
+     * to trigger type problems.
+     */
+    protected static class MySimpleModule extends SimpleModule
+    {
+        public MySimpleModule(String name, Version version) {
+            super(name, version);
+            _deserializers = new MySimpleDeserializers();
+            _serializers = new MySimpleSerializers();
+        }
+    }
+
+    protected static class ContextVerifierModule extends Module
+    {
+        @Override
+        public String getModuleName() { return "x"; }
+
+        @Override
+        public Version version() { return Version.unknownVersion(); }
+
+        @Override
+        public void setupModule(SetupContext context)
+        {
+            ObjectCodec c = context.getOwner();
+            assertNotNull(c);
+            assertTrue(c instanceof ObjectMapper);
+            ObjectMapper m = context.getOwner();
+            assertNotNull(m);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests; first, verifying need for custom handlers
+    /**********************************************************
+     */
+
+    /**
+     * Basic test to ensure we do not have functioning default
+     * serializers for custom types used in tests.
+     */
+    public void testWithoutModule()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // first: serialization failure:
+        try {
+            mapper.writeValueAsString(new CustomBean("foo", 3));
+            fail("Should have caused an exception");
+        } catch (IOException e) {
+            verifyException(e, "No serializer found");
+        }
+
+        // then deserialization
+        try {
+            mapper.readValue("{\"str\":\"ab\",\"num\":2}", CustomBean.class);
+            fail("Should have caused an exception");
+        } catch (IOException e) {
+            verifyException(e, "No suitable constructor found");
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests; simple serializers
+    /**********************************************************
+     */
+    
+    public void testSimpleBeanSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        mod.addSerializer(new CustomBeanSerializer());
+        mapper.registerModule(mod);
+        assertEquals(quote("abcde|5"), mapper.writeValueAsString(new CustomBean("abcde", 5)));
+    }
+
+    public void testSimpleEnumSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        mod.addSerializer(new SimpleEnumSerializer());
+        mapper.registerModule(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);
+        assertEquals(quote("Base:1"), mapper.writeValueAsString(new Impl1()));
+        assertEquals(quote("Base:2"), mapper.writeValueAsString(new Impl2()));
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests; simple deserializers
+    /**********************************************************
+     */
+    
+    public void testSimpleBeanDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        mod.addDeserializer(CustomBean.class, new CustomBeanDeserializer());
+        mapper.registerModule(mod);
+        CustomBean bean = mapper.readValue(quote("xyz|3"), CustomBean.class);
+        assertEquals("xyz", bean.str);
+        assertEquals(3, bean.num);
+    }
+
+    public void testSimpleEnumDeserializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule mod = new SimpleModule("test", Version.unknownVersion());
+        mod.addDeserializer(SimpleEnum.class, new SimpleEnumDeserializer());
+        mapper.registerModule(mod);
+        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());
+        mod2.addSerializer(CustomBean.class, new CustomBeanSerializer());
+
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(mod1);
+        mapper.registerModule(mod2);
+        assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
+        SimpleEnum result = mapper.readValue(quote("a"), SimpleEnum.class);
+        assertSame(SimpleEnum.A, result);
+
+        // also let's try it with different order of registration, just in case
+        mapper = new ObjectMapper();
+        mapper.registerModule(mod2);
+        mapper.registerModule(mod1);
+        assertEquals(quote("b"), mapper.writeValueAsString(SimpleEnum.B));
+        result = mapper.readValue(quote("a"), SimpleEnum.class);
+        assertSame(SimpleEnum.A, result);
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests; other
+    /**********************************************************
+     */
+    
+    // [JACKSON-644]: ability to register mix-ins
+    public void testMixIns() throws Exception
+    {
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.setMixInAnnotation(MixableBean.class, MixInForOrder.class);
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(module);
+        Map<String,Object> props = this.writeAndMap(mapper, new MixableBean());
+        assertEquals(3, props.size());
+        assertEquals(Integer.valueOf(3), props.get("c"));
+        assertEquals(Integer.valueOf(1), props.get("a"));
+        assertEquals(Integer.valueOf(2), props.get("b"));
+    }
+
+    // [JACKSON-686]
+    public void testAccessToMapper() throws Exception
+    {
+        ContextVerifierModule module = new ContextVerifierModule();        
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(module);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java
new file mode 100644
index 0000000..65f1e02
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/module/TestTypeModifiers.java
@@ -0,0 +1,277 @@
+package com.fasterxml.jackson.databind.module;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.module.SimpleDeserializers;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.type.*;
+
+ at SuppressWarnings("serial")
+public class TestTypeModifiers extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class ModifierModule extends SimpleModule
+    {
+        public ModifierModule() {
+            super("test", Version.unknownVersion());
+        }
+
+        @Override
+        public void setupModule(SetupContext context)
+        {
+            context.addSerializers(new Serializers.Base() {
+                @Override
+                public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
+                        MapLikeType type, BeanDescription beanDesc,
+                        JsonSerializer<Object> keySerializer,
+                        TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+                {
+                    if (MapMarker.class.isAssignableFrom(type.getRawClass())) {
+                        return new MyMapSerializer(keySerializer, elementValueSerializer);
+                    }
+                    return null;
+                }
+
+                @Override
+                public JsonSerializer<?> findCollectionLikeSerializer(SerializationConfig config,
+                        CollectionLikeType type, BeanDescription beanDesc,
+                        TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+                {
+                    if (CollectionMarker.class.isAssignableFrom(type.getRawClass())) {
+                        return new MyCollectionSerializer();
+                    }
+                    return null;
+                }
+            });
+            context.addDeserializers(new SimpleDeserializers() {
+                @Override
+                public JsonDeserializer<?> findCollectionLikeDeserializer(CollectionLikeType type, DeserializationConfig config,
+                        BeanDescription beanDesc, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+                    throws JsonMappingException
+                {
+                    if (CollectionMarker.class.isAssignableFrom(type.getRawClass())) {
+                        return new MyCollectionDeserializer();
+                    }
+                    return null;
+                }
+                @Override
+                public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type, DeserializationConfig config,
+                        BeanDescription beanDesc, KeyDeserializer keyDeserializer,
+                        TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+                    throws JsonMappingException
+                {
+                    if (MapMarker.class.isAssignableFrom(type.getRawClass())) {
+                        return new MyMapDeserializer();
+                    }
+                    return null;
+                }
+            });
+        }
+    }
+
+    static class XxxSerializer extends JsonSerializer<Object>
+    {
+        @Override
+        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeString("xxx:"+value);
+        }
+    }
+    
+    interface MapMarker<K,V> {
+        public K getKey();
+        public V getValue();
+    }
+    interface CollectionMarker<V> {
+        public V getValue();
+    }
+
+    @JsonSerialize(contentUsing=XxxSerializer.class)
+    static class MyMapLikeType implements MapMarker<String,Integer> {
+        public String key;
+        public int value;
+
+        public MyMapLikeType() { }
+        public MyMapLikeType(String k, int v) {
+            key = k;
+            value = v;
+        }
+
+        @Override
+        public String getKey() { return key; }
+        @Override
+        public Integer getValue() { return value; }
+    }
+
+    static class MyCollectionLikeType implements CollectionMarker<Integer>
+    {
+        public int value;
+
+        public MyCollectionLikeType() { }
+        public MyCollectionLikeType(int v) {
+            value = v;
+        }
+
+        @Override
+        public Integer getValue() { return value; }
+    }
+
+    static class MyMapSerializer extends JsonSerializer<MapMarker<?,?>>
+    {
+        protected final JsonSerializer<Object> _keySerializer;
+        protected final JsonSerializer<Object> _valueSerializer;
+        
+        public MyMapSerializer(JsonSerializer<Object> keySer, JsonSerializer<Object> valueSer) {
+            _keySerializer = keySer;
+            _valueSerializer = valueSer;
+        }
+        
+        @Override
+        public void serialize(MapMarker<?,?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeStartObject();
+            if (_keySerializer == null) {
+                jgen.writeFieldName((String) value.getKey());
+            } else {
+                _keySerializer.serialize(value.getKey(), jgen, provider);
+            }
+            if (_valueSerializer == null) {
+                jgen.writeNumber(((Number) value.getValue()).intValue());
+            } else {
+                _valueSerializer.serialize(value.getValue(), jgen, provider);
+            }
+            jgen.writeEndObject();
+        }
+    }
+    static class MyMapDeserializer extends JsonDeserializer<MapMarker<?,?>>
+    {
+        @Override
+        public MapMarker<?,?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+            if (jp.getCurrentToken() != JsonToken.START_OBJECT) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            if (jp.nextToken() != JsonToken.FIELD_NAME) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            String key = jp.getCurrentName();
+            if (jp.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            int value = jp.getIntValue();
+            if (jp.nextToken() != JsonToken.END_OBJECT) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            return new MyMapLikeType(key, value);
+        }        
+    }
+
+    static class MyCollectionSerializer extends JsonSerializer<MyCollectionLikeType>
+    {
+        @Override
+        public void serialize(MyCollectionLikeType value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeStartArray();
+            jgen.writeNumber(value.value);
+            jgen.writeEndArray();
+        }
+    }
+    static class MyCollectionDeserializer extends JsonDeserializer<MyCollectionLikeType>
+    {
+        @Override
+        public MyCollectionLikeType deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+            if (jp.getCurrentToken() != JsonToken.START_ARRAY) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            if (jp.nextToken() != JsonToken.VALUE_NUMBER_INT) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            int value = jp.getIntValue();
+            if (jp.nextToken() != JsonToken.END_ARRAY) throw new IOException("Wrong token: "+jp.getCurrentToken());
+            return new MyCollectionLikeType(value);
+        }        
+    }
+    
+    static class MyTypeModifier extends TypeModifier
+    {
+        @Override
+        public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory)
+        {
+            Class<?> raw = type.getRawClass();
+            if (MapMarker.class.isAssignableFrom(raw)) {
+                JavaType[] params = typeFactory.findTypeParameters(type, MapMarker.class);
+                return typeFactory.constructMapLikeType(raw, params[0], params[1]);
+            }
+            if (CollectionMarker.class.isAssignableFrom(raw)) {
+                JavaType[] params = typeFactory.findTypeParameters(type, CollectionMarker.class);
+                return typeFactory.constructCollectionLikeType(raw, params[0]);
+            }
+            return type;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    /**
+     * Basic test for ensuring that we can get "xxx-like" types recognized.
+     */
+    public void testLikeTypeConstruction() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier()));
+        JavaType type = mapper.constructType(MyMapLikeType.class);
+        assertTrue(type.isMapLikeType());
+        // also, must have resolved type info
+        JavaType param = ((MapLikeType) type).getKeyType();
+        assertNotNull(param);
+        assertSame(String.class, param.getRawClass());
+        param = ((MapLikeType) type).getContentType();
+        assertNotNull(param);
+        assertSame(Integer.class, param.getRawClass());
+        
+        type = mapper.constructType(MyCollectionLikeType.class);
+        assertTrue(type.isCollectionLikeType());
+        param = ((CollectionLikeType) type).getContentType();
+        assertNotNull(param);
+        assertSame(Integer.class, param.getRawClass());
+    }
+
+    public void testCollectionLikeSerialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier()));
+        mapper.registerModule(new ModifierModule());
+        assertEquals("[19]", mapper.writeValueAsString(new MyCollectionLikeType(19)));
+    }
+
+    public void testMapLikeSerialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier()));
+        mapper.registerModule(new ModifierModule());
+        // Due to custom serializer, should get:
+        assertEquals("{\"x\":\"xxx:3\"}", mapper.writeValueAsString(new MyMapLikeType("x", 3)));
+    }
+
+
+    public void testCollectionLikeDeserialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier()));
+        mapper.registerModule(new ModifierModule());
+        // !!! TBI
+        MyMapLikeType result = mapper.readValue("{\"a\":13}", MyMapLikeType.class);
+        assertEquals("a", result.getKey());
+        assertEquals(Integer.valueOf(13), result.getValue());
+    }
+
+    public void testMapLikeDeserialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setTypeFactory(mapper.getTypeFactory().withModifier(new MyTypeModifier()));
+        mapper.registerModule(new ModifierModule());
+        // !!! TBI
+        MyCollectionLikeType result = mapper.readValue("[-37]", MyCollectionLikeType.class);
+        assertEquals(Integer.valueOf(-37), result.getValue());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/NodeTestBase.java b/src/test/java/com/fasterxml/jackson/databind/node/NodeTestBase.java
new file mode 100644
index 0000000..c351b79
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/NodeTestBase.java
@@ -0,0 +1,30 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+abstract class NodeTestBase extends BaseMapTest
+{
+    protected void assertNodeNumbersForNonNumeric(JsonNode n)
+    { 
+        assertFalse(n.isNumber());
+        assertEquals(0, n.asInt());
+        assertEquals(-42, n.asInt(-42));
+        assertEquals(0, n.asLong());
+        assertEquals(12345678901L, n.asLong(12345678901L));
+        assertEquals(0.0, n.asDouble());
+        assertEquals(-19.25, n.asDouble(-19.25));
+    }
+    
+    protected void assertNodeNumbers(JsonNode n, int expInt, double expDouble)
+    {
+        assertEquals(expInt, n.asInt());
+        assertEquals(expInt, n.asInt(-42));
+        assertEquals((long) expInt, n.asLong());
+        assertEquals((long) expInt, n.asLong(19L));
+        assertEquals(expDouble, n.asDouble());
+        assertEquals(expDouble, n.asDouble(-19.25));
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java
new file mode 100644
index 0000000..73e413d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestArrayNode.java
@@ -0,0 +1,167 @@
+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());
+    }
+
+    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
new file mode 100644
index 0000000..b2d51f6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestConversions.java
@@ -0,0 +1,217 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Unit tests for verifying functionality of {@link JsonNode} methods that
+ * convert values to other types
+ */
+public class TestConversions extends BaseMapTest
+{
+    static class Root {
+        public Leaf leaf;
+    }
+
+    static class Leaf {
+        public int value;
+
+        public Leaf() { }
+        public Leaf(int v) { value = v; }
+    }
+    
+    // MixIn for [JACKSON-554]
+    @JsonDeserialize(using = LeafDeserializer.class)
+    public static class LeafMixIn
+    {
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testAsInt() throws Exception
+    {
+        assertEquals(9, IntNode.valueOf(9).asInt());
+        assertEquals(7, LongNode.valueOf(7L).asInt());
+        assertEquals(13, new TextNode("13").asInt());
+        assertEquals(0, new TextNode("foobar").asInt());
+        assertEquals(27, new TextNode("foobar").asInt(27));
+        assertEquals(1, BooleanNode.TRUE.asInt());
+    }
+
+    public void testAsBoolean() throws Exception
+    {
+        assertEquals(false, BooleanNode.FALSE.asBoolean());
+        assertEquals(true, BooleanNode.TRUE.asBoolean());
+        assertEquals(false, IntNode.valueOf(0).asBoolean());
+        assertEquals(true, IntNode.valueOf(1).asBoolean());
+        assertEquals(false, LongNode.valueOf(0).asBoolean());
+        assertEquals(true, LongNode.valueOf(-34L).asBoolean());
+        assertEquals(true, new TextNode("true").asBoolean());
+        assertEquals(false, new TextNode("false").asBoolean());
+        assertEquals(false, new TextNode("barf").asBoolean());
+        assertEquals(true, new TextNode("barf").asBoolean(true));
+
+        assertEquals(true, new POJONode(Boolean.TRUE).asBoolean());
+    }
+    
+    // Deserializer to trigger the problem described in [JACKSON-554]
+    public static class LeafDeserializer extends JsonDeserializer<Leaf>
+    {
+        @Override
+        public Leaf deserialize(JsonParser jp, DeserializationContext ctxt)
+                throws IOException, JsonProcessingException
+        {
+            JsonNode tree = (JsonNode) jp.readValueAsTree();
+            Leaf leaf = new Leaf();
+            leaf.value = tree.get("value").intValue();
+            return leaf;
+        }
+    }
+
+    // Test for [JACKSON-554]
+    public void testTreeToValue() throws Exception
+    {
+        String JSON = "{\"leaf\":{\"value\":13}}";
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(Leaf.class, LeafMixIn.class);
+        JsonNode root = mapper.readTree(JSON);
+        // Ok, try converting to bean using two mechanisms
+        Root r1 = mapper.treeToValue(root, Root.class);
+        assertNotNull(r1);
+        assertEquals(13, r1.leaf.value);
+    }
+
+    // Test for [JACKSON-631]
+    public void testBase64Text() throws Exception
+    {
+        // let's actually iterate over sets of encoding modes, lengths
+        
+        final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 };
+        final Base64Variant[] VARIANTS = {
+                Base64Variants.MIME,
+                Base64Variants.MIME_NO_LINEFEEDS,
+                Base64Variants.MODIFIED_FOR_URL,
+                Base64Variants.PEM
+        };
+
+        for (int len : LENS) {
+            byte[] input = new byte[len];
+            for (int i = 0; i < input.length; ++i) {
+                input[i] = (byte) i;
+            }
+            for (Base64Variant variant : VARIANTS) {
+                TextNode n = new TextNode(variant.encode(input));
+                byte[] data = null;
+                try {
+                    data = n.getBinaryValue(variant);
+                } catch (Exception e) {
+                    throw new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage());
+                }
+                assertNotNull(data);
+                assertArrayEquals(data, input);
+            }
+        }
+    }
+
+    static class Issue709Bean {
+        public byte[] data;
+    }
+    
+    /**
+     * Simple test to verify that byte[] values can be handled properly when
+     * converting, as long as there is metadata (from POJO definitions).
+     */
+    public void testIssue709() throws Exception
+    {
+        byte[] inputData = new byte[] { 1, 2, 3 };
+        ObjectNode node = MAPPER.createObjectNode();
+        node.put("data", inputData);
+        Issue709Bean result = MAPPER.treeToValue(node, Issue709Bean.class);
+        String json = MAPPER.writeValueAsString(node);
+        Issue709Bean resultFromString = MAPPER.readValue(json, Issue709Bean.class);
+        Issue709Bean resultFromConvert = MAPPER.convertValue(node, Issue709Bean.class);
+        
+        // all methods should work equally well:
+        Assert.assertArrayEquals(inputData, resultFromString.data);
+        Assert.assertArrayEquals(inputData, resultFromConvert.data);
+        Assert.assertArrayEquals(inputData, result.data);
+    }
+
+    public void testEmbeddedObject() throws Exception
+    {
+        TokenBuffer buf = new TokenBuffer(MAPPER);
+        buf.writeObject(new byte[3]);
+        JsonNode node = MAPPER.readTree(buf.asParser());
+        buf.close();
+        assertTrue(node.isBinary());
+        byte[] data = node.binaryValue();
+        assertNotNull(data);
+        assertEquals(3, data.length);
+    }
+    
+    private final Object MARKER = new Object();
+
+    public void testEmbeddedObjectInArray() throws Exception
+    {
+        TokenBuffer buf = new TokenBuffer(MAPPER);
+        buf.writeStartArray();
+        buf.writeObject(MARKER);
+        buf.writeEndArray();
+        JsonNode node = MAPPER.readTree(buf.asParser());
+        buf.close();
+        assertTrue(node.isArray());
+        assertEquals(1, node.size());
+        JsonNode n = node.get(0);
+        assertTrue(n.isPojo());
+        assertSame(MARKER, ((POJONode) n).getPojo());
+    }
+
+    public void testEmbeddedObjectInObject() throws Exception
+    {
+        TokenBuffer buf = new TokenBuffer(MAPPER);
+        buf.writeStartObject();
+        buf.writeFieldName("pojo");
+        buf.writeObject(MARKER);
+        buf.writeEndObject();
+        JsonNode node = MAPPER.readTree(buf.asParser());
+        buf.close();
+        assertTrue(node.isObject());
+        assertEquals(1, node.size());
+        JsonNode n = node.get("pojo");
+        assertTrue(n.isPojo());
+        assertSame(MARKER, ((POJONode) n).getPojo());
+    }
+
+    // [Issue#232]
+    public void testBigDecimalAsPlainStringTreeConversion()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
+        Map<String, Object> map = new HashMap<String, Object>();
+        String PI_STR = "3.00000000";
+        map.put("pi", new BigDecimal(PI_STR));
+        JsonNode tree = mapper.valueToTree(map);
+        assertNotNull(tree);
+        assertEquals(1, tree.size());
+        assertTrue(tree.has("pi"));
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestDeepCopy.java b/src/test/java/com/fasterxml/jackson/databind/node/TestDeepCopy.java
new file mode 100644
index 0000000..5f033fa
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestDeepCopy.java
@@ -0,0 +1,88 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Simple tests to verify that [JACKSON-707] is implemented correctly.
+ */
+public class TestDeepCopy extends BaseMapTest
+{
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testWithObjectSimple()
+    {
+        ObjectNode root = mapper.createObjectNode();
+        root.put("a", 3);
+        assertEquals(1, root.size());
+        
+        ObjectNode copy = root.deepCopy();
+        assertEquals(1, copy.size());
+
+        // adding to root won't change copy:
+        root.put("b", 7);
+        assertEquals(2, root.size());
+        assertEquals(1, copy.size());
+
+        // nor vice versa
+        copy.put("c", 3);
+        assertEquals(2, root.size());
+        assertEquals(2, copy.size());
+    }
+
+    public void testWithArraySimple()
+    {
+        ArrayNode root = mapper.createArrayNode();
+        root.add("a");
+        assertEquals(1, root.size());
+        
+        ArrayNode copy = root.deepCopy();
+        assertEquals(1, copy.size());
+
+        // adding to root won't change copy:
+        root.add( 7);
+        assertEquals(2, root.size());
+        assertEquals(1, copy.size());
+
+        // nor vice versa
+        copy.add(3);
+        assertEquals(2, root.size());
+        assertEquals(2, copy.size());
+    }
+
+    public void testWithNested()
+    {
+        ObjectNode root = mapper.createObjectNode();
+        ObjectNode leafObject = root.putObject("ob");
+        ArrayNode leafArray = root.putArray("arr");
+        assertEquals(2, root.size());
+
+        leafObject.put("a", 3);
+        assertEquals(1, leafObject.size());
+        leafArray.add(true);
+        assertEquals(1, leafArray.size());
+        
+        ObjectNode copy = root.deepCopy();
+        assertNotSame(copy, root);
+        assertEquals(2, copy.size());
+
+        // should be detached, once again
+
+        leafObject.put("x", 9);
+        assertEquals(2, leafObject.size());
+        assertEquals(1, copy.get("ob").size());
+
+        leafArray.add("foobar");
+        assertEquals(2, leafArray.size());
+        assertEquals(1, copy.get("arr").size());
+
+        // nor vice versa
+        ((ObjectNode) copy.get("ob")).put("c", 3);
+        assertEquals(2, leafObject.size());
+        assertEquals(2, copy.get("ob").size());
+
+        ((ArrayNode) copy.get("arr")).add(13);
+        assertEquals(2, leafArray.size());
+        assertEquals(2, copy.get("arr").size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestFindMethods.java b/src/test/java/com/fasterxml/jackson/databind/node/TestFindMethods.java
new file mode 100644
index 0000000..21bc900
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestFindMethods.java
@@ -0,0 +1,73 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class TestFindMethods
+    extends BaseMapTest
+{
+    public void testNonMatching() throws Exception
+    {
+        JsonNode root = _buildTree();
+
+        assertNull(root.findValue("boogaboo"));
+        assertNull(root.findParent("boogaboo"));
+        JsonNode n = root.findPath("boogaboo");
+        assertNotNull(n);
+        assertTrue(n.isMissingNode());
+
+        assertTrue(root.findValues("boogaboo").isEmpty());
+        assertTrue(root.findParents("boogaboo").isEmpty());
+    }
+
+    public void testMatchingSingle() throws Exception
+    {
+        JsonNode root = _buildTree();
+
+        JsonNode node = root.findValue("b");
+        assertNotNull(node);
+        assertEquals(3, node.intValue());
+        node = root.findParent("b");
+        assertNotNull(node);
+        assertTrue(node.isObject());
+        assertEquals(1, ((ObjectNode) node).size());
+        assertEquals(3, node.path("b").intValue());
+    }
+
+    public void testMatchingMultiple() throws Exception
+    {
+        JsonNode root = _buildTree();
+
+        List<JsonNode> nodes = root.findValues("value");
+        assertEquals(2, nodes.size());
+        // here we count on nodes being returned in order; true with Jackson:
+        assertEquals(3, nodes.get(0).intValue());
+        assertEquals(42, nodes.get(1).intValue());
+
+        nodes = root.findParents("value");
+        assertEquals(2, nodes.size());
+        // should only return JSON Object nodes:
+        assertTrue(nodes.get(0).isObject());
+        assertTrue(nodes.get(1).isObject());
+        assertEquals(3, nodes.get(0).path("value").intValue());
+        assertEquals(42, nodes.get(1).path("value").intValue());
+
+        // and finally, convenience conversion method
+        List<String> values = root.findValuesAsText("value");
+        assertEquals(2, values.size());
+        assertEquals("3", values.get(0));
+        assertEquals("42", values.get(1));
+    }
+    
+    private JsonNode _buildTree() throws Exception
+    {
+        final String SAMPLE = "{ \"a\" : { \"value\" : 3 },"
+            +"\"array\" : [ { \"b\" : 3 }, {\"value\" : 42}, { \"other\" : true } ]"
+            +"}";
+        return new ObjectMapper().readTree(SAMPLE);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java
new file mode 100644
index 0000000..ba8e465
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestJsonNode.java
@@ -0,0 +1,102 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Basic tests for {@link JsonNode} base class and some features
+ * of implementation classes
+ */
+public class TestJsonNode extends NodeTestBase
+{
+    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
+        assertNodeNumbersForNonNumeric(TextNode.valueOf("foobar"));
+
+    }
+
+    public void testBoolean()
+    {
+        BooleanNode f = BooleanNode.getFalse();
+        assertNotNull(f);
+        assertTrue(f.isBoolean());
+        assertSame(f, BooleanNode.valueOf(false));
+        assertStandardEquals(f);
+        assertFalse(f.booleanValue());
+        assertFalse(f.asBoolean());
+        assertEquals("false", f.asText());
+        assertEquals(JsonToken.VALUE_FALSE, f.asToken());
+
+        // and ditto for true
+        BooleanNode t = BooleanNode.getTrue();
+        assertNotNull(t);
+        assertTrue(t.isBoolean());
+        assertSame(t, BooleanNode.valueOf(true));
+        assertStandardEquals(t);
+        assertTrue(t.booleanValue());
+        assertTrue(t.asBoolean());
+        assertEquals("true", t.asText());
+        assertEquals(JsonToken.VALUE_TRUE, t.asToken());
+
+        // 1.6:
+        assertNodeNumbers(f, 0, 0.0);
+        assertNodeNumbers(t, 1, 1.0);
+    }
+
+
+    public void testBinary() throws Exception
+    {
+        assertNull(BinaryNode.valueOf(null));
+        assertNull(BinaryNode.valueOf(null, 0, 0));
+
+        BinaryNode empty = BinaryNode.valueOf(new byte[1], 0, 0);
+        assertSame(BinaryNode.EMPTY_BINARY_NODE, empty);
+        assertStandardEquals(empty);
+
+        byte[] data = new byte[3];
+        data[1] = (byte) 3;
+        BinaryNode n = BinaryNode.valueOf(data, 1, 1);
+        data[2] = (byte) 3;
+        BinaryNode n2 = BinaryNode.valueOf(data, 2, 1);
+        assertTrue(n.equals(n2));
+        assertEquals("\"Aw==\"", n.toString());
+
+        assertEquals("AAMD", new BinaryNode(data).asText());
+
+        // 1.6:
+        assertNodeNumbersForNonNumeric(n);
+    }
+
+    public void testPOJO()
+    {
+        POJONode n = new POJONode("x"); // not really a pojo but that's ok
+        assertStandardEquals(n);
+        assertEquals(n, new POJONode("x"));
+        assertEquals("x", n.asText());
+        // not sure if this is what it'll remain as but:
+        assertEquals("x", n.toString());
+
+        assertEquals(new POJONode(null), new POJONode(null));
+
+        // 1.6:
+        // default; non-numeric
+        assertNodeNumbersForNonNumeric(n);
+        // but if wrapping actual number, use it
+        assertNodeNumbers(new POJONode(Integer.valueOf(123)), 123, 123.0);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java
new file mode 100644
index 0000000..bd0fd5f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestMissingNode.java
@@ -0,0 +1,28 @@
+package com.fasterxml.jackson.databind.node;
+
+import com.fasterxml.jackson.core.JsonToken;
+
+public class TestMissingNode extends NodeTestBase
+{
+    public void testMissing()
+    {
+        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));
+        assertEquals(0.25, n.asDouble(0.25));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java
new file mode 100644
index 0000000..7fdbb76
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestNullNode.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class TestNullNode extends NodeTestBase
+{
+    public void testBasicsWithNullNode() throws Exception
+    {
+        // Let's use something that doesn't add much beyond JsonNode base
+        NullNode n = NullNode.instance;
+
+        // basic properties
+        assertFalse(n.isContainerNode());
+        assertFalse(n.isBigDecimal());
+        assertFalse(n.isBigInteger());
+        assertFalse(n.isBinary());
+        assertFalse(n.isBoolean());
+        assertFalse(n.isPojo());
+        assertFalse(n.isMissingNode());
+
+        // fallback accessors
+        assertFalse(n.booleanValue());
+        assertNull(n.numberValue());
+        assertEquals(0, n.intValue());
+        assertEquals(0L, n.longValue());
+        assertEquals(BigDecimal.ZERO, n.decimalValue());
+        assertEquals(BigInteger.ZERO, n.bigIntegerValue());
+
+        assertEquals(0, n.size());
+        assertFalse(n.elements().hasNext());
+        assertFalse(n.fieldNames().hasNext());
+        // path is never null; but does point to missing node
+        assertNotNull(n.path("xyz"));
+        assertTrue(n.path("xyz").isMissingNode());
+
+        assertFalse(n.has("field"));
+        assertFalse(n.has(3));
+
+        // 1.6:
+        assertNodeNumbersForNonNumeric(n);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java b/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
new file mode 100644
index 0000000..6cb89f6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestNumberNodes.java
@@ -0,0 +1,226 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Basic tests for {@link JsonNode} implementations that
+ * contain numeric values.
+ */
+public class TestNumberNodes extends NodeTestBase
+{
+    public void testShort()
+    {
+        ShortNode n = ShortNode.valueOf((short) 1);
+        assertStandardEquals(n);
+        assertTrue(0 != n.hashCode());
+        assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
+        assertEquals(JsonParser.NumberType.INT, n.numberType());	// should be SHORT
+        assertEquals(1, n.intValue());
+        assertEquals(1L, n.longValue());
+        assertEquals(BigDecimal.ONE, n.decimalValue());
+        assertEquals(BigInteger.ONE, n.bigIntegerValue());
+        assertEquals("1", n.asText());
+
+        assertNodeNumbers(n, 1, 1.0);
+
+        assertTrue(ShortNode.valueOf((short) 0).canConvertToInt());
+        assertTrue(ShortNode.valueOf(Short.MAX_VALUE).canConvertToInt());
+        assertTrue(ShortNode.valueOf(Short.MIN_VALUE).canConvertToInt());
+
+        assertTrue(ShortNode.valueOf((short) 0).canConvertToLong());
+        assertTrue(ShortNode.valueOf(Short.MAX_VALUE).canConvertToLong());
+        assertTrue(ShortNode.valueOf(Short.MIN_VALUE).canConvertToLong());
+    }
+    
+	public void testInt()
+    {
+        IntNode n = IntNode.valueOf(1);
+        assertStandardEquals(n);
+        assertTrue(0 != n.hashCode());
+        assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
+        assertEquals(JsonParser.NumberType.INT, n.numberType());
+        assertEquals(1, n.intValue());
+        assertEquals(1L, n.longValue());
+        assertEquals(BigDecimal.ONE, n.decimalValue());
+        assertEquals(BigInteger.ONE, n.bigIntegerValue());
+        assertEquals("1", n.asText());
+
+        assertNodeNumbers(n, 1, 1.0);
+
+        assertTrue(IntNode.valueOf(0).canConvertToInt());
+        assertTrue(IntNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
+        assertTrue(IntNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
+
+        assertTrue(IntNode.valueOf(0).canConvertToLong());
+        assertTrue(IntNode.valueOf(Integer.MAX_VALUE).canConvertToLong());
+        assertTrue(IntNode.valueOf(Integer.MIN_VALUE).canConvertToLong());
+    }
+
+    public void testLong()
+    {
+        LongNode n = LongNode.valueOf(1L);
+        assertStandardEquals(n);
+        assertTrue(0 != n.hashCode());
+        assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
+        assertEquals(JsonParser.NumberType.LONG, n.numberType());
+        assertEquals(1, n.intValue());
+        assertEquals(1L, n.longValue());
+        assertEquals(BigDecimal.ONE, n.decimalValue());
+        assertEquals(BigInteger.ONE, n.bigIntegerValue());
+        assertEquals("1", n.asText());
+
+        assertNodeNumbers(n, 1, 1.0);
+
+        // ok if contains small enough value
+        assertTrue(LongNode.valueOf(0).canConvertToInt());
+        assertTrue(LongNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
+        assertTrue(LongNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
+        // but not in other cases
+        assertFalse(LongNode.valueOf(1L + Integer.MAX_VALUE).canConvertToInt());
+        assertFalse(LongNode.valueOf(-1L + Integer.MIN_VALUE).canConvertToInt());
+
+        assertTrue(LongNode.valueOf(0L).canConvertToLong());
+        assertTrue(LongNode.valueOf(Long.MAX_VALUE).canConvertToLong());
+        assertTrue(LongNode.valueOf(Long.MIN_VALUE).canConvertToLong());
+    }
+
+    public void testDouble()
+    {
+        DoubleNode n = DoubleNode.valueOf(0.25);
+        assertStandardEquals(n);
+        assertTrue(0 != n.hashCode());
+        assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
+        assertEquals(JsonParser.NumberType.DOUBLE, n.numberType());
+        assertEquals(0, n.intValue());
+        assertEquals(0.25, n.doubleValue());
+        assertNotNull(n.decimalValue());
+        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());
+        assertTrue(DoubleNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
+        assertTrue(DoubleNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
+        assertFalse(DoubleNode.valueOf(1L + Integer.MAX_VALUE).canConvertToInt());
+        assertFalse(DoubleNode.valueOf(-1L + Integer.MIN_VALUE).canConvertToInt());
+
+        assertTrue(DoubleNode.valueOf(0L).canConvertToLong());
+        assertTrue(DoubleNode.valueOf(Long.MAX_VALUE).canConvertToLong());
+        assertTrue(DoubleNode.valueOf(Long.MIN_VALUE).canConvertToLong());
+    }
+
+    // @since 2.2
+    public void testFloat()
+    {
+        FloatNode n = FloatNode.valueOf(0.25f);
+        assertStandardEquals(n);
+        assertTrue(0 != n.hashCode());
+        assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
+        assertEquals(JsonParser.NumberType.FLOAT, n.numberType());
+        assertEquals(0, n.intValue());
+        assertEquals(0.25, n.doubleValue());
+        assertEquals(0.25f, n.floatValue());
+        assertNotNull(n.decimalValue());
+        assertEquals(BigInteger.ZERO, n.bigIntegerValue());
+        assertEquals("0.25", n.asText());
+
+        // 1.6:
+        assertNodeNumbers(FloatNode.valueOf(4.5f), 4, 4.5f);
+
+        assertTrue(FloatNode.valueOf(0).canConvertToInt());
+        assertTrue(FloatNode.valueOf(Integer.MAX_VALUE).canConvertToInt());
+        assertTrue(FloatNode.valueOf(Integer.MIN_VALUE).canConvertToInt());
+
+        // rounding errors if we just add/sub 1... so:
+        assertFalse(FloatNode.valueOf(1000L + Integer.MAX_VALUE).canConvertToInt());
+        assertFalse(FloatNode.valueOf(-1000L + Integer.MIN_VALUE).canConvertToInt());
+
+        assertTrue(FloatNode.valueOf(0L).canConvertToLong());
+        assertTrue(FloatNode.valueOf(Integer.MAX_VALUE).canConvertToLong());
+        assertTrue(FloatNode.valueOf(Integer.MIN_VALUE).canConvertToLong());
+    }
+    
+    public void testDecimalNode() throws Exception
+    {
+        DecimalNode n = DecimalNode.valueOf(BigDecimal.ONE);
+        assertStandardEquals(n);
+        assertTrue(n.equals(new DecimalNode(BigDecimal.ONE)));
+        assertEquals(JsonToken.VALUE_NUMBER_FLOAT, n.asToken());
+        assertEquals(JsonParser.NumberType.BIG_DECIMAL, n.numberType());
+        assertTrue(n.isNumber());
+        assertFalse(n.isIntegralNumber());
+        assertTrue(n.isBigDecimal());
+        assertEquals(BigDecimal.ONE, n.numberValue());
+        assertEquals(1, n.intValue());
+        assertEquals(1L, n.longValue());
+        assertEquals(BigDecimal.ONE, n.decimalValue());
+        assertEquals("1", n.asText());
+
+        // 1.6:
+        assertNodeNumbers(n, 1, 1.0);
+
+        assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToInt());
+        assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Integer.MAX_VALUE)).canConvertToInt());
+        assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Integer.MIN_VALUE)).canConvertToInt());
+        assertFalse(DecimalNode.valueOf(BigDecimal.valueOf(1L + Integer.MAX_VALUE)).canConvertToInt());
+        assertFalse(DecimalNode.valueOf(BigDecimal.valueOf(-1L + Integer.MIN_VALUE)).canConvertToInt());
+
+        assertTrue(DecimalNode.valueOf(BigDecimal.ZERO).canConvertToLong());
+        assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MAX_VALUE)).canConvertToLong());
+        assertTrue(DecimalNode.valueOf(BigDecimal.valueOf(Long.MIN_VALUE)).canConvertToLong());
+    }
+
+    public void testBigIntegerNode() throws Exception
+    {
+        BigIntegerNode n = BigIntegerNode.valueOf(BigInteger.ONE);
+        assertStandardEquals(n);
+        assertTrue(n.equals(new BigIntegerNode(BigInteger.ONE)));
+        assertEquals(JsonToken.VALUE_NUMBER_INT, n.asToken());
+        assertEquals(JsonParser.NumberType.BIG_INTEGER, n.numberType());
+        assertTrue(n.isNumber());
+        assertTrue(n.isIntegralNumber());
+        assertTrue(n.isBigInteger());
+        assertEquals(BigInteger.ONE, n.numberValue());
+        assertEquals(1, n.intValue());
+        assertEquals(1L, n.longValue());
+        assertEquals(BigInteger.ONE, n.bigIntegerValue());
+        assertEquals("1", n.asText());
+        
+        // 1.6:
+        assertNodeNumbers(n, 1, 1.0);
+
+        BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
+        
+        n = BigIntegerNode.valueOf(maxLong);
+        assertEquals(Long.MAX_VALUE, n.longValue());
+
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode n2 = mapper.readTree(maxLong.toString());
+        assertEquals(Long.MAX_VALUE, n2.longValue());
+
+        // then over long limit:
+        BigInteger beyondLong = maxLong.shiftLeft(2); // 4x max long
+        n2 = mapper.readTree(beyondLong.toString());
+        assertEquals(beyondLong, n2.bigIntegerValue());
+
+        assertTrue(BigIntegerNode.valueOf(BigInteger.ZERO).canConvertToInt());
+        assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Integer.MAX_VALUE)).canConvertToInt());
+        assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Integer.MIN_VALUE)).canConvertToInt());
+        assertFalse(BigIntegerNode.valueOf(BigInteger.valueOf(1L + Integer.MAX_VALUE)).canConvertToInt());
+        assertFalse(BigIntegerNode.valueOf(BigInteger.valueOf(-1L + Integer.MIN_VALUE)).canConvertToInt());
+
+        assertTrue(BigIntegerNode.valueOf(BigInteger.ZERO).canConvertToLong());
+        assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Long.MAX_VALUE)).canConvertToLong());
+        assertTrue(BigIntegerNode.valueOf(BigInteger.valueOf(Long.MIN_VALUE)).canConvertToLong());
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java b/src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java
new file mode 100644
index 0000000..4e26b4f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestObjectNode.java
@@ -0,0 +1,232 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Additional tests for {@link ObjectNode} container class.
+ */
+public class TestObjectNode
+    extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testBasics()
+    {
+        ObjectNode n = new ObjectNode(JsonNodeFactory.instance);
+        assertStandardEquals(n);
+
+        assertFalse(n.elements().hasNext());
+        assertFalse(n.fields().hasNext());
+        assertFalse(n.fieldNames().hasNext());
+        assertNull(n.get("a"));
+        assertTrue(n.path("a").isMissingNode());
+
+        TextNode text = TextNode.valueOf("x");
+        n.set("a", text);
+        assertEquals(1, n.size());
+        assertTrue(n.elements().hasNext());
+        assertTrue(n.fields().hasNext());
+        assertTrue(n.fieldNames().hasNext());
+        assertSame(text, n.get("a"));
+        assertSame(text, n.path("a"));
+        assertNull(n.get("b"));
+        assertNull(n.get(0)); // not used with objects
+
+        assertFalse(n.has(0));
+        assertFalse(n.hasNonNull(0));
+        assertTrue(n.has("a"));
+        assertTrue(n.hasNonNull("a"));
+        assertFalse(n.has("b"));
+        assertFalse(n.hasNonNull("b"));
+
+        ObjectNode n2 = new ObjectNode(JsonNodeFactory.instance);
+        n2.put("b", 13);
+        assertFalse(n.equals(n2));
+        n.setAll(n2);
+        
+        assertEquals(2, n.size());
+        n.set("null", (JsonNode)null);
+        assertEquals(3, n.size());
+        // may be non-intuitive, but explicit nulls do exist in tree:
+        assertTrue(n.has("null"));
+        assertFalse(n.hasNonNull("null"));
+        // should replace, not add
+        n.put("null", "notReallNull");
+        assertEquals(3, n.size());
+        assertNotNull(n.remove("null"));
+        assertEquals(2, n.size());
+
+        Map<String,JsonNode> nodes = new HashMap<String,JsonNode>();
+        nodes.put("d", text);
+        n.setAll(nodes);
+        assertEquals(3, n.size());
+
+        n.removeAll();
+        assertEquals(0, n.size());
+    }
+
+    /**
+     * Verify null handling
+     */
+    public void testNullChecking()
+    {
+        ObjectNode o1 = JsonNodeFactory.instance.objectNode();
+        ObjectNode o2 = JsonNodeFactory.instance.objectNode();
+        // used to throw NPE before fix:
+        o1.setAll(o2);
+        assertEquals(0, o1.size());
+        assertEquals(0, o2.size());
+
+        // also: nulls should be converted to NullNodes...
+        o1.set("x", null);
+        JsonNode n = o1.get("x");
+        assertNotNull(n);
+        assertSame(n, NullNode.instance);
+
+        o1.put("str", (String) null);
+        n = o1.get("str");
+        assertNotNull(n);
+        assertSame(n, NullNode.instance);
+
+        o1.put("d", (BigDecimal) null);
+        n = o1.get("d");
+        assertNotNull(n);
+        assertSame(n, NullNode.instance);
+    }
+
+    /**
+     * Another test to verify [JACKSON-227]...
+     */
+    public void testNullChecking2()
+    {
+        ObjectNode src = MAPPER.createObjectNode();
+        ObjectNode dest = MAPPER.createObjectNode();
+        src.put("a", "b");
+        dest.setAll(src);
+    }
+
+    public void testRemove()
+    {
+        ObjectNode ob = MAPPER.createObjectNode();
+        ob.put("a", "a");
+        ob.put("b", "b");
+        ob.put("c", "c");
+        assertEquals(3, ob.size());
+        assertSame(ob, ob.without(Arrays.asList("a", "c")));
+        assertEquals(1, ob.size());
+        assertEquals("b", ob.get("b").textValue());
+    }
+
+    public void testRetain()
+    {
+        ObjectNode ob = MAPPER.createObjectNode();
+        ob.put("a", "a");
+        ob.put("b", "b");
+        ob.put("c", "c");
+        assertEquals(3, ob.size());
+        assertSame(ob, ob.retain("a", "c"));
+        assertEquals(2, ob.size());
+        assertEquals("a", ob.get("a").textValue());
+        assertNull(ob.get("b"));
+        assertEquals("c", ob.get("c").textValue());
+    }
+
+    public void testValidWith() throws Exception
+    {
+        ObjectNode root = MAPPER.createObjectNode();
+        assertEquals("{}", MAPPER.writeValueAsString(root));
+        JsonNode child = root.with("prop");
+        assertTrue(child instanceof ObjectNode);
+        assertEquals("{\"prop\":{}}", MAPPER.writeValueAsString(root));
+    }
+
+    public void testValidWithArray() throws Exception
+    {
+        ObjectNode root = MAPPER.createObjectNode();
+        assertEquals("{}", MAPPER.writeValueAsString(root));
+        JsonNode child = root.withArray("arr");
+        assertTrue(child instanceof ArrayNode);
+        assertEquals("{\"arr\":[]}", MAPPER.writeValueAsString(root));
+    }
+
+    public void testInvalidWith() throws Exception
+    {
+        JsonNode root = MAPPER.createArrayNode();
+        try { // should not work for non-ObjectNode nodes:
+            root.with("prop");
+            fail("Expected exception");
+        } catch (UnsupportedOperationException e) {
+            verifyException(e, "not of type ObjectNode");
+        }
+        // also: should fail of we already have non-object property
+        ObjectNode root2 = MAPPER.createObjectNode();
+        root2.put("prop", 13);
+        try { // should not work for non-ObjectNode nodes:
+            root2.with("prop");
+            fail("Expected exception");
+        } catch (UnsupportedOperationException e) {
+            verifyException(e, "has value that is not");
+        }
+    }
+
+    public void testInvalidWithArray() throws Exception
+    {
+        JsonNode root = MAPPER.createArrayNode();
+        try { // should not work for non-ObjectNode nodes:
+            root.withArray("prop");
+            fail("Expected exception");
+        } catch (UnsupportedOperationException e) {
+            verifyException(e, "not of type ObjectNode");
+        }
+        // also: should fail of we already have non-Array property
+        ObjectNode root2 = MAPPER.createObjectNode();
+        root2.put("prop", 13);
+        try { // should not work for non-ObjectNode nodes:
+            root2.withArray("prop");
+            fail("Expected exception");
+        } catch (UnsupportedOperationException e) {
+            verifyException(e, "has value that is not");
+        }
+    }
+
+    // [Issue#93]
+    public void testSetAll() throws Exception
+    {
+        ObjectNode root = MAPPER.createObjectNode();
+        assertEquals(0, root.size());
+        HashMap<String,JsonNode> map = new HashMap<String,JsonNode>();
+        map.put("a", root.numberNode(1));
+        root.setAll(map);
+        assertEquals(1, root.size());
+        assertTrue(root.has("a"));
+        assertFalse(root.has("b"));
+
+        map.put("b", root.numberNode(2));
+        root.setAll(map);
+        assertEquals(2, root.size());
+        assertTrue(root.has("a"));
+        assertTrue(root.has("b"));
+        assertEquals(2, root.path("b").intValue());
+
+        // Then with ObjectNodes...
+        ObjectNode root2 = MAPPER.createObjectNode();
+        root2.setAll(root);
+        assertEquals(2, root.size());
+        assertEquals(2, root2.size());
+
+        root2.setAll(root);
+        assertEquals(2, root.size());
+        assertEquals(2, root2.size());
+
+        ObjectNode root3 = MAPPER.createObjectNode();
+        root3.put("a", 2);
+        root3.put("c", 3);
+        assertEquals(2, root3.path("a").intValue());
+        root3.setAll(root2);
+        assertEquals(3, root3.size());
+        assertEquals(1, root3.path("a").intValue());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java
new file mode 100644
index 0000000..1f88848
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeDeserialization.java
@@ -0,0 +1,118 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.io.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite tries to verify that JsonNode-based trees
+ * can be deserialized as expected.
+ */
+public class TestTreeDeserialization
+    extends BaseMapTest
+{
+    final static class Bean {
+        int _x;
+        JsonNode _node;
+
+        public void setX(int x) { _x = x; }
+        public void setNode(JsonNode n) { _node = 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);
+        ObjectNode n2 = new ObjectNode(null);
+
+        assertTrue(n1.equals(n2));
+        assertTrue(n2.equals(n1));
+
+        n1.set("x", TextNode.valueOf("Test"));
+
+        assertFalse(n1.equals(n2));
+        assertFalse(n2.equals(n1));
+
+        n2.set("x", TextNode.valueOf("Test"));
+
+        assertTrue(n1.equals(n2));
+        assertTrue(n2.equals(n1));
+    }
+
+    public void testReadFromString() throws Exception
+    {
+        String json = "{\"field\":\"{\\\"name\\\":\\\"John Smith\\\"}\"}";
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode jNode = mapper.readValue(json, JsonNode.class);
+
+        String generated = mapper.writeValueAsString( jNode);  //back slashes are gone
+        JsonNode out = mapper.readValue( generated, JsonNode.class );   //crashes here
+        assertTrue(out.isObject());
+        assertEquals(1, out.size());
+        String value = out.path("field").asText();
+        assertNotNull(value);
+    }
+
+    // Issue#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());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java
new file mode 100644
index 0000000..92dbc1b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperDeserializer.java
@@ -0,0 +1,438 @@
+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.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * This unit test suite tries to verify that ObjectMapper
+ * can properly parse JSON and bind contents into appropriate
+ * JsonNode instances.
+ */
+public class TestTreeMapperDeserializer
+    extends BaseTest
+{
+	public void testSimple()
+        throws Exception
+    {
+        final String JSON = SAMPLE_DOC_JSON_SPEC;
+
+        ObjectMapper mapper = new ObjectMapper();
+
+        for (int type = 0; type < 2; ++type) {
+            JsonNode result;
+
+            if (type == 0) {
+                result = mapper.readTree(new StringReader(JSON));
+            } else {
+                result = mapper.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
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode result = mapper.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
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        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));
+    }
+
+    public void testInt()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        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 testLong()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // 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 testNull()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode result = mapper.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
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.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 = mapper.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();
+        ObjectMapper mapper = new ObjectMapper();
+        JsonParser jp = jf.createParser(new StringReader(JSON));
+        JsonNode result = mapper.readTree(jp);
+
+        assertTrue(result.isObject());
+        assertEquals(4, result.size());
+
+        assertNull(mapper.readTree(jp));
+    }
+
+    public void testMultiple() throws Exception
+    {
+        String JSON = "12  \"string\" [ 1, 2, 3 ]";
+        JsonFactory jf = new JsonFactory();
+        ObjectMapper mapper = new ObjectMapper();
+        JsonParser jp = jf.createParser(new StringReader(JSON));
+        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));
+    }
+
+    /**
+     * 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 = "[ { }, [ ] ]";
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode result = mapper.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
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        final String JSON = "[[[-0.027512,51.503221],[-0.008497,51.503221],[-0.008497,51.509744],[-0.027512,51.509744]]]";
+
+        JsonNode n = mapper.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/TestTreeMapperMaps.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperMaps.java
new file mode 100644
index 0000000..e4b6ec0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperMaps.java
@@ -0,0 +1,74 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * Unit tests to verify that Json Objects map property to Map-like
+ * ObjectNodes.
+ */
+public class TestTreeMapperMaps
+    extends BaseTest
+{
+    public void testSimpleObject() throws Exception
+    {
+        String JSON = "{ \"key\" : 1, \"b\" : \"x\" }";
+        ObjectMapper mapper = new ObjectMapper();
+        JsonNode root = mapper.readTree(JSON);
+
+        // basic properties first:
+        assertFalse(root.isValueNode());
+        assertTrue(root.isContainerNode());
+        assertFalse(root.isArray());
+        assertTrue(root.isObject());
+        assertEquals(2, root.size());
+
+        // Related to [JACKSON-50]:
+        Iterator<JsonNode> it = root.iterator();
+        assertNotNull(it);
+        assertTrue(it.hasNext());
+        JsonNode n = it.next();
+        assertNotNull(n);
+        assertEquals(IntNode.valueOf(1), n);
+
+        assertTrue(it.hasNext());
+        n = it.next();
+        assertNotNull(n);
+        assertEquals(TextNode.valueOf("x"), n);
+
+        assertFalse(it.hasNext());
+
+        // Ok, then, let's traverse via extended interface
+        ObjectNode obNode = (ObjectNode) root;
+        Iterator<Map.Entry<String,JsonNode>> fit = obNode.fields();
+        // we also know that LinkedHashMap is used, i.e. order preserved
+        assertTrue(fit.hasNext());
+        Map.Entry<String,JsonNode> en = fit.next();
+        assertEquals("key", en.getKey());
+        assertEquals(IntNode.valueOf(1), en.getValue());
+
+        assertTrue(fit.hasNext());
+        en = fit.next();
+        assertEquals("b", en.getKey());
+        assertEquals(TextNode.valueOf("x"), en.getValue());
+
+        // Plus: we should be able to modify the node via iterator too:
+        fit.remove();
+        assertEquals(1, obNode.size());
+        assertEquals(IntNode.valueOf(1), root.get("key"));
+        assertNull(root.get("b"));
+    }
+
+    public void testSimplePath() throws Exception
+    {
+        JsonNode root = new ObjectMapper().readTree("{ \"results\" : { \"a\" : 3 } }");
+        assertTrue(root.isObject());
+        JsonNode rnode = root.path("results");
+        assertNotNull(rnode);
+        assertTrue(rnode.isObject());
+        assertEquals(3, rnode.path("a").intValue());
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
new file mode 100644
index 0000000..f75252c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeMapperSerializer.java
@@ -0,0 +1,223 @@
+package com.fasterxml.jackson.databind.node;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * This unit test suite tries to verify that the trees ObjectMapper
+ * constructs can be serialized properly.
+ */
+public class TestTreeMapperSerializer
+    extends BaseTest
+{
+    final static String FIELD1 = "first";
+    final static String FIELD2 = "Second?";
+    final static String FIELD3 = "foo'n \"bar\"";
+    final static String FIELD4 = "4";
+
+    final static String TEXT1 = "Some text & \"stuff\"";
+    final static String TEXT2 = "Some more text:\twith\nlinefeeds and all!";
+
+    final static double DOUBLE_VALUE = 9.25;
+
+    public void testFromArray()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode root = mapper.createArrayNode();
+        root.add(TEXT1);
+        root.add(3);
+        ObjectNode obj = root.addObject();
+        obj.put(FIELD1, true);
+        obj.putArray(FIELD2);
+        root.add(false);
+
+        /* Ok, ready... let's serialize using one of two alternate
+         * methods: first preferred (using generator)
+         * (there are 2 variants here too)
+         */
+        for (int i = 0; i < 2; ++i) {
+            StringWriter sw = new StringWriter();
+            if (i == 0) {
+                JsonGenerator gen = new JsonFactory().createGenerator(sw);
+                root.serialize(gen, null);
+                gen.close();
+            } else {
+                mapper.writeValue(sw, root);
+            }
+            verifyFromArray(sw.toString());
+        }
+            
+        // And then convenient but less efficient alternative:
+        verifyFromArray(root.toString());
+    }
+
+    public void testFromMap()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode root = mapper.createObjectNode();
+        root.put(FIELD4, TEXT2);
+        root.put(FIELD3, -1);
+        root.putArray(FIELD2);
+        root.put(FIELD1, DOUBLE_VALUE);
+
+        /* Let's serialize using one of two alternate methods:
+         * first preferred (using generator)
+         * (there are 2 variants here too)
+         */
+        for (int i = 0; i < 2; ++i) {
+            StringWriter sw = new StringWriter();
+            if (i == 0) {
+                JsonGenerator gen = new JsonFactory().createGenerator(sw);
+                root.serialize(gen, null);
+                gen.close();
+            } else {
+                mapper.writeValue(sw, root);
+            }
+            verifyFromMap(sw.toString());
+        }
+
+        // And then convenient but less efficient alternative:
+        verifyFromMap(root.toString());
+    }
+
+    /**
+     * Unit test to check for regression of [JACKSON-18].
+     */
+    public void testSmallNumbers()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode root = mapper.createArrayNode();
+        for (int i = -20; i <= 20; ++i) {
+            JsonNode n = root.numberNode(i);
+            root.add(n);
+            // Hmmh. Not sure why toString() won't be triggered otherwise...
+            assertEquals(String.valueOf(i), n.toString());
+        }
+
+        // Loop over 2 different serialization methods
+        for (int type = 0; type < 2; ++type) {
+            StringWriter sw = new StringWriter();
+            if (type == 0) {
+                JsonGenerator gen = new JsonFactory().createGenerator(sw);
+                root.serialize(gen, null);
+                gen.close();
+            } else {
+                mapper.writeValue(sw, root);
+            }
+            
+            String doc = sw.toString();
+            JsonParser jp = new JsonFactory().createParser(new StringReader(doc));
+            
+            assertEquals(JsonToken.START_ARRAY, jp.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.END_ARRAY, jp.nextToken());
+            jp.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
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        final int LENGTH = 13045;
+        byte[] data = new byte[LENGTH];
+        for (int i = 0; i < LENGTH; ++i) {
+            data[i] = (byte) i;
+        }
+        StringWriter sw = new StringWriter();
+        mapper.writeValue(sw, BinaryNode.valueOf(data));
+
+        JsonParser jp = 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();
+    }
+
+    /*
+    ///////////////////////////////////////////////////////////////
+    // Internal methods
+    ///////////////////////////////////////////////////////////////
+     */
+
+    private void verifyFromArray(String input)
+        throws Exception
+    {
+        JsonParser jp = new JsonFactory().createParser(new StringReader(input));
+        
+        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+        
+        assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals(TEXT1, getAndVerifyText(jp));
+        
+        assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(3, jp.getIntValue());
+        
+        assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals(FIELD1, getAndVerifyText(jp));
+        
+        assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals(FIELD2, getAndVerifyText(jp));
+        
+        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+        assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+        
+        assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+        
+        assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+    }
+
+    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));
+        
+        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, jp.nextToken());
+        assertEquals(FIELD2, getAndVerifyText(jp));
+        assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+        assertEquals(JsonToken.END_ARRAY, jp.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.END_OBJECT, jp.nextToken());
+        
+        assertNull(jp.nextToken());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java
new file mode 100644
index 0000000..6833554
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/node/TestTreeTraversingParser.java
@@ -0,0 +1,273 @@
+package com.fasterxml.jackson.databind.node;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.BinaryNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.POJONode;
+import com.fasterxml.jackson.databind.node.TextNode;
+
+public class TestTreeTraversingParser
+    extends BaseMapTest
+{
+    static class Person {
+        public String name;
+        public int magicNumber;
+        public List<String> kids;
+    }
+
+    // Helper class for [JACKSON-370]
+    @JsonIgnoreProperties(ignoreUnknown=true)
+    public static class Jackson370Bean {
+        public Inner inner;
+    }
+
+    public static class Inner {
+        public String value;
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testSimple() throws Exception
+    {
+        // For convenience, parse tree from JSON first
+        final String JSON =
+            "{ \"a\" : 123, \"list\" : [ 12.25, null, true, { }, [ ] ] }";
+        ObjectMapper m = new ObjectMapper();
+        JsonNode tree = m.readTree(JSON);
+        JsonParser jp = tree.traverse();
+
+        assertNull(jp.getCurrentToken());
+        assertNull(jp.getCurrentName());
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertEquals("Expected START_OBJECT", JsonToken.START_OBJECT.asString(), jp.getText());
+
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("a", jp.getCurrentName());
+        assertEquals("a", jp.getText());
+
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals("a", jp.getCurrentName());
+        assertEquals(123, jp.getIntValue());
+        assertEquals("123", jp.getText());
+
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("list", jp.getCurrentName());
+        assertEquals("list", jp.getText());
+
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertEquals("list", jp.getCurrentName());
+        assertEquals(JsonToken.START_ARRAY.asString(), jp.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_NULL, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertEquals(JsonToken.VALUE_NULL.asString(), jp.getText());
+
+        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertTrue(jp.getBooleanValue());
+        assertEquals(JsonToken.VALUE_TRUE.asString(), jp.getText());
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.getCurrentName());
+
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.getCurrentName());
+
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.getCurrentName());
+
+        assertNull(jp.nextToken());
+
+        jp.close();
+        assertTrue(jp.isClosed());
+    }
+
+    public void testArray() throws Exception
+    {
+        // 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();
+
+        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();
+
+        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();
+    }
+    
+    public void testNested() throws Exception
+    {
+        // For convenience, parse tree from JSON first
+        final String JSON =
+            "{\"coordinates\":[[[-3,\n1],[179.859681,51.175092]]]}"
+            ;
+        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());
+
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, jp.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, jp.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+    }
+    
+    /**
+     * Unit test that verifies that we can (re)parse sample document
+     * from JSON specification.
+     */
+    public void testSpecDoc() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        JsonNode tree = m.readTree(SAMPLE_DOC_JSON_SPEC);
+        JsonParser jp = tree.traverse();
+
+        verifyJsonSpecSampleDoc(jp, true);
+    }
+
+    public void testBinaryPojo() throws Exception
+    {
+        byte[] inputBinary = new byte[] { 1, 2, 100 };
+        POJONode n = new POJONode(inputBinary);
+        JsonParser jp = n.traverse();
+
+        assertNull(jp.getCurrentToken());
+        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
+        byte[] data = jp.getBinaryValue();
+        assertNotNull(data);
+        assertArrayEquals(inputBinary, data);
+        Object pojo = jp.getEmbeddedObject();
+        assertSame(data, pojo);
+    }
+
+    public void testBinaryNode() throws Exception
+    {
+        byte[] inputBinary = new byte[] { 0, -5 };
+        BinaryNode n = new BinaryNode(inputBinary);
+        JsonParser jp = n.traverse();
+
+        assertNull(jp.getCurrentToken());
+        // exposed as POJO... not as VALUE_STRING
+        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
+        byte[] data = jp.getBinaryValue();
+        assertNotNull(data);
+        assertArrayEquals(inputBinary, data);
+
+        // but as importantly, can be viewed as base64 encoded thing:
+        assertEquals("APs=", jp.getText());
+
+        assertNull(jp.nextToken());
+    }
+
+    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();
+        assertNotNull(data);
+        assertArrayEquals(new byte[] { 0, -5 }, data);
+
+        assertNull(jp.nextToken());
+        jp.close();
+        assertTrue(jp.isClosed());
+
+        // Also: let's verify we get an exception for garbage...
+        n = new TextNode("?!??");
+        jp = n.traverse();
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        try {
+            jp.getBinaryValue();
+        } catch (JsonParseException e) {
+            verifyException(e, "Illegal character");
+        }
+    }
+
+    /**
+     * Very simple test case to verify that tree-to-POJO
+     * conversion works ok
+     */
+    public void testDataBind() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        JsonNode tree = m.readTree
+            ("{ \"name\" : \"Tatu\", \n"
+             +"\"magicNumber\" : 42,"
+             +"\"kids\" : [ \"Leo\", \"Lila\", \"Leia\" ] \n"
+             +"}");
+        Person tatu = m.treeToValue(tree, Person.class);
+        assertNotNull(tatu);
+        assertEquals(42, tatu.magicNumber);
+        assertEquals("Tatu", tatu.name);
+        assertNotNull(tatu.kids);
+        assertEquals(3, tatu.kids.size());
+        assertEquals("Leo", tatu.kids.get(0));
+        assertEquals("Lila", tatu.kids.get(1));
+        assertEquals("Leia", tatu.kids.get(2));
+    }
+
+    // [JACKSON-370]
+    public void testSkipChildrenWrt370() throws Exception
+    {
+        ObjectMapper o = new ObjectMapper();
+        ObjectNode n = o.createObjectNode();
+        n.putObject("inner").put("value", "test");
+        n.putObject("unknown").putNull("inner");
+        Jackson370Bean obj = o.readValue(n.traverse(), Jackson370Bean.class);
+        assertNotNull(obj.inner);
+        assertEquals("test", obj.inner.value);        
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java
new file mode 100644
index 0000000..12e449c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotationInheritance.java
@@ -0,0 +1,110 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite tests use of Annotations for
+ * bean serialization.
+ */
+public class TestAnnotationInheritance
+    extends com.fasterxml.jackson.test.BaseTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    /// Base class for testing {@link JsonProperty} annotations
+    static class BasePojo
+    {
+        @JsonProperty public int width() { return 3; }
+        @JsonProperty public int length() { return 7; }
+    }
+
+    /**
+     * It should also be possible to specify annotations on interfaces,
+     * to be implemented by classes. This should not only work when interface
+     * is used (which may be the case for de-serialization) but also
+     * when implementing class is used and overrides methods. In latter
+     * case overriding methods should still "inherit" annotations -- this
+     * is not something JVM runtime provides, but Jackson class
+     * instrospector does.
+     */
+    interface PojoInterface
+    {
+        @JsonProperty int width();
+        @JsonProperty int length();
+    }
+
+    /**
+     * Sub-class for testing that inheritance is handled properly
+     * wrt annotations.
+     */
+    static class PojoSubclass extends BasePojo
+    {
+        /**
+         * Should still be recognized as a Getter here.
+         */
+        @Override
+        public int width() { return 9; }
+    }
+
+    static class PojoImpl implements PojoInterface
+    {
+        // Both should be recognized as getters here
+
+        @Override
+        public int width() { return 1; }
+        @Override
+        public int length() { return 2; }
+
+        public int getFoobar() { return 5; }
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+
+    public void testSimpleGetterInheritance() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new PojoSubclass());
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(7), result.get("length"));
+        assertEquals(Integer.valueOf(9), result.get("width"));
+    }
+
+    public void testSimpleGetterInterfaceImpl() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new PojoImpl());
+        // should get 2 from interface, and one more from impl itself
+        assertEquals(3, result.size());
+        assertEquals(Integer.valueOf(5), result.get("foobar"));
+        assertEquals(Integer.valueOf(1), result.get("width"));
+        assertEquals(Integer.valueOf(2), result.get("length"));
+    }
+
+    /*
+    //////////////////////////////////////////////
+    // Helper methods
+    //////////////////////////////////////////////
+     */
+
+    @SuppressWarnings("unchecked")
+	private Map<String,Object> writeAndMap(ObjectMapper m, Object value)
+        throws IOException
+    {
+        StringWriter sw = new StringWriter();
+        m.writeValue(sw, value);
+        return (Map<String,Object>) m.readValue(sw.toString(), Object.class);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java
new file mode 100644
index 0000000..1308caf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnnotations.java
@@ -0,0 +1,260 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * This unit test suite tests use of Annotations for
+ * bean serialization.
+ */
+public class TestAnnotations
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /// Class for testing {@link JsonProperty} annotations with getters
+    final static class SizeClassGetter
+    {
+        @JsonProperty public int size() { return 3; }
+        @JsonProperty("length") public int foobar() { return -17; }
+        // note: need not be public since there's annotation
+        @JsonProperty protected int value() { return 0; }
+
+        // dummy method; not a getter signature
+        protected int getNotReally(int arg) { return 0; }
+    }
+
+    // And additional testing to cover [JACKSON-64]
+    final static class SizeClassGetter2
+    {
+        // Should still be considered property "x"
+        @JsonProperty protected int getX() { return 3; }
+    }
+
+    // and some support for testing [JACKSON-120]
+    final static class SizeClassGetter3
+    {
+        // Should be considered property "y" even tho non-public
+        @JsonSerialize protected int getY() { return 8; }
+    }
+
+
+    /**
+     * Class for testing {@link JsonSerializer} annotation
+     * for class itself.
+     */
+    @JsonSerialize(using=BogusSerializer.class)
+    final static class ClassSerializer {
+    }
+
+    /**
+     * Class for testing an active {@link JsonSerialize#using} annotation
+     * for a method
+     */
+    final static class ClassMethodSerializer {
+        private int _x;
+
+        public ClassMethodSerializer(int x) { _x = x; }
+
+        @JsonSerialize(using=StringSerializer.class)
+        public int getX() { return _x; }
+    }
+
+    /**
+     * Class for testing an inactive (one that will not have any effect)
+     * {@link JsonSerialize} annotation for a method
+     */
+    final static class InactiveClassMethodSerializer {
+        private int _x;
+
+        public InactiveClassMethodSerializer(int x) { _x = x; }
+
+        // Basically, has no effect, hence gets serialized as number
+        @JsonSerialize(using=JsonSerializer.None.class)
+        public int getX() { return _x; }
+    }
+
+    /**
+     * Class for verifying that getter information is inherited
+     * as expected via normal class inheritance
+     */
+    static class BaseBean {
+        public int getX() { return 1; }
+        @JsonProperty("y")
+        private int getY() { return 2; }
+    }
+
+    static class SubClassBean extends BaseBean {
+        public int getZ() { return 3; }
+    }
+
+    // For [JACKSON-666] ("SerializationFeature of the Beast!")
+    @JsonPropertyOrder(alphabetic=true)
+    static class GettersWithoutSetters
+    {
+        public int d = 0;
+        
+        @JsonCreator
+        public GettersWithoutSetters(@JsonProperty("a") int a) { }
+        
+        // included, since there is a constructor property
+        public int getA() { return 3; }
+
+        // not included, as there's nothing matching
+        public int getB() { return 4; }
+
+        // include as there is setter
+        public int getC() { return 5; }
+        public void setC(int v) { }
+
+        // and included, as there is a field
+        public int getD() { return 6; }
+    }
+
+    // [JACKSON-806]: override 'need-setter' with explicit annotation
+    static class GettersWithoutSetters2
+    {
+        @JsonProperty
+        public int getA() { return 123; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Other helper classes
+    /**********************************************************
+     */
+
+    public final static class BogusSerializer extends JsonSerializer<Object>
+    {
+        @Override
+        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeBoolean(true);
+        }
+    }
+
+    private final static class StringSerializer extends JsonSerializer<Object>
+    {
+        @Override
+        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException
+        {
+            jgen.writeString("X"+value+"X");
+        }
+
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleGetter() throws Exception
+    {
+        Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter());
+        assertEquals(3, result.size());
+        assertEquals(Integer.valueOf(3), result.get("size"));
+        assertEquals(Integer.valueOf(-17), result.get("length"));
+        assertEquals(Integer.valueOf(0), result.get("value"));
+    }
+
+    public void testSimpleGetter2() throws Exception
+    {
+        Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter2());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+    }
+
+    // testing [JACKSON-120], implied getter
+    public void testSimpleGetter3() throws Exception
+    {
+        Map<String,Object> result = writeAndMap(MAPPER, new SizeClassGetter3());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(8), result.get("y"));
+    }
+
+    /**
+     * Let's also verify that inherited super-class getters are used
+     * as expected
+     */
+    public void testGetterInheritance() throws Exception
+    {
+        Map<String,Object> result = writeAndMap(MAPPER, new SubClassBean());
+        assertEquals(3, result.size());
+        assertEquals(Integer.valueOf(1), result.get("x"));
+        assertEquals(Integer.valueOf(2), result.get("y"));
+        assertEquals(Integer.valueOf(3), result.get("z"));
+    }
+
+    /**
+     * Unit test to verify that {@link JsonSerialize#using} annotation works
+     * when applied to a class
+     */
+    public void testClassSerializer() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new ClassSerializer());
+        assertEquals("true", sw.toString());
+    }
+
+    /**
+     * Unit test to verify that @JsonSerializer annotation works
+     * when applied to a Method
+     */
+    public void testActiveMethodSerializer() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new ClassMethodSerializer(13));
+        /* Here we will get wrapped as an object, since we have
+         * full object, just override a single property
+         */
+        assertEquals("{\"x\":\"X13X\"}", sw.toString());
+    }
+
+    public void testInactiveMethodSerializer() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new InactiveClassMethodSerializer(8));
+        /* Here we will get wrapped as an object, since we have
+         * full object, just override a single property
+         */
+        assertEquals("{\"x\":8}", json);
+    }
+
+    public void testGettersWithoutSetters() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        GettersWithoutSetters bean = new GettersWithoutSetters(123);
+        assertFalse(m.isEnabled(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS));
+    
+        // by default, all 4 found:
+        assertEquals("{\"a\":3,\"b\":4,\"c\":5,\"d\":6}", m.writeValueAsString(bean));
+
+        // but 3 if we require mutator:
+        m = new ObjectMapper();
+        m.enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS);
+        assertEquals("{\"a\":3,\"c\":5,\"d\":6}", m.writeValueAsString(bean));
+    }
+
+    public void testGettersWithoutSettersOverride() throws Exception
+    {
+        GettersWithoutSetters2 bean = new GettersWithoutSetters2();
+        ObjectMapper m = new ObjectMapper();
+        m.enable(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS);
+        assertEquals("{\"a\":123}", m.writeValueAsString(bean));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java
new file mode 100644
index 0000000..bcf4e8a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAnyGetter.java
@@ -0,0 +1,76 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class TestAnyGetter
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class Bean
+    {
+        final static Map<String,Boolean> extra = new HashMap<String,Boolean>();
+        static {
+            extra.put("a", Boolean.TRUE);
+        }
+        
+        public int getX() { return 3; }
+
+        @JsonAnyGetter
+        public Map<String,Boolean> getExtra() { return extra; }
+    }
+
+    static class AnyOnlyBean
+    {
+        @JsonAnyGetter
+        public Map<String,Integer> any() {
+            HashMap<String,Integer> map = new HashMap<String,Integer>();
+            map.put("a", 3);
+            return map;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test cases
+    /**********************************************************
+     */
+
+    public void testSimpleJsonValue() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String json = serializeAsString(m, new Bean());
+        Map<?,?> map = m.readValue(json, Map.class);
+        assertEquals(2, map.size());
+        assertEquals(Integer.valueOf(3), map.get("x"));
+        assertEquals(Boolean.TRUE, map.get("a"));
+    }
+
+    // [JACKSON-392]
+    public void testAnyOnly() throws Exception
+    {
+        ObjectMapper m;
+
+        // First, with normal fail settings:
+        m = new ObjectMapper();
+        m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true);
+        String json = serializeAsString(m, new AnyOnlyBean());
+        assertEquals("{\"a\":3}", json);
+
+        // then without fail
+        m = new ObjectMapper();
+        m.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        json = serializeAsString(m, new AnyOnlyBean());
+        assertEquals("{\"a\":3}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java
new file mode 100644
index 0000000..9e15936
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestArraySerialization.java
@@ -0,0 +1,98 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.test.BaseTest;
+
+public class TestArraySerialization
+    extends BaseTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testLongStringArray() throws Exception
+    {
+        final int SIZE = 40000;
+
+        StringBuilder sb = new StringBuilder(SIZE*2);
+        for (int i = 0; i < SIZE; ++i) {
+            sb.append((char) i);
+        }
+        String str = sb.toString();
+        byte[] data = MAPPER.writeValueAsBytes(new String[] { "abc", str, null, str });
+        JsonParser jp = MAPPER.getFactory().createParser(data);
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals("abc", jp.getText());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        String actual = jp.getText();
+        assertEquals(str.length(), actual.length());
+        assertEquals(str, actual);
+        assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals(str, jp.getText());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+    }
+    
+    public void testIntArray() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new int[] { 1, 2, 3, -7 });
+        assertEquals("[1,2,3,-7]", sw.toString().trim());
+    }
+
+    public void testBigIntArray() throws Exception
+    {
+        final int SIZE = 99999;
+        int[] ints = new int[SIZE];
+        for (int i = 0; i < ints.length; ++i) {
+            ints[i] = i;
+        }
+
+        // Let's try couple of times, to ensure that state is handled
+        // correctly by ObjectMapper (wrt buffer recycling used
+        // with 'writeAsBytes()')
+        JsonFactory f = MAPPER.getFactory();
+        for (int round = 0; round < 3; ++round) {
+            byte[] data = MAPPER.writeValueAsBytes(ints);
+            JsonParser jp = f.createParser(data);
+            assertToken(JsonToken.START_ARRAY, jp.nextToken());
+            for (int i = 0; i < SIZE; ++i) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+                assertEquals(i, jp.getIntValue());
+            }
+            assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        }
+    }
+    
+    public void testLongArray() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new long[] { Long.MIN_VALUE, 0, Long.MAX_VALUE });
+        assertEquals("["+Long.MIN_VALUE+",0,"+Long.MAX_VALUE+"]", sw.toString().trim());
+    }
+
+    public void testStringArray() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new String[] { "a", "\"foo\"", null });
+        assertEquals("[\"a\",\"\\\"foo\\\"\",null]", sw.toString().trim());
+    }
+
+    public void testDoubleArray() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new double[] { 1.01, 2.0, -7, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY });
+        assertEquals("[1.01,2.0,-7.0,\"NaN\",\"-Infinity\",\"Infinity\"]", sw.toString().trim());
+    }
+
+    public void testFloatArray() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        MAPPER.writeValue(sw, new float[] { 1.01f, 2.0f, -7f, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY });
+        assertEquals("[1.01,2.0,-7.0,\"NaN\",\"-Infinity\",\"Infinity\"]", sw.toString().trim());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java
new file mode 100644
index 0000000..76edeba
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestAutoDetect.java
@@ -0,0 +1,124 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
+
+/**
+ * Unit tests for checking extended auto-detect configuration,
+ * in context of serialization
+ */
+public class TestAutoDetect
+    extends BaseMapTest
+{
+    static class FieldBean
+    {
+        public String p1 = "public";
+        protected String p2 = "protected";
+        @SuppressWarnings("unused")
+        private String p3 = "private";
+    }
+
+    @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC)
+    static class ProtFieldBean extends FieldBean { }
+
+    static class MethodBean
+    {
+        public String getA() { return "a"; }
+        protected String getB() { return "b"; }
+        @SuppressWarnings("unused")
+        private String getC() { return "c"; }
+    }
+
+    @JsonAutoDetect(getterVisibility=JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC)
+    static class ProtMethodBean extends MethodBean { }
+
+    /*
+    /*********************************************************
+    /* Test methods
+    /*********************************************************
+     */
+
+    public void testDefaults() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        // by default, only public fields and getters are detected
+        assertEquals("{\"p1\":\"public\"}",
+                     m.writeValueAsString(new FieldBean()));
+        assertEquals("{\"a\":\"a\"}",
+                     m.writeValueAsString(new MethodBean()));
+    }
+
+    public void testProtectedViaAnnotations() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+
+        Map<String,Object> result = writeAndMap(m, new ProtFieldBean());
+        assertEquals(2, result.size());
+        assertEquals("public", result.get("p1"));
+        assertEquals("protected", result.get("p2"));
+        assertNull(result.get("p3"));
+
+        result = writeAndMap(m, new ProtMethodBean());
+        assertEquals(2, result.size());
+        assertEquals("a", result.get("a"));
+        assertEquals("b", result.get("b"));
+        assertNull(result.get("c"));
+    }
+
+    public void testPrivateUsingGlobals() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        VisibilityChecker<?> vc = m.getVisibilityChecker();
+        vc = vc.withFieldVisibility(JsonAutoDetect.Visibility.ANY);
+        m.setVisibilityChecker(vc);
+        
+        Map<String,Object> result = writeAndMap(m, new FieldBean());
+        assertEquals(3, result.size());
+        assertEquals("public", result.get("p1"));
+        assertEquals("protected", result.get("p2"));
+        assertEquals("private", result.get("p3"));
+
+        m = new ObjectMapper();
+        vc = m.getVisibilityChecker();
+        vc = vc.withGetterVisibility(JsonAutoDetect.Visibility.ANY);
+        m.setVisibilityChecker(vc);
+        result = writeAndMap(m, new MethodBean());
+        assertEquals(3, result.size());
+        assertEquals("a", result.get("a"));
+        assertEquals("b", result.get("b"));
+        assertEquals("c", result.get("c"));
+    }
+
+    // [JACKSON-621]
+    public void testBasicSetup() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        VisibilityChecker<?> vc = m.getVisibilityChecker();
+        vc = vc.with(JsonAutoDetect.Visibility.ANY);
+        m.setVisibilityChecker(vc);
+
+        Map<String,Object> result = writeAndMap(m, new FieldBean());
+        assertEquals(3, result.size());
+        assertEquals("public", result.get("p1"));
+        assertEquals("protected", result.get("p2"));
+        assertEquals("private", result.get("p3"));
+    }
+
+    // [JACKSON-595]
+    public void testMapperShortcutMethods() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+
+        Map<String,Object> result = writeAndMap(m, new FieldBean());
+        assertEquals(3, result.size());
+        assertEquals("public", result.get("p1"));
+        assertEquals("protected", result.get("p2"));
+        assertEquals("private", result.get("p3"));
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
new file mode 100644
index 0000000..d14cb46
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestBeanSerializer.java
@@ -0,0 +1,367 @@
+package com.fasterxml.jackson.databind.ser;
+
+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.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.BeanSerializer;
+import com.fasterxml.jackson.databind.ser.BeanSerializerBuilder;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapType;
+
+/**
+ * Unit tests for verifying that it is possible to configure
+ * construction of {@link BeanSerializer} instances.
+ */
+ at SuppressWarnings("serial")
+public class TestBeanSerializer extends BaseMapTest
+{
+    /*
+    /********************************************************
+    /* Helper types
+    /********************************************************
+     */
+
+    static class ModuleImpl extends SimpleModule
+    {
+        protected BeanSerializerModifier modifier;
+        
+        public ModuleImpl(BeanSerializerModifier modifier)
+        {
+            super("test", Version.unknownVersion());
+            this.modifier = modifier;
+        }
+        
+        @Override
+        public void setupModule(SetupContext context)
+        {
+            super.setupModule(context);
+            if (modifier != null) {
+                context.addBeanSerializerModifier(modifier);
+            }
+        }
+    }
+
+    @JsonPropertyOrder({"b", "a"})
+    static class Bean {
+        public String b = "b";
+        public String a = "a";
+    }
+
+    static class RemovingModifier extends BeanSerializerModifier
+    {
+        private final String _removedProperty;
+        
+        public RemovingModifier(String remove) { _removedProperty = remove; }
+        
+        @Override
+        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
+                List<BeanPropertyWriter> beanProperties)
+        {
+            Iterator<BeanPropertyWriter> it = beanProperties.iterator();
+            while (it.hasNext()) {
+                BeanPropertyWriter bpw = it.next();
+                if (bpw.getName().equals(_removedProperty)) {
+                    it.remove();
+                }
+            }
+            return beanProperties;
+        }
+    }
+    
+    static class ReorderingModifier extends BeanSerializerModifier
+    {
+        @Override
+        public List<BeanPropertyWriter> orderProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)
+        {
+            TreeMap<String,BeanPropertyWriter> props = new TreeMap<String,BeanPropertyWriter>();
+            for (BeanPropertyWriter bpw : beanProperties) {
+                props.put(bpw.getName(), bpw);
+            }
+            return new ArrayList<BeanPropertyWriter>(props.values());
+        }
+    }
+
+    static class ReplacingModifier extends BeanSerializerModifier
+    {
+        private final JsonSerializer<?> _serializer;
+        
+        public ReplacingModifier(JsonSerializer<?> s) { _serializer = s; }
+        
+        @Override
+        public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
+                JsonSerializer<?> serializer) {
+            return _serializer;
+        }
+    }
+
+    static class BuilderModifier extends BeanSerializerModifier
+    {
+        private final JsonSerializer<?> _serializer;
+        
+        public BuilderModifier(JsonSerializer<?> ser) {
+            _serializer = ser;
+        }
+        
+        @Override
+        public BeanSerializerBuilder updateBuilder(SerializationConfig config,
+                BeanDescription beanDesc, BeanSerializerBuilder builder) {
+            return new BogusSerializerBuilder(builder, _serializer);
+        }
+    }
+
+    static class BogusSerializerBuilder extends BeanSerializerBuilder
+    {
+        private final JsonSerializer<?> _serializer;
+        
+        public BogusSerializerBuilder(BeanSerializerBuilder src,
+                JsonSerializer<?> ser) {
+            super(src);
+            _serializer = ser;
+        }
+
+        @Override
+        public JsonSerializer<?> build() {
+            return _serializer;
+        }
+    }
+    
+    static class BogusBeanSerializer extends JsonSerializer<Object>
+    {
+        private final int _value;
+        
+        public BogusBeanSerializer(int v) { _value = v; }
+        
+        @Override
+        public void serialize(Object value, JsonGenerator jgen,
+                SerializerProvider provider) throws IOException {
+            jgen.writeNumber(_value);
+        }
+    }
+
+    // for [JACKSON-670]
+    
+    static class EmptyBean {
+        @JsonIgnore
+        public String name = "foo";
+    }
+
+    static class EmptyBeanModifier extends BeanSerializerModifier
+    {
+        @Override
+        public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
+                BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties)
+        {
+            JavaType strType = config.constructType(String.class);
+            // we need a valid BeanPropertyDefinition; this will do (just need name to match)
+            POJOPropertyBuilder prop = new POJOPropertyBuilder("bogus", null, true);
+            try {
+                AnnotatedField f = new AnnotatedField(EmptyBean.class.getDeclaredField("name"), null);
+                beanProperties.add(new BeanPropertyWriter(prop, f, null,
+                        strType,
+                        null, null, strType,
+                        false, null));
+            } catch (NoSuchFieldException e) {
+                throw new IllegalStateException(e.getMessage());
+            }
+            return beanProperties;
+        }
+    }
+
+    // For [JACKSON-694]: error message for conflicting getters sub-optimal
+    static class BeanWithConflict
+    {
+        public int getX() { return 3; }
+        public boolean isX() { return false; }
+    }
+
+    // [Issue#120], arrays, collections, maps
+    
+    static class ArraySerializerModifier extends BeanSerializerModifier {
+        @Override
+        public JsonSerializer<?> modifyArraySerializer(SerializationConfig config,
+                ArrayType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+            return new StdSerializer<Object>(Object.class) {
+                @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+                    jgen.writeNumber(123);
+                }
+            };
+        }
+    }
+
+    static class CollectionSerializerModifier extends BeanSerializerModifier {
+        @Override
+        public JsonSerializer<?> modifyCollectionSerializer(SerializationConfig config,
+                CollectionType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+            return new StdSerializer<Object>(Object.class) {
+                @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+                    jgen.writeNumber(123);
+                }
+            };
+        }
+    }
+
+    static class MapSerializerModifier extends BeanSerializerModifier {
+        @Override
+        public JsonSerializer<?> modifyMapSerializer(SerializationConfig config,
+                MapType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+            return new StdSerializer<Object>(Object.class) {
+                @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+                    jgen.writeNumber(123);
+                }
+            };
+        }
+    }
+
+    static class EnumSerializerModifier extends BeanSerializerModifier {
+        @Override
+        public JsonSerializer<?> modifyEnumSerializer(SerializationConfig config,
+                JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+            return new StdSerializer<Object>(Object.class) {
+                @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+                    jgen.writeNumber(123);
+                }
+            };
+        }
+    }
+
+    static class KeySerializerModifier extends BeanSerializerModifier {
+        @Override
+        public JsonSerializer<?> modifyKeySerializer(SerializationConfig config,
+                JavaType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
+            return new StdSerializer<Object>(Object.class) {
+                @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+                    jgen.writeFieldName("foo");
+                }
+            };
+        }
+    }
+    
+    enum EnumABC { A, B, C };
+    
+    /*
+    /********************************************************
+    /* Unit tests: success
+    /********************************************************
+     */
+
+    public void testPropertyRemoval() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new RemovingModifier("a")));
+        Bean bean = new Bean();
+        assertEquals("{\"b\":\"b\"}", mapper.writeValueAsString(bean));
+    }
+
+    public void testPropertyReorder() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new ReorderingModifier()));
+        Bean bean = new Bean();
+        assertEquals("{\"a\":\"a\",\"b\":\"b\"}", mapper.writeValueAsString(bean));
+    }
+
+    public void testBuilderReplacement() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new BuilderModifier(new BogusBeanSerializer(17))));
+        Bean bean = new Bean();
+        assertEquals("17", mapper.writeValueAsString(bean));
+    }    
+    public void testSerializerReplacement() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new ModuleImpl(new ReplacingModifier(new BogusBeanSerializer(123))));
+        Bean bean = new Bean();
+        assertEquals("123", mapper.writeValueAsString(bean));
+    }
+
+    // for [JACKSON-670]
+    public void testEmptyBean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test", Version.unknownVersion()) {
+            @Override
+            public void setupModule(SetupContext context)
+            {
+                super.setupModule(context);
+                context.addBeanSerializerModifier(new EmptyBeanModifier());
+            }
+        });
+        String json = mapper.writeValueAsString(new EmptyBean());
+        assertEquals("{\"bogus\":\"foo\"}", json);
+    }
+
+    // [Issue#121]
+
+    public void testModifyArraySerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setSerializerModifier(new ArraySerializerModifier()));
+        assertEquals("123", mapper.writeValueAsString(new Integer[] { 1, 2 }));
+    }
+
+    public void testModifyCollectionSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setSerializerModifier(new CollectionSerializerModifier()));
+        assertEquals("123", mapper.writeValueAsString(new ArrayList<Integer>()));
+    }
+
+    public void testModifyMapSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setSerializerModifier(new MapSerializerModifier()));
+        assertEquals("123", mapper.writeValueAsString(new HashMap<String,String>()));
+    }
+
+    public void testModifyEnumSerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setSerializerModifier(new EnumSerializerModifier()));
+        assertEquals("123", mapper.writeValueAsString(EnumABC.C));
+    }
+
+    public void testModifyKeySerializer() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new SimpleModule("test")
+            .setSerializerModifier(new KeySerializerModifier()));
+        Map<String,Integer> map = new HashMap<String,Integer>();
+        map.put("x", 3);
+        assertEquals("{\"foo\":3}", mapper.writeValueAsString(map));
+    }
+    
+    /*
+    /********************************************************
+    /* Unit tests: failure handling
+    /********************************************************
+     */
+    
+    // for [JACKSON-694]
+    public void testFailWithDupProps() throws Exception
+    {
+        BeanWithConflict bean = new BeanWithConflict();
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            String json = mapper.writeValueAsString(bean);
+            fail("Should have failed due to conflicting accessor definitions; got JSON = "+json);
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Conflicting getter definitions");
+        }
+    }        
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java
new file mode 100644
index 0000000..6520cab
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCollectionSerialization.java
@@ -0,0 +1,357 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class TestCollectionSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    enum Key { A, B, C };
+
+    // Field-based simple bean with a single property, "values"
+    final static class CollectionBean
+    {
+        @JsonProperty // not required
+            public Collection<Object> values;
+
+        public CollectionBean(Collection<Object> c) { values = c; }
+    }
+
+    static class EnumMapBean
+    {
+        EnumMap<Key,String> _map;
+
+        public EnumMapBean(EnumMap<Key,String> m)
+        {
+            _map = m;
+        }
+
+        public EnumMap<Key,String> getMap() { return _map; }
+    }
+
+    final static class IterableWrapper
+        implements Iterable<Integer>
+    {
+        List<Integer> _ints = new ArrayList<Integer>();
+
+        public IterableWrapper(int[] values) {
+            for (int i : values) {
+                _ints.add(Integer.valueOf(i));
+            }
+        }
+
+        @Override
+        public Iterator<Integer> iterator() {
+            return _ints.iterator();
+        }
+    }
+
+    /**
+     * Class needed for testing [JACKSON-220]
+     */
+    @SuppressWarnings("serial")
+    @JsonSerialize(using=ListSerializer.class)    
+    static class PseudoList extends ArrayList<String>
+    {
+        public PseudoList(String... values) {
+            super(Arrays.asList(values));
+        }
+    }
+
+    static class ListSerializer extends JsonSerializer<List<String>>
+    {
+        @Override
+        public void serialize(List<String> value,
+                              JsonGenerator jgen,
+                              SerializerProvider provider)
+            throws IOException
+        {
+            // just use standard List.toString(), output as JSON String
+            jgen.writeString(value.toString());
+        }
+    }
+
+    // for [JACKSON-254], suppression of empty collections
+    static class EmptyListBean {
+        public List<String> empty = new ArrayList<String>();
+    }
+
+    static class EmptyArrayBean {
+        public String[] empty = new String[0];
+    }
+
+    // [JACKSON-689]
+    static class BeanWithIterable {
+        private final ArrayList<String> values = new ArrayList<String>();
+        {
+            values.add("value");
+        }
+
+        public Iterable<String> getValues() { return values; }
+    }
+
+    static class IntIterable implements Iterable<Integer>
+    {
+        @Override
+        public Iterator<Integer> iterator() {
+            return new IntIterator(1, 3);
+        }
+    }
+
+    static class IntIterator implements Iterator<Integer> {
+        int i;
+        final int last;
+
+        public IntIterator(int first, int last) {
+            i = first;
+            this.last = last;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return i <= last;
+        }
+
+        @Override
+        public Integer next() {
+            return i++;
+        }
+
+        @Override
+        public void remove() { }
+
+        public int getX() { return 13; }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testCollections()
+        throws IOException
+    {
+        // Let's try different collections, arrays etc
+        final int entryLen = 98;
+
+        for (int type = 0; type < 4; ++type) {
+            Object value;
+
+            if (type == 0) { // first, array
+                int[] ints = new int[entryLen];
+                for (int i = 0; i < entryLen; ++i) {
+                    ints[i] = Integer.valueOf(i);
+                }
+                value = ints;
+            } else {
+                Collection<Integer> c;
+
+                switch (type) {
+                case 1:
+                    c = new LinkedList<Integer>();
+                    break;
+                case 2:
+                    c = new TreeSet<Integer>(); // has to be ordered
+                    break;
+                default:
+                    c = new ArrayList<Integer>();
+                    break;
+                }
+                for (int i = 0; i < entryLen; ++i) {
+                    c.add(Integer.valueOf(i));
+                }
+                value = c;
+            }
+            String json = MAPPER.writeValueAsString(value);
+            
+            // and then need to verify:
+            JsonParser jp = new JsonFactory().createParser(json);
+            assertToken(JsonToken.START_ARRAY, jp.nextToken());
+            for (int i = 0; i < entryLen; ++i) {
+                assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+                assertEquals(i, jp.getIntValue());
+            }
+            assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        }
+    }
+
+    public void testBigCollection()
+        throws IOException
+    {
+        final int COUNT = 9999;
+        ArrayList<Integer> value = new ArrayList<Integer>();
+        for (int i = 0; i <= COUNT; ++i) {
+            value.add(i);
+        }
+        // Let's test using 3 main variants...
+        for (int mode = 0; mode < 3; ++mode) {
+            JsonParser jp = null;
+            switch (mode) {
+            case 0:
+                {
+                    byte[] data = MAPPER.writeValueAsBytes(value);
+                    jp = new JsonFactory().createParser(data);
+                }
+                break;
+            case 1:
+                {
+                    StringWriter sw = new StringWriter(value.size());
+                    MAPPER.writeValue(sw, value);
+                    jp = createParserUsingReader(sw.toString());
+                }
+                break;
+            case 2:
+                {
+                    String str = MAPPER.writeValueAsString(value);
+                    jp = createParserUsingReader(str);
+                }
+                break;
+            }
+
+            // and verify
+            assertToken(JsonToken.START_ARRAY, jp.nextToken());
+            for (int i = 0; i <= COUNT; ++i) {
+                assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+                assertEquals(i, jp.getIntValue());
+            }
+            assertToken(JsonToken.END_ARRAY, jp.nextToken());
+            jp.close();
+        }
+    }
+
+    public void testEnumMap()
+        throws IOException
+    {
+        EnumMap<Key,String> map = new EnumMap<Key,String>(Key.class);
+        map.put(Key.B, "xyz");
+        map.put(Key.C, "abc");
+        // assuming EnumMap uses enum entry order, which I think is true...
+        String json = MAPPER.writeValueAsString(map);
+        assertEquals("{\"B\":\"xyz\",\"C\":\"abc\"}",json.trim());
+    }
+
+    public void testIterator()
+        throws IOException
+    {
+        StringWriter sw = new StringWriter();
+        ArrayList<Integer> l = new ArrayList<Integer>();
+        l.add(1);
+        l.add(-9);
+        l.add(0);
+        MAPPER.writeValue(sw, l.iterator());
+        assertEquals("[1,-9,0]", sw.toString().trim());
+    }
+
+    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());
+    }
+
+    // Test that checks that empty collections are properly serialized
+    // when they are Bean properties
+    @SuppressWarnings("unchecked")
+    public void testEmptyBeanCollection()
+        throws IOException
+    {
+        Collection<Object> x = new ArrayList<Object>();
+        x.add("foobar");
+        CollectionBean cb = new CollectionBean(x);
+        Map<String,Object> result = writeAndMap(MAPPER, cb);
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("values"));
+        Collection<Object> x2 = (Collection<Object>) result.get("values");
+        assertNotNull(x2);
+        assertEquals(x, x2);
+    }
+
+    public void testNullBeanCollection()
+        throws IOException
+    {
+        CollectionBean cb = new CollectionBean(null);
+        Map<String,Object> result = writeAndMap(MAPPER, cb);
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("values"));
+        assertNull(result.get("values"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testEmptyBeanEnumMap()
+        throws IOException
+    {
+        EnumMap<Key,String> map = new EnumMap<Key,String>(Key.class);
+        EnumMapBean b = new EnumMapBean(map);
+        Map<String,Object> result = writeAndMap(MAPPER, b);
+
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("map"));
+        // we deserialized to untyped, not back to bean, so:
+        Map<Object,Object> map2 = (Map<Object,Object>) result.get("map");
+        assertNotNull(map2);
+        assertEquals(0, map2.size());
+    }
+
+    // Should also be able to serialize null EnumMaps as expected
+    public void testNullBeanEnumMap()
+        throws IOException
+    {
+        EnumMapBean b = new EnumMapBean(null);
+        Map<String,Object> result = writeAndMap(MAPPER, b);
+
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("map"));
+        assertNull(result.get("map"));
+    }
+
+    // Test [JACKSON-220]
+    public void testListSerializer() throws IOException
+    {
+        assertEquals("\"[ab, cd, ef]\"",
+                MAPPER.writeValueAsString(new PseudoList("ab", "cd", "ef")));
+    }
+
+    // [JACKSON-254]
+    public void testEmptyListOrArray() throws IOException
+    {
+        // by default, empty lists serialized normally
+        EmptyListBean list = new EmptyListBean();
+        EmptyArrayBean array = new EmptyArrayBean();
+        assertTrue(MAPPER.isEnabled(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS));
+        assertEquals("{\"empty\":[]}", MAPPER.writeValueAsString(list));
+        assertEquals("{\"empty\":[]}", MAPPER.writeValueAsString(array));
+
+        // note: value of setting may be cached when constructing serializer, need a new instance
+        ObjectMapper m = new ObjectMapper();
+        m.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
+        assertEquals("{}", m.writeValueAsString(list));
+        assertEquals("{}", m.writeValueAsString(array));
+    }
+    
+    // [JACKSON-689], [JACKSON-876]
+    public void testWithIterable() throws IOException
+    {
+        // 689:
+        assertEquals("{\"values\":[\"value\"]}",
+                MAPPER.writeValueAsString(new BeanWithIterable()));
+        // 876:
+        assertEquals("[1,2,3]",
+                MAPPER.writeValueAsString(new IntIterable()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java
new file mode 100644
index 0000000..33951ae
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestConfig.java
@@ -0,0 +1,197 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.StringWriter;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.introspect.ClassIntrospector;
+
+/**
+ * Unit tests for checking handling of SerializationConfig.
+ */
+public class TestConfig
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT,
+                   typing=JsonSerialize.Typing.STATIC)
+    final static class Config { }
+
+    final static class ConfigNone { }
+
+    static class AnnoBean {
+        public int getX() { return 1; }
+        @JsonProperty("y")
+        private int getY() { return 2; }
+    }
+
+    static class Indentable {
+        public int a = 3;
+    }
+
+    public static class SimpleBean {
+        public int x = 1;
+    }
+    
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+
+    final static ObjectMapper MAPPER = new ObjectMapper();
+
+    /* 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 (SerializationFeature f : SerializationFeature.values()) {
+            max = Math.max(max, f.ordinal());
+        }
+        if (max >= 31) { // 31 is actually ok; 32 not
+            fail("Max number of SerializationFeature enums reached: "+max);
+        }
+    }
+    
+    public void testDefaults()
+    {
+        SerializationConfig cfg = MAPPER.getSerializationConfig();
+
+        // First, defaults:
+        assertTrue(cfg.isEnabled(MapperFeature.USE_ANNOTATIONS));
+        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));
+        assertTrue(cfg.isEnabled(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS));
+
+        assertTrue(cfg.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
+
+        assertFalse(cfg.isEnabled(SerializationFeature.INDENT_OUTPUT));
+        assertFalse(cfg.isEnabled(MapperFeature.USE_STATIC_TYPING));
+
+        // since 1.3:
+        assertTrue(cfg.isEnabled(MapperFeature.AUTO_DETECT_IS_GETTERS));
+        // since 1.4
+        
+        assertTrue(cfg.isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+        // since 1.5
+        assertTrue(cfg.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION));
+
+    }
+
+    public void testOverrideIntrospectors()
+    {
+        SerializationConfig cfg = MAPPER.getSerializationConfig();
+        // 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()
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.setDateFormat(null); // just to execute the code path
+        assertNotNull(m.getSerializationConfig().toString()); // ditto
+    }
+
+    public void testIndentation() throws Exception
+    {
+        Map<String,Integer> map = new HashMap<String,Integer>();
+        map.put("a", Integer.valueOf(2));
+        String result = MAPPER.writer().with(SerializationFeature.INDENT_OUTPUT)
+                .writeValueAsString(map);
+        // 02-Jun-2009, tatu: not really a clean way but...
+        String lf = getLF();
+        assertEquals("{"+lf+"  \"a\" : 2"+lf+"}", result);
+    }
+
+    public void testAnnotationsDisabled() throws Exception
+    {
+        // first: verify that annotation introspection is enabled by default
+        assertTrue(MAPPER.isEnabled(MapperFeature.USE_ANNOTATIONS));
+        Map<String,Object> result = writeAndMap(MAPPER, new AnnoBean());
+        assertEquals(2, result.size());
+
+        ObjectMapper m2 = new ObjectMapper();
+        m2.configure(MapperFeature.USE_ANNOTATIONS, false);
+        result = writeAndMap(m2, new AnnoBean());
+        assertEquals(1, result.size());
+    }
+
+    /**
+     * Test for verifying working of [JACKSON-191]
+     */
+    public void testProviderConfig() throws Exception   
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        DefaultSerializerProvider prov = (DefaultSerializerProvider) mapper.getSerializerProvider();
+        assertEquals(0, prov.cachedSerializersCount());
+        // and then should get one constructed for:
+        Map<String,Object> result = this.writeAndMap(mapper, new AnnoBean());
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(1), result.get("x"));
+        assertEquals(Integer.valueOf(2), result.get("y"));
+
+        /* Note: it is 2 because we'll also get serializer for basic 'int', not
+         * just AnnoBean
+         */
+        /* 12-Jan-2010, tatus: Actually, probably more, if and when we typing
+         *   aspects are considered (depending on what is cached)
+         */
+        int count = prov.cachedSerializersCount();
+        if (count < 2) {
+            fail("Should have at least 2 cached serializers, got "+count);
+        }
+        prov.flushCachedSerializers();
+        assertEquals(0, prov.cachedSerializersCount());
+    }
+
+    // Test for [Issue#12]
+    public void testIndentWithPassedGenerator() throws Exception
+    {
+        Indentable input = new Indentable();
+        assertEquals("{\"a\":3}", MAPPER.writeValueAsString(input));
+        String LF = getLF();
+        String INDENTED = "{"+LF+"  \"a\" : 3"+LF+"}";
+        final ObjectWriter indentWriter = MAPPER.writer().with(SerializationFeature.INDENT_OUTPUT);
+        assertEquals(INDENTED, indentWriter.writeValueAsString(input));
+
+        // [Issue#12]
+        StringWriter sw = new StringWriter();
+        JsonGenerator jgen = MAPPER.getFactory().createGenerator(sw);
+        indentWriter.writeValue(jgen, input);
+        jgen.close();
+        assertEquals(INDENTED, sw.toString());
+
+        // and also with ObjectMapper itself
+        sw = new StringWriter();
+        ObjectMapper m2 = new ObjectMapper();
+        m2.enable(SerializationFeature.INDENT_OUTPUT);
+        jgen = m2.getFactory().createGenerator(sw);
+        m2.writeValue(jgen, input);
+        jgen.close();
+        assertEquals(INDENTED, sw.toString());
+    }
+
+    public void testNoAccessOverrides() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.disable(MapperFeature.CAN_OVERRIDE_ACCESS_MODIFIERS);
+        assertEquals("{\"x\":1}", m.writeValueAsString(new SimpleBean()));
+    }
+    
+    private final static String getLF() {
+        return System.getProperty("line.separator");
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
new file mode 100644
index 0000000..285126f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java
@@ -0,0 +1,108 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.*;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.CollectionSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
+import com.fasterxml.jackson.databind.util.StdConverter;
+
+/**
+ * Test for verifying [JACKSON-238]
+ *
+ * @author Pablo Lalloni <plalloni at gmail.com>
+ * @author tatu
+ */
+public class TestCustomSerializers
+    extends com.fasterxml.jackson.databind.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");
+        }
+    }
+    
+    @JsonSerialize(using = ElementSerializer.class)
+    public static class ElementMixin {}
+
+    public static class Immutable {
+        protected int x() { return 3; }
+        protected int y() { return 7; }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+    */
+    
+    public void testCustomization() throws Exception
+    {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.addMixInAnnotations(Element.class, ElementMixin.class);
+        Element element = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().createElement("el");
+        StringWriter sw = new StringWriter();
+        objectMapper.writeValue(sw, element);
+        assertEquals(sw.toString(), "\"element\"");
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testCustomLists() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        JsonSerializer<?> ser = new CollectionSerializer(null, false, null, null, null);
+        final JsonSerializer<Object> collectionSerializer = (JsonSerializer<Object>) ser;
+
+        module.addSerializer(Collection.class, new JsonSerializer<Collection>() {
+            @Override
+            public void serialize(Collection value, JsonGenerator jgen, SerializerProvider provider)
+                    throws IOException, JsonProcessingException {
+                if (value.size() != 0) {
+                    collectionSerializer.serialize(value, jgen, provider);
+                } else {
+                    jgen.writeNull();
+                }
+            }
+        });
+        mapper.registerModule(module);
+        assertEquals("null", mapper.writeValueAsString(new ArrayList<Object>()));
+    }
+
+    // [Issue#87]: delegating serializer
+    public void testDelegating() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test", Version.unknownVersion());
+        module.addSerializer(new StdDelegatingSerializer(Immutable.class,
+                new StdConverter<Immutable, Map<String,Integer>>() {
+                    @Override
+                    public Map<String, Integer> convert(Immutable value)
+                    {
+                        HashMap<String,Integer> map = new LinkedHashMap<String,Integer>();
+                        map.put("x", value.x());
+                        map.put("y", value.y());
+                        return map;
+                    }
+        }));
+        mapper.registerModule(module);
+        assertEquals("{\"x\":3,\"y\":7}", mapper.writeValueAsString(new Immutable()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestCyclicTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestCyclicTypes.java
new file mode 100644
index 0000000..616fbaf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestCyclicTypes.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Simple unit tests to verify that it is possible to handle
+ * potentially cyclic structures, as long as object graph itself
+ * is not cyclic. This is the case for directed hierarchies like
+ * trees and DAGs.
+ */
+public class TestCyclicTypes
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper bean classes
+    /**********************************************************
+     */
+
+    static class Bean
+    {
+        Bean _next;
+        final String _name;
+
+        public Bean(Bean next, String name) {
+            _next = next;
+            _name = name;
+        }
+
+        public Bean getNext() { return _next; }
+        public String getName() { return _name; }
+
+        public void assignNext(Bean n) { _next = n; }
+    }
+
+    /*
+    /**********************************************************
+    /* Types
+    /**********************************************************
+     */
+
+    public void testLinked() throws Exception
+    {
+        Bean last = new Bean(null, "last");
+        Bean first = new Bean(last, "first");
+        Map<String,Object> map = writeAndMap(new ObjectMapper(), first);
+
+        assertEquals(2, map.size());
+        assertEquals("first", map.get("name"));
+
+        @SuppressWarnings("unchecked")
+        Map<String,Object> map2 = (Map<String,Object>) map.get("next");
+        assertNotNull(map2);
+        assertEquals(2, map2.size());
+        assertEquals("last", map2.get("name"));
+        assertNull(map2.get("next"));
+    }
+
+    /**
+     * Test for verifying that [JACKSON-158] works as expected
+     */
+    public void testSelfReference() throws Exception
+    {
+        Bean selfRef = new Bean(null, "self-refs");
+        Bean first = new Bean(selfRef, "first");
+        selfRef.assignNext(selfRef);
+        ObjectMapper m = new ObjectMapper();
+        Bean[] wrapper = new Bean[] { first };
+        try {
+            writeAndMap(m, wrapper);
+        } catch (JsonMappingException e) {
+            verifyException(e, "Direct self-reference leading to cycle");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestDateSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestDateSerialization.java
new file mode 100644
index 0000000..3a83de2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestDateSerialization.java
@@ -0,0 +1,166 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+
+public class TestDateSerialization
+    extends BaseMapTest
+{
+    static class TimeZoneBean {
+        private TimeZone tz;
+        
+        public TimeZoneBean(String name) {
+            tz = TimeZone.getTimeZone(name);
+        }
+
+        public TimeZone getTz() { return tz; }
+    }
+
+    static class DateAsNumberBean {
+        @JsonFormat(shape=JsonFormat.Shape.NUMBER)
+        public Date date;
+        public DateAsNumberBean(long l) { date = new java.util.Date(l); }
+    }
+
+    static class DateAsStringBean {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
+        public Date date;
+        public DateAsStringBean(long l) { date = new java.util.Date(l); }
+    }
+
+    static class DateInCETBean {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH:00", timezone="CET")
+        public Date date;
+        public DateInCETBean(long l) { date = new java.util.Date(l); }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testDateNumeric() throws IOException
+    {
+        // default is to output time stamps...
+        assertTrue(MAPPER.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS));
+        // shouldn't matter which offset we give...
+        String json = MAPPER.writeValueAsString(new Date(199L));
+        assertEquals("199", json);
+    }
+
+    public void testDateISO8601() throws IOException
+    {
+        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);
+    }
+
+    public void testDateOther() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss");
+        df.setTimeZone(TimeZone.getTimeZone("PST"));
+        mapper.setDateFormat(df);
+        // 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));
+    }
+
+    public void testTimeZone() throws IOException
+    {
+        TimeZone input = TimeZone.getTimeZone("PST");
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals(quote("PST"), json);
+    }
+
+    // [JACKSON-663]
+    public void testTimeZoneInBean() throws IOException
+    {
+        String json = MAPPER.writeValueAsString(new TimeZoneBean("PST"));
+        assertEquals("{\"tz\":\"PST\"}", json);
+    }
+    
+    // [JACKSON-648]: (re)configuring via ObjectWriter
+    public void testDateUsingObjectWriter() throws IOException
+    {
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'X'HH:mm:ss");
+        df.setTimeZone(TimeZone.getTimeZone("PST"));
+        assertEquals(quote("1969-12-31X16:00:00"),
+                MAPPER.writer(df).writeValueAsString(new Date(0L)));
+        ObjectWriter w = MAPPER.writer((DateFormat)null);
+        assertEquals("0", w.writeValueAsString(new Date(0L)));
+
+        w = w.with(df);
+        assertEquals(quote("1969-12-31X16:00:00"), w.writeValueAsString(new Date(0L)));
+        w = w.with((DateFormat) null);
+        assertEquals("0", w.writeValueAsString(new Date(0L)));
+    }
+
+    // [JACKSON-606]
+    public void testDatesAsMapKeys() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<Date,Integer> map = new HashMap<Date,Integer>();
+        assertFalse(mapper.isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS));
+        map.put(new Date(0L), Integer.valueOf(1));
+        // by default will serialize as ISO-8601 values...
+        assertEquals("{\"1970-01-01T00:00:00.000+0000\":1}", mapper.writeValueAsString(map));
+        
+        // but can change to use timestamps too
+        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true);
+        assertEquals("{\"0\":1}", mapper.writeValueAsString(map));
+    }
+
+    // [JACKSON-435]
+    public void testDateWithJsonFormat() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json;
+
+        // first: test overriding writing as timestamp
+        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        json = mapper.writeValueAsString(new DateAsNumberBean(0L));
+        assertEquals("{\"date\":0}", json);
+
+        // then reverse
+        mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        json = mapper.writer().with(getUTCTimeZone()).writeValueAsString(new DateAsStringBean(0L));
+        assertEquals("{\"date\":\"1970-01-01\"}", json);
+
+        // and with different DateFormat; CET is one hour ahead of GMT
+        json = mapper.writeValueAsString(new DateInCETBean(0L));
+        assertEquals("{\"date\":\"1970-01-01,01:00\"}", json);
+    }
+
+    /**
+     * Test to ensure that setting a TimeZone _after_ dateformat should enforce
+     * that timezone on format, regardless of TimeZone format had.
+     */
+    public void testWithTimeZoneOverride() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        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);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java
new file mode 100644
index 0000000..6aa1857
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEmptyClass.java
@@ -0,0 +1,100 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class TestEmptyClass
+    extends BaseMapTest
+{
+    static class Empty { }
+
+    @JsonSerialize
+    static class EmptyWithAnno { }
+
+    // for [JACKSON-695]:
+
+    @JsonSerialize(using=NonZeroSerializer.class)
+    static class NonZero {
+        public int nr;
+        
+        public NonZero(int i) { nr = i; }
+    }
+
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY)
+    static class NonZeroWrapper {
+        public NonZero value;
+        
+        public NonZeroWrapper(int i) {
+            value = new NonZero(i);
+        }
+    }
+    
+    static class NonZeroSerializer extends JsonSerializer<NonZero>
+    {
+        @Override
+        public void serialize(NonZero value, JsonGenerator jgen, SerializerProvider provider) throws IOException
+        {
+            jgen.writeNumber(value.nr);
+        }
+
+        @Override
+        public boolean isEmpty(NonZero value) {
+            if (value == null) return true;
+            return (value.nr == 0);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    protected final ObjectMapper mapper = new ObjectMapper();
+    
+    /**
+     * Test to check that [JACKSON-201] works if there is a recognized
+     * annotation (which indicates type is serializable)
+     */
+    public void testEmptyWithAnnotations() throws Exception
+    {
+        // First: without annotations, should complain
+        try {
+            serializeAsString(mapper, new Empty());
+        } catch (JsonMappingException e) {
+            verifyException(e, "No serializer found for class");
+        }
+
+        // But not if there is a recognized annotation
+        assertEquals("{}", serializeAsString(mapper, new EmptyWithAnno()));
+
+        // Including class annotation through mix-ins
+        ObjectMapper m2 = new ObjectMapper();
+        m2.addMixInAnnotations(Empty.class, EmptyWithAnno.class);
+        assertEquals("{}", m2.writeValueAsString(new Empty()));
+    }
+
+    /**
+     * Alternative it is possible to use a feature to allow
+     * serializing empty classes, too
+     */
+    public void testEmptyWithFeature() throws Exception
+    {
+        // should be enabled by default
+        assertTrue(mapper.getSerializationConfig().isEnabled(SerializationFeature.FAIL_ON_EMPTY_BEANS));
+        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+        assertEquals("{}", serializeAsString(mapper, new Empty()));
+    }
+
+    // [JACKSON-695], JsonSerializer.isEmpty()
+    public void testCustomNoEmpty() throws Exception
+    {
+        // first non-empty:
+        assertEquals("{\"value\":123}", mapper.writeValueAsString(new NonZeroWrapper(123)));
+        // then empty:
+        assertEquals("{}", mapper.writeValueAsString(new NonZeroWrapper(0)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java
new file mode 100644
index 0000000..5271936
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java
@@ -0,0 +1,282 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+/**
+ * Unit tests for verifying serialization of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class TestEnumSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper enums
+    /**********************************************************
+     */
+
+    /**
+     * Test enumeration for verifying Enum serialization functionality.
+     */
+    protected enum TestEnum {
+        A, B, C;
+        private TestEnum() { }
+
+        @Override public String toString() { return name().toLowerCase(); }
+    }
+
+    /**
+     * Alternative version that forces use of "toString-serializer".
+     */
+    @JsonSerialize(using=ToStringSerializer.class)
+    protected enum AnnotatedTestEnum {
+        A2, B2, C2;
+        private AnnotatedTestEnum() { }
+
+        @Override public String toString() { return name().toLowerCase(); }
+    }
+
+    protected enum EnumWithJsonValue {
+        A("foo"), B("bar");
+        private final String name;
+        private EnumWithJsonValue(String n) {
+            name = n;
+        }
+        @JsonValue
+        @Override
+        public String toString() { return name; }
+    }
+    
+    protected static interface ToStringMixin {
+        @Override
+        @JsonValue public String toString();
+    }
+
+    protected enum SerializableEnum implements JsonSerializable
+    {
+        A, B, C;
+
+        private SerializableEnum() { }
+
+        @Override
+        public void serializeWithType(JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)
+                throws IOException, JsonProcessingException
+        {
+            serialize(jgen, provider);
+        }
+
+        @Override
+        public void serialize(JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException
+        {
+            jgen.writeString("foo");
+        }
+    }
+
+    protected enum LowerCaseEnum {
+        A, B, C;
+        private LowerCaseEnum() { }
+        @Override
+        public String toString() { return name().toLowerCase(); }
+    }
+
+    static class MapBean {
+        public Map<TestEnum,Integer> map = new HashMap<TestEnum,Integer>();
+        
+        public void add(TestEnum key, int value) {
+            map.put(key, Integer.valueOf(value));
+        }
+    }
+
+    // [JACKSON-757]
+    static enum NOT_OK {
+        V1("v1"); 
+        protected String key;
+        // any runtime-persistent annotation is fine
+        NOT_OK(@JsonProperty String key) { this.key = key; }
+    }
+
+    static enum OK {
+        V1("v1");
+        protected String key;
+        OK(String key) { this.key = key; }
+    }
+    
+    // Types for [https://github.com/FasterXML/jackson-databind/issues/24]
+    // (Enums as JSON Objects)
+
+    @JsonFormat(shape=JsonFormat.Shape.OBJECT)
+    static enum PoNUM {
+        A("a1"), B("b2");
+
+        @JsonProperty
+        protected final String value;
+        
+        private PoNUM(String v) { value = v; }
+
+        public String getValue() { return value; }
+    }
+
+    static class PoNUMContainer {
+        @JsonFormat(shape=Shape.NUMBER)
+        public OK text = OK.V1;
+    }
+    
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY) // not supported as of now
+    static enum BrokenPoNum
+    {
+        A, B;
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimple() throws Exception
+    {
+        assertEquals("\"B\"", mapper.writeValueAsString(TestEnum.B));
+    }
+
+    public void testEnumSet() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        EnumSet<TestEnum> value = EnumSet.of(TestEnum.B);
+        mapper.writeValue(sw, value);
+        assertEquals("[\"B\"]", sw.toString());
+    }
+
+    /**
+     * Whereas regular Enum serializer uses enum names, some users
+     * prefer calling toString() instead. So let's verify that
+     * this can be done using annotation for enum class.
+     */
+    public void testEnumUsingToString() throws Exception
+    {
+        StringWriter sw = new StringWriter();
+        mapper.writeValue(sw, AnnotatedTestEnum.C2);
+        assertEquals("\"c2\"", sw.toString());
+    }
+
+    // Test [JACKSON-214]
+    public void testSubclassedEnums() throws Exception
+    {
+        assertEquals("\"B\"", mapper.writeValueAsString(EnumWithSubClass.B));
+    }
+
+    // [JACKSON-193]
+    public void testEnumsWithJsonValue() throws Exception
+    {
+        assertEquals("\"bar\"", mapper.writeValueAsString(EnumWithJsonValue.B));
+    }
+
+    // also, for [JACKSON-193], needs to work via mix-ins
+    public void testEnumsWithJsonValueUsingMixin() throws Exception
+    {
+        // can't share, as new mix-ins are added
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.addMixInAnnotations(TestEnum.class, ToStringMixin.class);
+        assertEquals("\"b\"", mapper.writeValueAsString(TestEnum.B));
+    }
+
+    /**
+     * Test for ensuring that @JsonSerializable is used with Enum types as well
+     * as with any other types.
+     */
+    public void testSerializableEnum() throws Exception
+    {
+        assertEquals("\"foo\"", mapper.writeValueAsString(SerializableEnum.A));
+    }
+
+    // [JACKSON-212]
+    public void testToStringEnum() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
+        assertEquals("\"b\"", mapper.writeValueAsString(LowerCaseEnum.B));
+    }
+
+    // [JACKSON-212]
+    public void testToStringEnumWithEnumMap() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
+        EnumMap<LowerCaseEnum,String> m = new EnumMap<LowerCaseEnum,String>(LowerCaseEnum.class);
+        m.put(LowerCaseEnum.C, "value");
+        assertEquals("{\"c\":\"value\"}", mapper.writeValueAsString(m));
+    }
+
+    // [JACKSON-576]
+    public void testMapWithEnumKeys() throws Exception
+    {
+        MapBean bean = new MapBean();
+        bean.add(TestEnum.B, 3);
+        String json = mapper.writeValueAsString(bean);
+        assertEquals("{\"map\":{\"b\":3}}", json);
+    }
+    
+    // [JACKSON-684]
+    public void testAsIndex() throws Exception
+    {
+        // By default, serialize using name
+        ObjectMapper mapper = new ObjectMapper();
+        assertFalse(mapper.isEnabled(SerializationFeature.WRITE_ENUMS_USING_INDEX));
+        assertEquals(quote("B"), mapper.writeValueAsString(TestEnum.B));
+
+        // but we can change (dynamically, too!) it to be number-based
+        mapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
+        assertEquals("1", mapper.writeValueAsString(TestEnum.B));
+    }
+
+    // [JACKSON-757]
+    public void testAnnotationsOnEnumCtor() throws Exception
+    {
+        assertEquals(quote("V1"), mapper.writeValueAsString(OK.V1));
+        assertEquals(quote("V1"), mapper.writeValueAsString(NOT_OK.V1));
+        assertEquals(quote("V2"), mapper.writeValueAsString(NOT_OK2.V2));
+    }
+
+    // Tests for [issue#24]
+
+    public void testEnumAsObjectValid() throws Exception {
+        assertEquals("{\"value\":\"a1\"}", mapper.writeValueAsString(PoNUM.A));
+    }
+
+    public void testEnumAsIndexViaAnnotations() throws Exception {
+        assertEquals("{\"text\":0}", mapper.writeValueAsString(new PoNUMContainer()));
+    }
+    
+    public void testEnumAsObjectBroken() throws Exception
+    {
+        try {
+            String json = mapper.writeValueAsString(BrokenPoNum.A);
+            fail("Should not have succeeded, produced: "+json);
+        } catch (JsonMappingException e) {
+            verifyException(e, "Unsupported serialization shape");
+        }
+    }
+}
+
+// [JACKSON-757], non-inner enum
+enum NOT_OK2 {
+    V2("v2"); 
+    protected String key;
+    // any runtime-persistent annotation is fine
+    NOT_OK2(@JsonProperty String key) { this.key = key; }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
new file mode 100644
index 0000000..2334467
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionHandling.java
@@ -0,0 +1,156 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import com.fasterxml.jackson.test.BaseTest;
+import com.fasterxml.jackson.test.BrokenStringWriter;
+
+/**
+ * Unit test for verifying that exceptions are properly handled (caught,
+ * re-thrown or wrapped, depending)
+ * with Object serialization.
+ */
+public class TestExceptionHandling
+    extends BaseTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    static class Bean {
+        // no methods, we'll use our custom serializer
+    }
+
+    static class SerializerWithErrors
+        extends JsonSerializer<Bean>
+    {
+        @Override
+        public void serialize(Bean value, JsonGenerator jgen, SerializerProvider provider)
+        {
+            throw new IllegalArgumentException("test string");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    /**
+     * Unit test that verifies that by default all exceptions except for
+     * JsonMappingException are caught and wrapped.
+     */
+    public void testCatchAndRethrow()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SimpleModule module = new SimpleModule("test-exceptions", Version.unknownVersion());
+        module.addSerializer(Bean.class, new SerializerWithErrors());
+        mapper.registerModule(module);
+        try {
+            StringWriter sw = new StringWriter();
+            /* And just to make things more interesting, let's create
+             * a nested data struct...
+             */
+            Bean[] b = { new Bean() };
+            List<Bean[]> l = new ArrayList<Bean[]>();
+            l.add(b);
+            mapper.writeValue(sw, l);
+            fail("Should have gotten an exception");
+        } catch (IOException e) {
+            // should contain original message somewhere
+            verifyException(e, "test string");
+            Throwable root = e.getCause();
+            assertNotNull(root);
+
+            if (!(root instanceof IllegalArgumentException)) {
+                fail("Wrapped exception not IAE, but "+root.getClass());
+            }
+        }
+    }
+
+    /**
+     * Unit test for verifying that regular IOExceptions are not wrapped
+     * but are passed through as is.
+     */
+    public void testExceptionWithSimpleMapper()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            BrokenStringWriter sw = new BrokenStringWriter("TEST");
+            mapper.writeValue(sw, createLongObject());
+            fail("Should have gotten an exception");
+        } catch (IOException e) {
+            verifyException(e, IOException.class, "TEST");
+        }
+    }
+
+    public void testExceptionWithMapperAndGenerator()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JsonFactory f = new MappingJsonFactory();
+        BrokenStringWriter sw = new BrokenStringWriter("TEST");
+        JsonGenerator jg = f.createGenerator(sw);
+
+        try {
+            mapper.writeValue(jg, createLongObject());
+            fail("Should have gotten an exception");
+        } catch (IOException e) {
+            verifyException(e, IOException.class, "TEST");
+        }
+    }
+
+    public void testExceptionWithGeneratorMapping()
+        throws Exception
+    {
+        JsonFactory f = new MappingJsonFactory();
+        JsonGenerator jg = f.createGenerator(new BrokenStringWriter("TEST"));
+        try {
+            jg.writeObject(createLongObject());
+            fail("Should have gotten an exception");
+        } catch (Exception e) {
+            verifyException(e, IOException.class, "TEST");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    void verifyException(Exception e, Class<?> expType, String expMsg)
+        throws Exception
+    {
+        if (e.getClass() != expType) {
+            fail("Expected exception of type "+expType.getName()+", got "+e.getClass().getName());
+        }
+        if (expMsg != null) {
+            verifyException(e, expMsg);
+        }
+    }
+
+    Object createLongObject()
+    {
+        List<Object> leaf = new ArrayList<Object>();
+        for (int i = 0; i < 256; ++i) {
+            leaf.add(Integer.valueOf(i));
+        }
+        List<Object> root = new ArrayList<Object>(256);
+        for (int i = 0; i < 256; ++i) {
+            root.add(leaf);
+        }
+        return root;
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java
new file mode 100644
index 0000000..f056ab2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestExceptionSerialization.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that simple exceptions can be serialized.
+ */
+public class TestExceptionSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+
+    public void testSimple() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String TEST = "test exception";
+        Map<String,Object> result = writeAndMap(mapper, new Exception(TEST));
+        // JDK 7 has introduced a new property 'suppressed' to Throwable
+        Object ob = result.get("suppressed");
+        if (ob != null) {
+            assertEquals(5, result.size());
+        } else {
+            assertEquals(4, result.size());
+        }
+
+        assertEquals(TEST, result.get("message"));
+        assertNull(result.get("cause"));
+        assertEquals(TEST, result.get("localizedMessage"));
+
+        // hmmh. what should we get for stack traces?
+        Object traces = result.get("stackTrace");
+        if (!(traces instanceof List<?>)) {
+            fail("Expected a List for exception member 'stackTrace', got: "+traces);
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
new file mode 100644
index 0000000..197204d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestFeatures.java
@@ -0,0 +1,269 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+/**
+ * Unit tests for checking whether JsonSerializerFactory.Feature
+ * configuration works
+ */
+public class TestFeatures
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * 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;
+
+        protected boolean wasClosed = false;
+
+        @Override
+        public void close() throws IOException {
+            wasClosed = true;
+        }
+    }
+
+    private static class StringListBean {
+        @SuppressWarnings("unused")
+        public Collection<String> values;
+        
+        public StringListBean(Collection<String> v) { values = v; }
+    }
+    
+    /*
+    /**********************************************************
+    /* 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]
+    public void testCloseCloseable() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        // default should be disabled:
+        CloseableBean bean = new CloseableBean();
+        m.writeValueAsString(bean);
+        assertFalse(bean.wasClosed);
+
+        // but can enable it:
+        m.configure(SerializationFeature.CLOSE_CLOSEABLE, true);
+        bean = new CloseableBean();
+        m.writeValueAsString(bean);
+        assertTrue(bean.wasClosed);
+
+        // also: let's ensure that ObjectWriter won't interfere with it
+        bean = new CloseableBean();
+        m.writerWithType(CloseableBean.class).writeValueAsString(bean);
+        assertTrue(bean.wasClosed);
+    }
+
+    // Test for [JACKSON-289]
+    public void testCharArrays() throws IOException
+    {
+        char[] chars = new char[] { 'a','b','c' };
+        ObjectMapper m = new ObjectMapper();
+        // default: serialize as Strings
+        assertEquals(quote("abc"), m.writeValueAsString(chars));
+        
+        // new feature: serialize as JSON array:
+        m.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true);
+        assertEquals("[\"a\",\"b\",\"c\"]", m.writeValueAsString(chars));
+    }
+
+    // Test for [JACKSON-401]
+    public void testFlushingAutomatic() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertTrue(mapper.getSerializationConfig().isEnabled(SerializationFeature.FLUSH_AFTER_WRITE_VALUE));
+        // default is to flush after writeValue()
+        StringWriter sw = new StringWriter();
+        JsonGenerator jgen = mapper.getFactory().createGenerator(sw);
+        mapper.writeValue(jgen, Integer.valueOf(13));
+        assertEquals("13", sw.toString());
+        jgen.close();
+
+        // ditto with ObjectWriter
+        sw = new StringWriter();
+        jgen = mapper.getFactory().createGenerator(sw);
+        ObjectWriter ow = mapper.writer();
+        ow.writeValue(jgen, Integer.valueOf(99));
+        assertEquals("99", sw.toString());
+        jgen.close();
+    }
+
+    // Test for [JACKSON-401]
+    public void testFlushingNotAutomatic() throws IOException
+    {
+        // but should not occur if configured otherwise
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false);
+        StringWriter sw = new StringWriter();
+        JsonGenerator jgen = mapper.getFactory().createGenerator(sw);
+
+        mapper.writeValue(jgen, Integer.valueOf(13));
+        // no flushing now:
+        assertEquals("", sw.toString());
+        // except when actually flushing
+        jgen.flush();
+        assertEquals("13", sw.toString());
+        jgen.close();
+        // Also, same should happen with ObjectWriter
+        sw = new StringWriter();
+        jgen = mapper.getFactory().createGenerator(sw);
+        ObjectWriter ow = mapper.writer();
+        ow.writeValue(jgen, Integer.valueOf(99));
+        assertEquals("", sw.toString());
+        // except when actually flushing
+        jgen.flush();
+        assertEquals("99", sw.toString());
+        jgen.close();
+    }
+
+    // Test for [JACKSON-805]
+    public void testSingleElementCollections() throws IOException
+    {
+        final ObjectWriter writer = objectWriter().with(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
+
+        // Lists:
+        ArrayList<String> strs = new ArrayList<String>();
+        strs.add("xyz");
+        assertEquals(quote("xyz"), writer.writeValueAsString(strs));
+        ArrayList<Integer> ints = new ArrayList<Integer>();
+        ints.add(13);
+        assertEquals("13", writer.writeValueAsString(ints));
+
+        // other Collections, like Sets:
+        HashSet<Long> longs = new HashSet<Long>();
+        longs.add(42L);
+        assertEquals("42", writer.writeValueAsString(longs));
+        // [Issue#180]
+        final String EXP_STRINGS = "{\"values\":\"foo\"}";
+        assertEquals(EXP_STRINGS, writer.writeValueAsString(new StringListBean(Collections.singletonList("foo"))));
+
+        final Set<String> SET = new HashSet<String>();
+        SET.add("foo");
+        assertEquals(EXP_STRINGS, writer.writeValueAsString(new StringListBean(SET)));
+        
+        // arrays:
+        assertEquals("true", writer.writeValueAsString(new boolean[] { true }));
+        assertEquals("true", writer.writeValueAsString(new Boolean[] { Boolean.TRUE }));
+        assertEquals("3", writer.writeValueAsString(new int[] { 3 }));
+        assertEquals(quote("foo"), writer.writeValueAsString(new String[] { "foo" }));
+        
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestFieldSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestFieldSerialization.java
new file mode 100644
index 0000000..4a21ec3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestFieldSerialization.java
@@ -0,0 +1,212 @@
+package com.fasterxml.jackson.databind.ser;
+
+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.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * Unit tests for verifying that field-backed properties can also be serialized
+ * (since version 1.1) as well as getter-accessible properties.
+ */
+public class TestFieldSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    static class SimpleFieldBean
+    {
+        public int x, y;
+
+        // not auto-detectable, not public
+        int z;
+
+        // ignored, not detectable either
+        @JsonIgnore public int a;
+    }
+    
+    static class SimpleFieldBean2
+    {
+        @JsonSerialize String[] values;
+
+        // note: this annotation should not matter for serialization:
+        @JsonDeserialize int dummy;
+    }
+
+    static class TransientBean
+    {
+        public int a;
+        // transients should not be included
+        public transient int b;
+        // or statics
+        public static int c;
+    }
+
+    @JsonAutoDetect(setterVisibility=Visibility.PUBLIC_ONLY, fieldVisibility=Visibility.NONE)
+    public class NoAutoDetectBean
+    {
+        // not auto-detectable any more
+        public int x;
+
+        @JsonProperty("z")
+        public int _z;
+    }
+
+    /**
+     * Let's test invalid bean too: can't have 2 logical properties
+     * with same name.
+     *<p>
+     * 21-Feb-2010, tatus: That is, not within same class.
+     *    As per [JACKSON-226] it is acceptable to "override"
+     *    field definitions in sub-classes.
+     */
+    public static class DupFieldBean
+    {
+        @JsonProperty("foo")
+        public int _z;
+
+        @JsonSerialize
+        private int foo;
+    }
+
+    public static class DupFieldBean2
+    {
+        public int z;
+
+        @JsonProperty("z")
+        public int _z;
+    }
+
+    public static class OkDupFieldBean
+        extends SimpleFieldBean
+    {
+        @JsonProperty("x")
+        protected int myX;
+
+        public int y;
+
+        public OkDupFieldBean(int x, int y) {
+            this.myX = x;
+            this.y = y;
+        }
+    }
+
+    /**
+     * It is ok to have a method-based and field-based property
+     * introspectable: only one should be serialized, and since
+     * methods have precedence, it should be the method one.
+     */
+    public static class FieldAndMethodBean
+    {
+        @JsonProperty public int z;
+
+        @JsonProperty("z") public int getZ() { return z+1; }
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests, success
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleAutoDetect() throws Exception
+    {
+        SimpleFieldBean bean = new SimpleFieldBean();
+        // let's set x, leave y as is
+        bean.x = 13;
+        Map<String,Object> result = writeAndMap(MAPPER, bean);
+        assertEquals(2, result.size());
+        assertEquals(Integer.valueOf(13), result.get("x"));
+        assertEquals(Integer.valueOf(0), result.get("y"));
+    }
+
+    @SuppressWarnings("unchecked")
+	public void testSimpleAnnotation() throws Exception
+    {
+        SimpleFieldBean2 bean = new SimpleFieldBean2();
+        bean.values = new String[] { "a", "b" };
+        Map<String,Object> result = writeAndMap(MAPPER, bean);
+        assertEquals(1, result.size());
+        List<String> values = (List<String>) result.get("values");
+        assertEquals(2, values.size());
+        assertEquals("a", values.get(0));
+        assertEquals("b", values.get(1));
+    }
+
+    public void testTransientAndStatic() throws Exception
+    {
+        TransientBean bean = new TransientBean();
+        Map<String,Object> result = writeAndMap(MAPPER, bean);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(0), result.get("a"));
+    }
+
+    public void testNoAutoDetect() throws Exception
+    {
+        NoAutoDetectBean bean = new NoAutoDetectBean();
+        bean._z = -4;
+        Map<String,Object> result = writeAndMap(MAPPER, bean);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(-4), result.get("z"));
+    }
+
+    /**
+     * Unit test that verifies that if both a field and a getter
+     * method exist for a logical property (which is allowed),
+     * getter has precendence over field.
+     */
+    public void testMethodPrecedence() throws Exception
+    {
+        FieldAndMethodBean bean = new FieldAndMethodBean();
+        bean.z = 9;
+        assertEquals(10, bean.getZ());
+        assertEquals("{\"z\":10}", MAPPER.writeValueAsString(bean));
+    }
+
+    /**
+     * Testing [JACKSON-226]: it is ok to have "field override",
+     * as long as there are no intra-class conflicts.
+     */
+    public void testOkDupFields() throws Exception
+    {
+        OkDupFieldBean bean = new OkDupFieldBean(1, 2);
+        Map<String,Object> json = writeAndMap(MAPPER, bean);
+        assertEquals(2, json.size());
+        assertEquals(Integer.valueOf(1), json.get("x"));
+        assertEquals(Integer.valueOf(2), json.get("y"));
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests, failure cases
+    /**********************************************************
+     */
+
+    public void testFailureDueToDups() throws Exception
+    {
+        try {
+            writeAndMap(MAPPER, new DupFieldBean());
+        } catch (JsonMappingException e) {
+            verifyException(e, "Multiple fields representing");
+        }
+    }
+
+    public void testFailureDueToDupField() throws Exception
+    {
+        try {
+            writeAndMap(MAPPER, new DupFieldBean2());
+        } catch (JsonMappingException e) {
+            verifyException(e, "Multiple fields representing");
+        }
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestFiltering.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestFiltering.java
new file mode 100644
index 0000000..a9d9f47
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestFiltering.java
@@ -0,0 +1,126 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.impl.*;
+
+/**
+ * Tests for verifying that bean property filtering using JsonFilter
+ * works as expected.
+ */
+public class TestFiltering extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    @JsonFilter("RootFilter")
+    static class Bean {
+        public String a = "a";
+        public String b = "b";
+    }
+
+    
+    // [Issue#89]
+    static class Pod
+    {
+        protected String username;
+
+//        @JsonProperty(value = "user_password")
+        protected String userPassword;
+
+        public String getUsername() {
+            return username;
+        }
+
+        public void setUsername(String value) {
+            this.username = value;
+        }
+
+        @JsonIgnore
+        @JsonProperty(value = "user_password")
+        public java.lang.String getUserPassword() {
+            return userPassword;
+        }
+
+        @JsonProperty(value = "user_password")
+        public void setUserPassword(String value) {
+            this.userPassword = value;
+        }
+
+    }    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testSimpleInclusionFilter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FilterProvider prov = new SimpleFilterProvider().addFilter("RootFilter",
+                SimpleBeanPropertyFilter.filterOutAllExcept("a"));
+        assertEquals("{\"a\":\"a\"}", mapper.writer(prov).writeValueAsString(new Bean()));
+
+        // [JACKSON-504]: also verify it works via mapper
+        mapper = new ObjectMapper();
+        mapper.setFilters(prov);
+        assertEquals("{\"a\":\"a\"}", mapper.writeValueAsString(new Bean()));
+    }
+
+    public void testSimpleExclusionFilter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FilterProvider prov = new SimpleFilterProvider().addFilter("RootFilter",
+                SimpleBeanPropertyFilter.serializeAllExcept("a"));
+        assertEquals("{\"b\":\"b\"}", mapper.writer(prov).writeValueAsString(new Bean()));
+    }
+
+    // should handle missing case gracefully
+    public void testMissingFilter() throws Exception
+    {
+        // First: default behavior should be to throw an exception
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            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 BeanPropertyFilter with id 'RootFilter'");
+        }
+        
+        // but when changing behavior, should work difference
+        SimpleFilterProvider fp = new SimpleFilterProvider().setFailOnUnknownId(false);
+        mapper.setFilters(fp);
+        String json = mapper.writeValueAsString(new Bean());
+        assertEquals("{\"a\":\"a\",\"b\":\"b\"}", json);
+    }
+    
+    // defaulting, as per [JACKSON-449]
+    public void testDefaultFilter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FilterProvider prov = new SimpleFilterProvider().setDefaultFilter(SimpleBeanPropertyFilter.filterOutAllExcept("b"));
+        assertEquals("{\"b\":\"b\"}", mapper.writer(prov).writeValueAsString(new Bean()));
+    }
+
+    // [Issue#89] combining @JsonIgnore, @JsonProperty
+    public void testIssue89() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Pod pod = new Pod();
+        pod.username = "Bob";
+        pod.userPassword = "s3cr3t!";
+
+        String json = mapper.writeValueAsString(pod);
+
+        assertEquals("{\"username\":\"Bob\"}", json);
+
+        Pod pod2 = mapper.readValue("{\"username\":\"Bill\",\"user_password\":\"foo!\"}", Pod.class);
+        assertEquals("Bill", pod2.username);
+        assertEquals("foo!", pod2.userPassword);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java
new file mode 100644
index 0000000..a28f9dd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestGenericTypes.java
@@ -0,0 +1,158 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestGenericTypes extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+    
+    static class Account {
+        private Long id;        
+        private String name;
+        
+        public Account(String name, Long id) {
+            this.id = id;
+            this.name = name;
+        }
+
+        public String getName() { return name; }
+        public Long getId() { return id; }
+    }
+
+    static class Key<T> {
+        private final T id;
+        
+        public Key(T id) { this.id = id; }
+        
+        public T getId() { return id; }
+
+        public <V> Key<V> getParent() { return null; }
+    }
+ 
+    static class Person1 {
+        private Long id;
+        private String name;
+        private Key<Account> account;
+        
+        public Person1(String name) { this.name = name; }
+
+        public String getName() {
+                return name;
+        }
+
+        public Key<Account> getAccount() {
+                return account;
+        }
+
+        public Long getId() {
+                return id;
+        }
+
+        public void setAccount(Key<Account> account) {
+            this.account = account;
+        }    
+    }
+
+    static class Person2 {
+        private Long id;
+        private String name;
+        private List<Key<Account>> accounts;
+        
+        public Person2(String name) {
+                this.name = name;
+        }
+
+        public String getName() { return name; }
+        public List<Key<Account>> getAccounts() { return accounts; }
+        public Long getId() { return id; }
+
+        public void setAccounts(List<Key<Account>> accounts) {
+            this.accounts = accounts;
+        }
+    }
+
+    static class GenericBogusWrapper<T> {
+        public Element wrapped;
+
+        public GenericBogusWrapper(T v) { wrapped = new Element(v); }
+
+        class Element {
+            public T value;
+    
+            public Element(T v) { value = v; }
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public void testIssue468a() throws Exception
+    {
+        Person1 p1 = new Person1("John");
+        p1.setAccount(new Key<Account>(new Account("something", 42L)));
+        
+        // First: ensure we can serialize (pre 1.7 this failed)
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(p1);
+
+        // and then verify that results make sense
+        Map<String,Object> map = mapper.readValue(json, Map.class);
+        assertEquals("John", map.get("name"));
+        Object ob = map.get("account");
+        assertNotNull(ob);
+        Map<String,Object> acct = (Map<String,Object>) ob;
+        Object idOb = acct.get("id");
+        assertNotNull(idOb);
+        Map<String,Object> key = (Map<String,Object>) idOb;
+        assertEquals("something", key.get("name"));
+        assertEquals(Integer.valueOf(42), key.get("id"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testIssue468b() throws Exception
+    {
+        Person2 p2 = new Person2("John");
+        List<Key<Account>> accounts = new ArrayList<Key<Account>>();
+        accounts.add(new Key<Account>(new Account("a", 42L)));
+        accounts.add(new Key<Account>(new Account("b", 43L)));
+        accounts.add(new Key<Account>(new Account("c", 44L)));
+        p2.setAccounts(accounts);
+
+        // serialize without error:
+        ObjectMapper mapper = new ObjectMapper();               
+        String json = mapper.writeValueAsString(p2);
+
+        // then verify output
+        Map<String,Object> map = mapper.readValue(json, Map.class);
+        assertEquals("John", map.get("name"));
+        Object ob = map.get("accounts");
+        assertNotNull(ob);
+        List<?> acctList = (List<?>) ob;
+        assertEquals(3, acctList.size());
+        // ... might want to verify more, but for now that should suffice
+    }
+
+    /**
+     * Issue [JACKSON-572] is about unbound type variables, usually resulting
+     * from inner classes of generic classes (like Sets).
+     */
+    public void testUnboundIssue572() throws Exception
+    {
+        GenericBogusWrapper<Integer> list = new GenericBogusWrapper<Integer>(Integer.valueOf(7));
+        String json = new ObjectMapper().writeValueAsString(list);
+        assertEquals("{\"wrapped\":{\"value\":7}}", json);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJSONP.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJSONP.java
new file mode 100644
index 0000000..62860a7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJSONP.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.util.JSONPObject;
+
+public class TestJSONP
+    extends BaseMapTest
+{
+    static class Base {
+        public String a;
+    }
+    static class Impl extends Base {
+        public String b;
+
+        public Impl(String a, String b) {
+            this.a = a;
+            this.b = b;
+        }
+    }
+    
+    public void testSimpleScalars() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals("callback(\"abc\")",
+                serializeAsString(m, new JSONPObject("callback", "abc")));
+        assertEquals("calc(123)",
+                serializeAsString(m, new JSONPObject("calc", Integer.valueOf(123))));
+        assertEquals("dummy(null)",
+                serializeAsString(m, new JSONPObject("dummy", null)));
+    }
+
+    public void testSimpleBean() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals("xxx({\"a\":\"123\",\"b\":\"456\"})",
+                serializeAsString(m, new JSONPObject("xxx",
+                        new Impl("123", "456"))));
+    }
+    
+    /**
+     * Test to ensure that it is possible to force a static type for wrapped
+     * value.
+     */
+    public void testWithType() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Object ob = new Impl("abc", "def");
+        JavaType type = TypeFactory.defaultInstance().uncheckedSimpleType(Base.class);
+        assertEquals("do({\"a\":\"abc\"})",
+                serializeAsString(m, new JSONPObject("do", ob, type)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java
new file mode 100644
index 0000000..559c952
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJacksonTypes.java
@@ -0,0 +1,48 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.util.TokenBuffer;
+
+/**
+ * Unit tests for those Jackson types we want to ensure can be serialized.
+ */
+public class TestJacksonTypes
+    extends BaseMapTest
+{
+    public void testLocation() throws IOException
+    {
+        File f = new File("/tmp/test.json");
+        JsonLocation loc = new JsonLocation(f, -1, 100, 13);
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(mapper, loc);
+        assertEquals(5, result.size());
+        assertEquals(f.getAbsolutePath(), result.get("sourceRef"));
+        assertEquals(Integer.valueOf(-1), result.get("charOffset"));
+        assertEquals(Integer.valueOf(-1), result.get("byteOffset"));
+        assertEquals(Integer.valueOf(100), result.get("lineNr"));
+        assertEquals(Integer.valueOf(13), result.get("columnNr"));
+
+    }
+
+    /**
+     * Verify that {@link TokenBuffer} can be properly serialized
+     * automatically, using the "standard" JSON sample document
+     */
+    public void testTokenBuffer() throws Exception
+    {
+        // First, copy events from known good source (StringReader)
+        JsonParser jp = createParserUsingReader(SAMPLE_DOC_JSON_SPEC);
+        TokenBuffer tb = new TokenBuffer(null);
+        while (jp.nextToken() != null) {
+            tb.copyCurrentEvent(jp);
+        }
+        // Then serialize as String
+        String str = serializeAsString(tb);
+        // and verify it looks ok
+        verifyJsonSpecSampleDoc(createParserUsingReader(str), true);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
new file mode 100644
index 0000000..6e8b9cc
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJdkTypes.java
@@ -0,0 +1,102 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.net.InetAddress;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * 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
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * Unit test to catch bug [JACKSON-8].
+     */
+    public void testBigDecimal()
+        throws Exception
+    {
+        Map<String, Object> map = new HashMap<String, Object>();
+        String PI_STR = "3.14159265";
+        map.put("pi", new BigDecimal(PI_STR));
+        String str = MAPPER.writeValueAsString(map);
+        assertEquals("{\"pi\":3.14159265}", str);
+    }
+    
+    public void testBigDecimalAsPlainString()
+        throws Exception
+    {
+        MAPPER.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
+        Map<String, Object> map = new HashMap<String, Object>();
+        String PI_STR = "3.00000000";
+        map.put("pi", new BigDecimal(PI_STR));
+        String str = MAPPER.writeValueAsString(map);
+        assertEquals("{\"pi\":3.00000000}", str);
+    }
+    
+    /**
+     * Unit test related to [JACKSON-155]
+     */
+    public void testFile() throws IOException
+    {
+        // this may get translated to different representation on Windows, maybe Mac:
+        File f = new File(new File("/tmp"), "foo.text");
+        String str = MAPPER.writeValueAsString(f);
+        // escape backslashes (for portability with windows)
+        String escapedAbsPath = f.getAbsolutePath().replaceAll("\\\\", "\\\\\\\\"); 
+        assertEquals(quote(escapedAbsPath), str);
+    }
+
+    public void testRegexps() throws IOException
+    {
+        final String PATTERN_STR = "\\s+([a-b]+)\\w?";
+        Pattern p = Pattern.compile(PATTERN_STR);
+        Map<String,Object> input = new HashMap<String,Object>();
+        input.put("p", p);
+        Map<String,Object> result = writeAndMap(MAPPER, input);
+        assertEquals(p.pattern(), result.get("p"));
+    }
+
+    public void testCurrency() throws IOException
+    {
+        Currency usd = Currency.getInstance("USD");
+        assertEquals(quote("USD"), MAPPER.writeValueAsString(usd));
+    }
+
+    public void testLocale() throws IOException
+    {
+        assertEquals(quote("en"), MAPPER.writeValueAsString(new Locale("en")));
+        assertEquals(quote("es_ES"), MAPPER.writeValueAsString(new Locale("es", "ES")));
+        assertEquals(quote("fi_FI_savo"), MAPPER.writeValueAsString(new Locale("FI", "fi", "savo")));
+    }
+
+    // [JACKSON-484]
+    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")));
+    }
+
+    // [JACKSON-597]
+    public void testClass() throws IOException
+    {
+        assertEquals(quote("java.lang.String"), MAPPER.writeValueAsString(String.class));
+        assertEquals(quote("int"), MAPPER.writeValueAsString(Integer.TYPE));
+        assertEquals(quote("boolean"), MAPPER.writeValueAsString(Boolean.TYPE));
+        assertEquals(quote("void"), MAPPER.writeValueAsString(Void.TYPE));
+    }
+
+    // [JACKSON-789]
+    public void testCharset() throws IOException
+    {
+        assertEquals(quote("UTF-8"), MAPPER.writeValueAsString(Charset.forName("UTF-8")));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java
new file mode 100644
index 0000000..04b9850
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonRawValue.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * This unit test suite tests functioning of {@link JsonRawValue}
+ * annotation with bean serialization.
+ */
+public class TestJsonRawValue
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    /*
+    /*********************************************************
+    /* Helper bean classes
+    /*********************************************************
+     */
+
+    /// Class for testing {@link JsonRawValue} annotations with getters returning String
+    @JsonPropertyOrder(alphabetic=true)
+    final static class ClassGetter<T>
+    {
+    	private final T _value;
+    	
+        private ClassGetter(T value) { _value = value;}
+ 
+        public T getNonRaw() { return _value; }
+
+        @JsonProperty("raw") @JsonRawValue public T foobar() { return _value; }
+        
+        @JsonProperty @JsonRawValue protected T value() { return _value; }
+    }
+    
+    /*
+    /*********************************************************
+    /* Test cases
+    /*********************************************************
+     */
+
+    public void testSimpleStringGetter() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String value = "abc";
+        String result = m.writeValueAsString(new ClassGetter<String>(value));
+        String expected = String.format("{\"nonRaw\":\"%s\",\"raw\":%s,\"value\":%s}", value, value, value);
+        assertEquals(expected, result);
+    }
+
+    public void testSimpleNonStringGetter() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        int value = 123;
+        String result = m.writeValueAsString(new ClassGetter<Integer>(value));
+        String expected = String.format("{\"nonRaw\":%d,\"raw\":%d,\"value\":%d}", value, value, value);
+        assertEquals(expected, result);
+    }
+
+    public void testNullStringGetter() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        String result = m.writeValueAsString(new ClassGetter<String>(null));
+        String expected = "{\"nonRaw\":null,\"raw\":null,\"value\":null}";
+        assertEquals(expected, result);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
new file mode 100644
index 0000000..f601454
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize.java
@@ -0,0 +1,231 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.JsonGenerator;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * This unit test suite tests use of @JsonClass Annotation
+ * with bean serialization.
+ */
+public class TestJsonSerialize
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    interface ValueInterface {
+        public int getX();
+    }
+
+    static class ValueClass
+        implements ValueInterface
+    {
+        @Override
+        public int getX() { return 3; }
+        public int getY() { return 5; }
+    }
+
+    /**
+     * Test class to verify that <code>JsonSerialize.as</code>
+     * works as expected
+     */
+    static class WrapperClassForAs
+    {
+        @JsonSerialize(as=ValueInterface.class)
+        public ValueClass getValue() {
+            return new ValueClass();
+        }
+    }
+
+    // This should indicate that static type be used for all fields
+    @JsonSerialize(typing=JsonSerialize.Typing.STATIC)
+    static class WrapperClassForStaticTyping
+    {
+        public ValueInterface getValue() {
+            return new ValueClass();
+        }
+    }
+
+    static class WrapperClassForStaticTyping2
+    {
+        @JsonSerialize(typing=JsonSerialize.Typing.STATIC)
+        public ValueInterface getStaticValue() {
+            return new ValueClass();
+        }
+
+        @JsonSerialize(typing=JsonSerialize.Typing.DYNAMIC)
+        public ValueInterface getDynamicValue() {
+            return new ValueClass();
+        }
+    }
+
+    /**
+     * Test bean that has an invalid {@link JsonSerialize} annotation.
+     */
+    static class BrokenClass
+    {
+        // invalid annotation: String not a supertype of Long
+        @JsonSerialize(as=String.class)
+        public Long getValue() {
+            return Long.valueOf(4L);
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class ValueMap extends HashMap<String,ValueInterface> { }
+    @SuppressWarnings("serial")
+    static class ValueList extends ArrayList<ValueInterface> { }
+    @SuppressWarnings("serial")
+    static class ValueLinkedList extends LinkedList<ValueInterface> { }
+    
+    // Classes for [JACKSON-294]
+    static class Foo294
+    {
+        @JsonProperty private String id;
+        @JsonSerialize(using = Bar294Serializer.class)
+        private Bar294 bar;
+
+        public Foo294() { }
+        public Foo294(String id, String id2) {
+            this.id = id;
+            bar = new Bar294(id2);
+        }
+    }
+
+    static class Bar294{
+        @JsonProperty private String id;
+        @JsonProperty private String name;
+
+        public Bar294() { }
+        public Bar294(String id) {
+            this.id = id;
+        }
+
+        public String getId() { return id; }
+        public String getName() { return name; }
+    }
+
+    static class Bar294Serializer extends JsonSerializer<Bar294>
+    {
+        @Override
+        public void serialize(Bar294 bar, JsonGenerator jgen,
+            SerializerProvider provider) throws IOException
+        {
+            jgen.writeString(bar.id);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+
+    @SuppressWarnings("unchecked")
+    public void testSimpleValueDefinition() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new WrapperClassForAs());
+        assertEquals(1, result.size());
+        Object ob = result.get("value");
+        // Should see only "x", not "y"
+        result = (Map<String,Object>) ob;
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+    }
+
+    public void testBrokenAnnotation() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        try {
+            serializeAsString(m, new BrokenClass());
+        } catch (Exception e) {
+            verifyException(e, "not a super-type of");
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testStaticTypingForClass() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new WrapperClassForStaticTyping());
+        assertEquals(1, result.size());
+        Object ob = result.get("value");
+        // Should see only "x", not "y"
+        result = (Map<String,Object>) ob;
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testMixedTypingForClass() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new WrapperClassForStaticTyping2());
+        assertEquals(2, result.size());
+
+        Object obStatic = result.get("staticValue");
+        // Should see only "x", not "y"
+        Map<String,Object> stat = (Map<String,Object>) obStatic;
+        assertEquals(1, stat.size());
+        assertEquals(Integer.valueOf(3), stat.get("x"));
+
+        Object obDynamic = result.get("dynamicValue");
+        // Should see both
+        Map<String,Object> dyn = (Map<String,Object>) obDynamic;
+        assertEquals(2, dyn.size());
+        assertEquals(Integer.valueOf(3), dyn.get("x"));
+        assertEquals(Integer.valueOf(5), dyn.get("y"));
+    }
+
+    public void testStaticTypingWithMap() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_STATIC_TYPING, true);
+        ValueMap map = new ValueMap();
+        map.put("a", new ValueClass());
+        assertEquals("{\"a\":{\"x\":3}}", serializeAsString(m, map));
+    }
+
+    public void testStaticTypingWithArrayList() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_STATIC_TYPING, true);
+        ValueList list = new ValueList();
+        list.add(new ValueClass());
+        assertEquals("[{\"x\":3}]", m.writeValueAsString(list));
+    }
+
+    public void testStaticTypingWithLinkedList() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_STATIC_TYPING, true);
+        ValueLinkedList list = new ValueLinkedList();
+        list.add(new ValueClass());
+        assertEquals("[{\"x\":3}]", serializeAsString(m, list));
+    }
+    
+    public void testStaticTypingWithArray() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.USE_STATIC_TYPING, true);
+        ValueInterface[] array = new ValueInterface[] { new ValueClass() };
+        assertEquals("[{\"x\":3}]", serializeAsString(m, array));
+    }
+
+    public void testProblem294() throws Exception
+    {
+        assertEquals("{\"id\":\"fooId\",\"bar\":\"barId\"}",
+                new ObjectMapper().writeValueAsString(new Foo294("fooId", "barId")));
+    }
+    
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java
new file mode 100644
index 0000000..6bd2d6e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize2.java
@@ -0,0 +1,217 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.NullSerializer;
+
+ at SuppressWarnings("serial")
+public class TestJsonSerialize2
+    extends BaseMapTest
+{
+    // [JACKSON-480]
+
+    static class SimpleKey {
+        protected final String key;
+        
+        public SimpleKey(String str) { key = str; }
+        
+        @Override public String toString() { return "toString:"+key; }
+    }
+
+    static class SimpleValue {
+        public final String value;
+        
+        public SimpleValue(String str) { value = str; }
+    }
+
+    @JsonPropertyOrder({"value", "value2"})
+    static class ActualValue extends SimpleValue
+    {
+        public final String other = "123";
+        
+        public ActualValue(String str) { super(str); }
+    }
+
+    static class SimpleKeySerializer extends JsonSerializer<SimpleKey> {
+        @Override
+        public void serialize(SimpleKey key, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException {
+            jgen.writeFieldName("key "+key.key);
+        }
+    }
+
+    static class SimpleValueSerializer extends JsonSerializer<SimpleValue> {
+        @Override
+        public void serialize(SimpleValue value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException {
+            jgen.writeString("value "+value.value);
+        }
+    }
+
+    @JsonSerialize(contentAs=SimpleValue.class)
+    static class SimpleValueList extends ArrayList<ActualValue> { }
+
+    @JsonSerialize(contentAs=SimpleValue.class)
+    static class SimpleValueMap extends HashMap<SimpleKey, ActualValue> { }
+
+    @JsonSerialize(contentUsing=SimpleValueSerializer.class)
+    static class SimpleValueListWithSerializer extends ArrayList<ActualValue> { }
+
+    @JsonSerialize(keyUsing=SimpleKeySerializer.class, contentUsing=SimpleValueSerializer.class)
+    static class SimpleValueMapWithSerializer extends HashMap<SimpleKey, ActualValue> { }
+    
+    static class ListWrapperSimple
+    {
+        @JsonSerialize(contentAs=SimpleValue.class)
+        public final ArrayList<ActualValue> values = new ArrayList<ActualValue>();
+        
+        public ListWrapperSimple(String value) {
+            values.add(new ActualValue(value));
+        }
+    }
+
+    static class ListWrapperWithSerializer
+    {
+        @JsonSerialize(contentUsing=SimpleValueSerializer.class)
+        public final ArrayList<ActualValue> values = new ArrayList<ActualValue>();
+        
+        public ListWrapperWithSerializer(String value) {
+            values.add(new ActualValue(value));
+        }
+    }
+    
+    static class MapWrapperSimple
+    {
+        @JsonSerialize(contentAs=SimpleValue.class)
+        public final HashMap<SimpleKey, ActualValue> values = new HashMap<SimpleKey, ActualValue>();
+        
+        public MapWrapperSimple(String key, String value) {
+            values.put(new SimpleKey(key), new ActualValue(value));
+        }
+    }
+
+    static class MapWrapperWithSerializer
+    {
+        @JsonSerialize(keyUsing=SimpleKeySerializer.class, contentUsing=SimpleValueSerializer.class)
+        public final HashMap<SimpleKey, ActualValue> values = new HashMap<SimpleKey, ActualValue>();
+        
+        public MapWrapperWithSerializer(String key, String value) {
+            values.put(new SimpleKey(key), new ActualValue(value));
+        }
+    }
+
+    static class NullBean
+    {
+        @JsonSerialize(using=NullSerializer.class)
+        public String value = "abc";
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    // [JACKSON-480], test value annotation applied to List value class
+    public void testSerializedAsListWithClassAnnotations() throws IOException
+    {
+        SimpleValueList list = new SimpleValueList();
+        list.add(new ActualValue("foo"));
+        assertEquals("[{\"value\":\"foo\"}]", MAPPER.writeValueAsString(list));
+    }
+
+    // [JACKSON-480], test value annotation applied to Map value class
+    public void testSerializedAsMapWithClassAnnotations() throws IOException
+    {
+        SimpleValueMap map = new SimpleValueMap();
+        map.put(new SimpleKey("x"), new ActualValue("y"));
+        assertEquals("{\"toString:x\":{\"value\":\"y\"}}", MAPPER.writeValueAsString(map));
+    }
+
+    // [JACKSON-480], test Serialization annotation with List
+    public void testSerializedAsListWithClassSerializer() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        SimpleValueListWithSerializer list = new SimpleValueListWithSerializer();
+        list.add(new ActualValue("foo"));
+        assertEquals("[\"value foo\"]", m.writeValueAsString(list));
+    }
+
+    // [JACKSON-480], test annotations when applied to List property (getter, setter)
+    public void testSerializedAsListWithPropertyAnnotations() throws IOException
+    {
+        ListWrapperSimple input = new ListWrapperSimple("bar");
+        assertEquals("{\"values\":[{\"value\":\"bar\"}]}", MAPPER.writeValueAsString(input));
+    }
+    
+    // [JACKSON-480], test Serialization annotation with Map
+    public void testSerializedAsMapWithClassSerializer() throws IOException
+    {
+        SimpleValueMapWithSerializer map = new SimpleValueMapWithSerializer();
+        map.put(new SimpleKey("abc"), new ActualValue("123"));
+        assertEquals("{\"key abc\":\"value 123\"}", MAPPER.writeValueAsString(map));
+    }
+
+    // [JACKSON-480], test annotations when applied to Map property (getter, setter)
+    public void testSerializedAsMapWithPropertyAnnotations() throws IOException
+    {
+        MapWrapperSimple input = new MapWrapperSimple("a", "b");
+        assertEquals("{\"values\":{\"toString:a\":{\"value\":\"b\"}}}",
+                MAPPER.writeValueAsString(input));
+    }
+    
+    public void testSerializedAsListWithPropertyAnnotations2() throws IOException
+    {
+        ListWrapperWithSerializer input = new ListWrapperWithSerializer("abc");
+        assertEquals("{\"values\":[\"value abc\"]}", MAPPER.writeValueAsString(input));
+    }
+
+    public void testSerializedAsMapWithPropertyAnnotations2() throws IOException
+    {
+        MapWrapperWithSerializer input = new MapWrapperWithSerializer("foo", "b");
+        assertEquals("{\"values\":{\"key foo\":\"value b\"}}", MAPPER.writeValueAsString(input));
+    }
+
+    // [JACKSON-602]: Include.NON_EMPTY
+    public void testEmptyInclusion() throws IOException
+    {
+        ObjectMapper defMapper = MAPPER;
+        ObjectMapper inclMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+
+        StringWrapper str = new StringWrapper("");
+        assertEquals("{\"str\":\"\"}", defMapper.writeValueAsString(str));
+        assertEquals("{}", inclMapper.writeValueAsString(str));
+        assertEquals("{}", inclMapper.writeValueAsString(new StringWrapper()));
+
+        ListWrapper<String> list = new ListWrapper<String>();
+        assertEquals("{\"list\":[]}", defMapper.writeValueAsString(list));
+        assertEquals("{}", inclMapper.writeValueAsString(list));
+        assertEquals("{}", inclMapper.writeValueAsString(new ListWrapper<String>()));
+
+        MapWrapper<String,Integer> map = new MapWrapper<String,Integer>(new HashMap<String,Integer>());
+        assertEquals("{\"map\":{}}", defMapper.writeValueAsString(map));
+        assertEquals("{}", inclMapper.writeValueAsString(map));
+        assertEquals("{}", inclMapper.writeValueAsString(new MapWrapper<String,Integer>(null)));
+
+        ArrayWrapper<Integer> array = new ArrayWrapper<Integer>(new Integer[0]);
+        assertEquals("{\"array\":[]}", defMapper.writeValueAsString(array));
+        assertEquals("{}", inclMapper.writeValueAsString(array));
+        assertEquals("{}", inclMapper.writeValueAsString(new ArrayWrapper<Integer>(null)));
+    }
+
+    // [JACKSON-699]
+    public void testNullSerializer() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new NullBean());
+        assertEquals("{\"value\":null}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize3.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize3.java
new file mode 100644
index 0000000..9512acf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerialize3.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class TestJsonSerialize3 extends BaseMapTest
+{
+    // [JACKSON-829]
+    static class FooToBarSerializer extends JsonSerializer<String> {
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider)
+               throws IOException {
+            if ("foo".equals(value)) {
+                jgen.writeString("bar");
+            } else {
+                jgen.writeString(value);
+            }
+        }
+    }
+
+    static class MyObject {
+        @JsonSerialize(contentUsing = FooToBarSerializer.class)
+        List<String> list;
+    }    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testCustomContentSerializer() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        MyObject object = new MyObject();
+        object.list = Arrays.asList("foo");
+        String json = m.writeValueAsString(object);
+        assertEquals("{\"list\":[\"bar\"]}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerializeAs.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerializeAs.java
new file mode 100644
index 0000000..00deb57
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonSerializeAs.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+public class TestJsonSerializeAs extends BaseMapTest
+{
+    // [JACKSON-799] stuff:
+    
+    public interface Fooable {
+        public int getFoo();
+    }
+
+    // force use of interface
+    @JsonSerialize(as=Fooable.class)
+    public static class FooImpl implements Fooable {
+        @Override
+        public int getFoo() { return 42; }
+        public int getBar() { return 15; }
+    }
+
+    public class Fooables {
+        public FooImpl[] getFoos() {
+            return new FooImpl[] { new FooImpl() };
+        }
+    }
+
+    public class FooableWrapper {
+        public FooImpl getFoo() {
+            return new FooImpl();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final ObjectWriter WRITER = objectWriter();
+    
+    // [JACKSON-799]
+    public void testSerializeAsInClass() throws IOException
+    {
+        assertEquals("{\"foo\":42}", WRITER.writeValueAsString(new FooImpl()));
+    }
+
+    public void testSerializeAsForArrayProp() throws IOException
+    {
+        assertEquals("{\"foos\":[{\"foo\":42}]}", WRITER.writeValueAsString(new Fooables()));
+    }
+
+    public void testSerializeAsForSimpleProp() throws IOException
+    {
+        assertEquals("{\"foo\":{\"foo\":42}}", WRITER.writeValueAsString(new FooableWrapper()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java
new file mode 100644
index 0000000..694d6e7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestJsonValue.java
@@ -0,0 +1,254 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+/**
+ * This unit test suite tests functioning of {@link JsonValue}
+ * annotation with bean serialization.
+ */
+public class TestJsonValue
+    extends BaseMapTest
+{
+    /*
+    /*********************************************************
+    /* Helper bean classes
+    /*********************************************************
+     */
+
+    static class ValueClass<T>
+    {
+        final T _value;
+
+        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 "???"; }
+    }
+
+    /**
+     * Another test class to check that it is also possible to
+     * force specific serializer to use with @JsonValue annotated
+     * method. Difference is between Integer serialization, and
+     * conversion to a Json String.
+     */
+    final static class ToStringValueClass<T>
+        extends ValueClass<T>
+    {
+        public ToStringValueClass(T value) { super(value); }
+
+        // Also, need to use this annotation to help
+        @JsonSerialize(using=ToStringSerializer.class)
+        @Override
+        @JsonValue T value() { return super.value(); }
+    }
+
+    final static class ToStringValueClass2
+        extends ValueClass<String>
+    {
+        public ToStringValueClass2(String value) { super(value); }
+
+        /* Simple as well, but let's ensure that other getters won't matter...
+         */
+
+        @JsonProperty int getFoobar() { return 4; }
+
+        public String[] getSomethingElse() { return new String[] { "1", "a" }; }
+    }
+
+    static class ValueBase {
+        public String a = "a";
+    }
+
+    static class ValueType extends ValueBase {
+        public String b = "b";
+    }
+    
+    // Finally, let's also test static vs dynamic type
+    static class ValueWrapper {
+        @JsonValue
+        public ValueBase getX() { return new ValueType(); }
+    }
+
+    static class MapBean
+    {
+        @JsonValue
+        public Map<String,String> toMap()
+        {
+            HashMap<String,String> map = new HashMap<String,String>();
+            map.put("a", "1");
+            return map;
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class MapAsNumber extends HashMap<String,String>
+    {
+        @JsonValue
+        public int value() { return 42; }
+    }
+
+    @SuppressWarnings("serial")
+    static class ListAsNumber extends ArrayList<Integer>
+    {
+        @JsonValue
+        public int value() { return 13; }
+    }
+
+    static class IntExtBean {
+        public List<Internal> values = new ArrayList<Internal>();
+        
+        public void add(int v) { values.add(new Internal(v)); }
+    }
+    
+    static class Internal {
+        public int value;
+        
+        public Internal(int v) { value = v; }
+        
+        @JsonValue
+        public External asExternal() { return new External(this); }
+    }
+    
+    static class External {
+        public int i;
+        
+        External(Internal e) { i = e.value; }
+    }
+
+    // [Issue#167]
+    
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "boingo")
+    @JsonSubTypes(value = {@JsonSubTypes.Type(name = "boopsy", value = AdditionInterfaceImpl.class) })
+    static interface AdditionInterface
+    {
+    	public int add(int in);
+    }
+	
+    public static class AdditionInterfaceImpl implements AdditionInterface
+    {
+	    private final int toAdd;
+	
+	    @JsonCreator
+	    public AdditionInterfaceImpl(@JsonProperty("toAdd") int toAdd) {
+	      this.toAdd = toAdd;
+	    }
+	
+	    @JsonProperty
+	    public int getToAdd() {
+	      return toAdd;
+	    }
+	
+	    @Override
+	    public int add(int in) {
+	      return in + toAdd;
+	    }
+    }
+	
+    public static class NegatingAdditionInterface implements AdditionInterface
+    {
+	    final AdditionInterface delegate;
+	
+	    public NegatingAdditionInterface(AdditionInterface delegate) {
+	    	this.delegate = delegate;
+	    }
+	
+	    @Override
+	    public int add(int in) {
+	      return delegate.add(-in);
+	    }
+	
+	    @JsonValue
+	    public AdditionInterface getDelegate() {
+	      return delegate;
+	    }
+    }
+    
+    /*
+    /*********************************************************
+    /* Test cases
+    /*********************************************************
+     */
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testSimpleJsonValue() throws Exception
+    {
+        String result = MAPPER.writeValueAsString(new ValueClass<String>("abc"));
+        assertEquals("\"abc\"", result);
+    }
+
+    public void testJsonValueWithUseSerializer() throws Exception
+    {
+        String result = serializeAsString(MAPPER, new ToStringValueClass<Integer>(Integer.valueOf(123)));
+        assertEquals("\"123\"", result);
+    }
+
+    /**
+     * Test for verifying that additional getters won't confuse serializer.
+     */
+    public void testMixedJsonValue() throws Exception
+    {
+        String result = serializeAsString(MAPPER, new ToStringValueClass2("xyz"));
+        assertEquals("\"xyz\"", result);
+    }
+
+    public void testValueWithStaticType() throws Exception
+    {
+        // Ok; first, with dynamic type:
+        assertEquals("{\"a\":\"a\",\"b\":\"b\"}", MAPPER.writeValueAsString(new ValueWrapper()));
+
+        // then static
+        ObjectMapper staticMapper = new ObjectMapper();
+        staticMapper.configure(MapperFeature.USE_STATIC_TYPING, true);
+        assertEquals("{\"a\":\"a\"}", staticMapper.writeValueAsString(new ValueWrapper()));
+    }
+
+    public void testMapWithJsonValue() throws Exception {
+        assertEquals("{\"a\":\"1\"}", MAPPER.writeValueAsString(new MapBean()));
+    }
+
+    public void testWithMap() throws Exception {
+        assertEquals("42", MAPPER.writeValueAsString(new MapAsNumber()));
+
+    }
+
+    public void testWithList() throws Exception {
+        assertEquals("13", MAPPER.writeValueAsString(new ListAsNumber()));
+    }
+
+    public void testInList() throws Exception {
+        IntExtBean bean = new IntExtBean();
+        bean.add(1);
+        bean.add(2);
+        String json = MAPPER.writeValueAsString(bean);
+        assertEquals(json, "{\"values\":[{\"i\":1},{\"i\":2}]}");
+    }
+
+    // [Issue#167]
+    public void testPolymorphicSerdeWithDelegate() throws Exception
+    {
+	    AdditionInterface adder = new AdditionInterfaceImpl(1);
+	
+	    assertEquals(2, adder.add(1));
+	    String json = MAPPER.writeValueAsString(adder);
+	    assertEquals("{\"boingo\":\"boopsy\",\"toAdd\":1}", json);
+	    assertEquals(2, MAPPER.readValue(json, AdditionInterface.class).add(1));
+	
+	    adder = new NegatingAdditionInterface(adder);
+	    assertEquals(0, adder.add(1));
+	    json = MAPPER.writeValueAsString(adder);
+	    
+	    assertEquals("{\"boingo\":\"boopsy\",\"toAdd\":1}", json);
+	    assertEquals(2, MAPPER.readValue(json, AdditionInterface.class).add(1));
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
new file mode 100644
index 0000000..2e8e25e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestKeySerializers.java
@@ -0,0 +1,64 @@
+package com.fasterxml.jackson.databind.ser;
+
+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;
+
+public class TestKeySerializers extends BaseMapTest
+{
+    public static class KarlSerializer extends JsonSerializer<String>
+    {
+        @Override
+        public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
+            jgen.writeFieldName("Karl");
+        }
+    }
+
+    public static class NotKarlBean
+    {
+        public Map<String,Integer> map = new HashMap<String,Integer>();
+        {
+            map.put("Not Karl", 1);
+        }
+    }
+
+    public static class KarlBean
+    {
+        @JsonSerialize(keyUsing = KarlSerializer.class)
+        public Map<String,Integer> map = new HashMap<String,Integer>();
+        {
+            map.put("Not Karl", 1);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testNotKarl() throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+        final String serialized = mapper.writeValueAsString(new NotKarlBean());
+        assertEquals("{\"map\":{\"Not Karl\":1}}", serialized);
+    }
+
+    public void testKarl() throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+        final String serialized = mapper.writeValueAsString(new KarlBean());
+        assertEquals("{\"map\":{\"Karl\":1}}", serialized);
+    }
+
+    // [Issue#75]: caching of KeySerializers
+    public void testBoth() throws IOException
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        final String value1 = mapper.writeValueAsString(new NotKarlBean());
+        assertEquals("{\"map\":{\"Not Karl\":1}}", value1);
+        final String value2 = mapper.writeValueAsString(new KarlBean());
+        assertEquals("{\"map\":{\"Karl\":1}}", value2);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
new file mode 100644
index 0000000..ae03ae2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestMapSerialization.java
@@ -0,0 +1,128 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+ at SuppressWarnings("serial")
+public class TestMapSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Class needed for testing [JACKSON-220]
+     */
+    @JsonSerialize(using=MapSerializer.class)    
+    static class PseudoMap extends LinkedHashMap<String,String>
+    {
+        public PseudoMap(String... values) {
+            for (int i = 0, len = values.length; i < len; i += 2) {
+                put(values[i], values[i+1]);
+            }
+        }
+    }
+
+    static class MapSerializer extends JsonSerializer<Map<String,String>>
+    {
+        @Override
+        public void serialize(Map<String,String> value,
+                              JsonGenerator jgen,
+                              SerializerProvider provider)
+            throws IOException
+        {
+            // just use standard Map.toString(), output as JSON String
+            jgen.writeString(value.toString());
+        }
+    }
+
+    // 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);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    final ObjectMapper MAPPER = new ObjectMapper();
+    
+    // Test [JACKSON-220]
+    public void testMapSerializer() throws IOException
+    {
+        assertEquals("\"{a=b, c=d}\"", MAPPER.writeValueAsString(new PseudoMap("a", "b", "c", "d")));
+    }
+
+    // Test [JACKSON-314]
+    public void testMapNullSerialization() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,String> map = new HashMap<String,String>();
+        map.put("a", null);
+        // 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);
+        assertEquals("{}", m.writeValueAsString(map));
+    }
+
+    // [JACKSON-499], problems with map entries, values
+    public void testMapKeyValueSerialization() throws IOException
+    {
+        Map<String,String> map = new HashMap<String,String>();
+        map.put("a", "b");
+        assertEquals("[\"a\"]", MAPPER.writeValueAsString(map.keySet()));
+        assertEquals("[\"b\"]", MAPPER.writeValueAsString(map.values()));
+
+        // TreeMap has similar inner class(es):
+        map = new TreeMap<String,String>();
+        map.put("c", "d");
+        assertEquals("[\"c\"]", MAPPER.writeValueAsString(map.keySet()));
+        assertEquals("[\"d\"]", MAPPER.writeValueAsString(map.values()));
+
+        // and for [JACKSON-533], same for concurrent maps
+        map = new ConcurrentHashMap<String,String>();
+        map.put("e", "f");
+        assertEquals("[\"e\"]", MAPPER.writeValueAsString(map.keySet()));
+        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));
+    }
+
+    // [JACKSON-636]: sort Map entries by key
+    public void testOrderByKey() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertFalse(m.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS));
+        LinkedHashMap<String,Integer> map = new LinkedHashMap<String,Integer>();
+        map.put("b", 3);
+        map.put("a", 6);
+        // by default, no (re)ordering:
+        assertEquals("{\"b\":3,\"a\":6}", m.writeValueAsString(map));
+        // but can be changed
+        assertEquals("{\"a\":6,\"b\":3}", m.writer(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS).writeValueAsString(map));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestNullProperties.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestNullProperties.java
new file mode 100644
index 0000000..4107960
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestNullProperties.java
@@ -0,0 +1,146 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * Unit tests for checking that alternative settings for
+ * {@link JsonSerialize#include} annotation property work
+ * as expected.
+ */
+public class TestNullProperties
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper beans
+    /**********************************************************
+     */
+
+    static class SimpleBean
+    {
+        public String getA() { return "a"; }
+        public String getB() { return null; }
+    }
+    
+    @JsonSerialize(include=JsonSerialize.Inclusion.ALWAYS) // just to ensure default
+    static class NoNullsBean
+    {
+        @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+        public String getA() { return null; }
+
+        public String getB() { return null; }
+    }
+
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
+    static class NonDefaultBean
+    {
+        String _a = "a", _b = "b";
+
+        NonDefaultBean() { }
+
+        public String getA() { return _a; }
+        public String getB() { return _b; }
+    }
+
+    static class MixedBean
+    {
+        String _a = "a", _b = "b";
+
+        MixedBean() { }
+
+        @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
+        public String getA() { return _a; }
+
+        @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+        public String getB() { return _b; }
+    }
+
+    // to ensure that default values work for collections as well
+    static class ListBean {
+        @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
+        public List<String> strings = new ArrayList<String>();
+    }
+    
+    @JsonSerialize(include=JsonSerialize.Inclusion.NON_DEFAULT)
+    static class ArrayBean {
+        public int[] ints = new int[] { 1, 2 };
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testGlobal() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new SimpleBean());
+        assertEquals(2, result.size());
+        assertEquals("a", result.get("a"));
+        assertNull(result.get("b"));
+        assertTrue(result.containsKey("b"));
+    }
+
+    public void testNonNullByClass() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new NoNullsBean());
+        assertEquals(1, result.size());
+        assertFalse(result.containsKey("a"));
+        assertNull(result.get("a"));
+        assertTrue(result.containsKey("b"));
+        assertNull(result.get("b"));
+    }
+
+    public void testNonDefaultByClass() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        NonDefaultBean bean = new NonDefaultBean();
+        // need to change one of defaults
+        bean._a = "notA";
+        Map<String,Object> result = writeAndMap(m, bean);
+        assertEquals(1, result.size());
+        assertTrue(result.containsKey("a"));
+        assertEquals("notA", result.get("a"));
+        assertFalse(result.containsKey("b"));
+        assertNull(result.get("b"));
+    }
+
+    public void testMixedMethod() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+
+        MixedBean bean = new MixedBean();
+        bean._a = "xyz";
+        bean._b = null;
+        Map<String,Object> result = writeAndMap(m, bean);
+        assertEquals(1, result.size());
+        assertEquals("xyz", result.get("a"));
+        assertFalse(result.containsKey("b"));
+
+        bean._a = "a";
+        bean._b = "b";
+        result = writeAndMap(m, bean);
+        assertEquals(1, result.size());
+        assertEquals("b", result.get("b"));
+        assertFalse(result.containsKey("a"));
+    }
+
+    public void testDefaultForEmptyList() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals("{}", m.writeValueAsString(new ListBean()));
+    }
+
+    // [JACKSON-531]: make NON_DEFAULT work for arrays too
+    public void testNonEmptyDefaultArray() throws IOException
+    {
+        ObjectMapper m = new ObjectMapper();
+        assertEquals("{}", m.writeValueAsString(new ArrayBean()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestNullSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestNullSerialization.java
new file mode 100644
index 0000000..81a7d0a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestNullSerialization.java
@@ -0,0 +1,80 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+
+public class TestNullSerialization
+    extends BaseMapTest
+{
+    static class NullSerializer extends JsonSerializer<Object>
+    {
+        @Override
+        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+        {
+            jgen.writeString("foobar");
+        }
+    }
+
+    static class Bean1 {
+        public String name = null;
+    }
+
+    static class Bean2 {
+        public String type = null;
+    }
+    
+    @SuppressWarnings("serial")
+    static class MyNullProvider extends DefaultSerializerProvider
+    {
+        public MyNullProvider() { super(); }
+        public MyNullProvider(MyNullProvider base, SerializationConfig config, SerializerFactory jsf) {
+            super(base, config, jsf);
+        }
+        
+        @Override
+        public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) {
+            return new MyNullProvider(this, config, jsf);
+        }
+        
+        @Override
+        public JsonSerializer<Object> findNullValueSerializer(BeanProperty property)
+            throws JsonMappingException
+        {
+            if ("name".equals(property.getName())) {
+                return new NullSerializer();
+            }
+            return super.findNullValueSerializer(property);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testSimple() throws Exception
+    {
+        assertEquals("null", new ObjectMapper().writeValueAsString(null));
+    }
+
+    public void testOverriddenDefaultNulls() throws Exception
+    {
+        DefaultSerializerProvider sp = new DefaultSerializerProvider.Impl();
+        sp.setNullValueSerializer(new NullSerializer());
+        ObjectMapper m = new ObjectMapper();
+        m.setSerializerProvider(sp);
+        assertEquals("\"foobar\"", m.writeValueAsString(null));
+    }
+
+    public void testCustomNulls() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.setSerializerProvider(new MyNullProvider());
+        assertEquals("{\"name\":\"foobar\"}", m.writeValueAsString(new Bean1()));
+        assertEquals("{\"type\":null}", m.writeValueAsString(new Bean2()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java
new file mode 100644
index 0000000..9883921
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestObjectWriter.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.PrettyPrinter;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for checking features added to {@link ObjectWriter}, such
+ * as adding of explicit pretty printer.
+ */
+public class TestObjectWriter
+    extends BaseMapTest
+{
+    public void testPrettyPrinter() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectWriter writer = mapper.writer();
+        HashMap<String, Integer> data = new HashMap<String,Integer>();
+        data.put("a", 1);
+        
+        // default: no indentation
+        assertEquals("{\"a\":1}", writer.writeValueAsString(data));
+
+        // and then with standard
+        writer = writer.withDefaultPrettyPrinter();
+
+        // pretty printer uses system-specific line feeds, so we do that as well.
+        String lf = System.getProperty("line.separator");
+        assertEquals("{" + lf + "  \"a\" : 1" + lf + "}", writer.writeValueAsString(data));
+
+        // and finally, again without indentation
+        writer = writer.with((PrettyPrinter) null);
+        assertEquals("{\"a\":1}", writer.writeValueAsString(data));
+    }
+
+    public void testPrefetch() throws Exception
+    {
+        ObjectWriter writer = objectWriter();
+        assertFalse(writer.hasPrefetchedSerializer());
+        writer = objectWriter().withType(String.class);
+        assertTrue(writer.hasPrefetchedSerializer());
+    }
+} 
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
new file mode 100644
index 0000000..e0e3122
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestRootType.java
@@ -0,0 +1,185 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.io.StringWriter;
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Unit tests for verifying functioning of [JACKSON-195], ability to
+ * force specific root type for serialization (super type of value)
+ */
+public class TestRootType
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    interface BaseInterface {
+        int getB();
+    }
+    
+    static class BaseType
+        implements BaseInterface
+    {
+        public String a = "a";
+
+        @Override
+        public int getB() { return 3; }
+    }
+
+    static class SubType extends BaseType {
+        public String a2 = "x";
+        
+        public boolean getB2() { return true; }
+    }
+
+    @JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="beanClass")
+    public abstract static class BaseClass398 { }
+
+    public static class TestClass398 extends BaseClass398 {
+       public String property = "aa";
+    }
+    
+    @JsonRootName("root")
+    static class WithRootName {
+        public int a = 3;
+    }
+
+    /*
+    /**********************************************************
+    /* Main tests
+    /**********************************************************
+     */
+    
+    @SuppressWarnings("unchecked")
+    public void testSuperClass() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SubType bean = new SubType();
+
+        // first, test with dynamically detected type
+        Map<String,Object> result = writeAndMap(mapper, bean);
+        assertEquals(4, result.size());
+        assertEquals("a", result.get("a"));
+        assertEquals(Integer.valueOf(3), result.get("b"));
+        assertEquals("x", result.get("a2"));
+        assertEquals(Boolean.TRUE, result.get("b2"));
+
+        // and then using specified typed writer
+        ObjectWriter w = mapper.writerWithType(BaseType.class);
+        String json = w.writeValueAsString(bean);
+        result = (Map<String,Object>)mapper.readValue(json, Map.class);
+        assertEquals(2, result.size());
+        assertEquals("a", result.get("a"));
+        assertEquals(Integer.valueOf(3), result.get("b"));
+    }
+
+    public void testSuperInterface() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SubType bean = new SubType();
+
+        // let's constrain by interface:
+        ObjectWriter w = mapper.writerWithType(BaseInterface.class);
+        String json = w.writeValueAsString(bean);
+        @SuppressWarnings("unchecked")
+        Map<String,Object> result = mapper.readValue(json, Map.class);
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(3), result.get("b"));
+    }
+
+    public void testInArray() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // must force static typing, otherwise won't matter a lot
+        mapper.configure(MapperFeature.USE_STATIC_TYPING, true);
+        SubType[] ob = new SubType[] { new SubType() };
+        String json = mapper.writerWithType(BaseInterface[].class).writeValueAsString(ob);
+        // should propagate interface type through due to root declaration; static typing
+        assertEquals("[{\"b\":3}]", json);
+    }
+    
+    /**
+     * Unit test to ensure that proper exception is thrown if declared
+     * root type is not compatible with given value instance.
+     */
+    public void testIncompatibleRootType() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SubType bean = new SubType();
+
+        // and then let's try using incompatible type
+        ObjectWriter w = mapper.writerWithType(HashMap.class);
+        try {
+            w.writeValueAsString(bean);
+            fail("Should have failed due to incompatible type");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Incompatible types");
+        }
+    }
+    
+    /**
+     * Unit test to verify [JACKSON-398]
+     */
+    public void testJackson398() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        JavaType collectionType = TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, BaseClass398.class);
+        List<TestClass398> typedList = new ArrayList<TestClass398>();
+        typedList.add(new TestClass398());
+
+        final String EXP = "[{\"beanClass\":\"TestRootType$TestClass398\",\"property\":\"aa\"}]";
+        
+        // First simplest way:
+        String json = mapper.writerWithType(collectionType).writeValueAsString(typedList);
+        assertEquals(EXP, json);
+
+        StringWriter out = new StringWriter();
+        JsonFactory f = new JsonFactory();
+        mapper.writerWithType(collectionType).writeValue(f.createGenerator(out), typedList);
+
+        assertEquals(EXP, out.toString());
+    }
+
+    // Test to verify [JACKSON-163]
+    public void testRootWrapping() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
+        String json = mapper.writeValueAsString(new StringWrapper("abc"));
+        assertEquals("{\"StringWrapper\":{\"str\":\"abc\"}}", json);
+    }
+
+    /**
+     * Test to verify that there is support for specifying root type as primitive,
+     * even if wrapper value is passed (there is no way to pass primitive values as
+     * Objects); this to support frameworks that may pass unprocessed
+     * {@link java.lang.reflect.Type} from field or method.
+     */
+    public void testIssue456WrapperPart() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("123", mapper.writerWithType(Integer.TYPE).writeValueAsString(Integer.valueOf(123)));
+        assertEquals("456", mapper.writerWithType(Long.TYPE).writeValueAsString(Long.valueOf(456L)));
+    }
+
+    // [JACKSON-630] also, allow annotation to define root name
+    public void testRootNameAnnotation() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
+        String json = mapper.writeValueAsString(new WithRootName());
+        assertEquals("{\"root\":{\"a\":3}}", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java
new file mode 100644
index 0000000..8b269ae
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializationOrder.java
@@ -0,0 +1,119 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying that constraints on ordering of serialized
+ * properties are held.
+ */
+public class TestSerializationOrder
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    static class BeanWithCreator
+    {
+        public int a;
+        public int b;
+        public int c;
+
+        @JsonCreator public BeanWithCreator(@JsonProperty("c") int c, @JsonProperty("a") int a) {
+            this.a = a;
+            this.c = c;
+        }
+    }
+
+    @JsonPropertyOrder({"c", "a", "b"})
+    static class BeanWithOrder
+    {
+        public int d, b, a, c;
+        
+        public BeanWithOrder(int a, int b, int c, int d) {
+            this.a = a;
+            this.b = b;
+            this.c = c;
+            this.d = d;
+        }
+    }
+
+    @JsonPropertyOrder(value={"d"}, alphabetic=true)
+    static class SubBeanWithOrder extends BeanWithOrder
+    {
+        public SubBeanWithOrder(int a, int b, int c, int d) {
+            super(a, b, c, d);
+        }
+    }
+
+    @JsonPropertyOrder({"b", "a",
+        // note: including non-existant properties is fine (has no effect, but not an error)
+        "foobar",
+        "c"
+    })
+    static class OrderMixIn { }
+
+    @JsonPropertyOrder(value={"a","b","x","z"})
+    static class BeanFor268 { // testing [JACKSON-268]
+    	@JsonProperty("a") public String xA = "a";
+    	@JsonProperty("z") public String aZ = "z";
+    	@JsonProperty("b") public String xB() { return "b"; }
+    	@JsonProperty("x") public String aX() { return "x"; }
+    }
+
+    static class BeanFor459 {
+        public int d = 4;
+        public int c = 3;
+        public int b = 2;
+        public int a = 1;
+    }
+    
+    /*
+    /*********************************************
+    /* Unit tests
+    /*********************************************
+     */
+
+    // Test for [JACKSON-170]
+    public void testImplicitOrderByCreator() throws Exception
+    {
+        assertEquals("{\"c\":1,\"a\":2,\"b\":0}", serializeAsString(new BeanWithCreator(1, 2)));
+    }
+
+    public void testExplicitOrder() throws Exception
+    {
+        assertEquals("{\"c\":3,\"a\":1,\"b\":2,\"d\":4}", serializeAsString(new BeanWithOrder(1, 2, 3, 4)));
+    }
+
+    public void testAlphabeticOrder() throws Exception
+    {
+        assertEquals("{\"d\":4,\"a\":1,\"b\":2,\"c\":3}", serializeAsString(new SubBeanWithOrder(1, 2, 3, 4)));
+    }
+
+
+    public void testOrderWithMixins() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.addMixInAnnotations(BeanWithOrder.class, OrderMixIn.class);
+        assertEquals("{\"b\":2,\"a\":1,\"c\":3,\"d\":4}", serializeAsString(m, new BeanWithOrder(1, 2, 3, 4)));
+    }
+
+    // Test for [JACKSON-268]
+    public void testOrderWrt268() throws Exception
+    {
+        assertEquals("{\"a\":\"a\",\"b\":\"b\",\"x\":\"x\",\"z\":\"z\"}",
+        		serializeAsString(new BeanFor268()));
+    }
+
+    // Test for [JACKSON-459]
+    public void testOrderWithFeature() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        m.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
+        assertEquals("{\"a\":1,\"b\":2,\"c\":3,\"d\":4}", serializeAsString(m, new BeanFor459()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java
new file mode 100644
index 0000000..27624f9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSerializerProvider.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.databind.ser;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
+
+public class TestSerializerProvider
+    extends com.fasterxml.jackson.databind.BaseMapTest
+{
+    static class MyBean {
+        public int getX() { return 3; }
+    }
+
+    public void testFindExplicit() throws JsonMappingException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        SerializationConfig config = mapper.getSerializationConfig();
+        SerializerFactory f = new BeanSerializerFactory(null);
+        DefaultSerializerProvider prov = new DefaultSerializerProvider.Impl().createInstance(config, f);
+
+        // Should have working default key and null key serializers
+        assertNotNull(prov.findKeySerializer(mapper.constructType(String.class), null));
+        assertNotNull(prov.getDefaultNullKeySerializer());
+        assertNotNull(prov.getDefaultNullValueSerializer());
+        // as well as 'unknown type' one (throws exception)
+        assertNotNull(prov.getUnknownTypeSerializer(getClass()));
+        
+        assertTrue(prov.createInstance(config, f).hasSerializerFor(String.class));
+        // call twice to verify it'll be cached (second code path)
+        assertTrue(prov.createInstance(config, f).hasSerializerFor(String.class));
+
+        assertTrue(prov.createInstance(config, f).hasSerializerFor(MyBean.class));
+        assertTrue(prov.createInstance(config, f).hasSerializerFor(MyBean.class));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java
new file mode 100644
index 0000000..a7fb6dd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleAtomicTypes.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.concurrent.atomic.*;
+
+
+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 TestSimpleAtomicTypes
+    extends BaseMapTest
+{
+    public void testAtomicBoolean() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("true", serializeAsString(mapper, new AtomicBoolean(true)));
+        assertEquals("false", serializeAsString(mapper, new AtomicBoolean(false)));
+    }
+
+    public void testAtomicInteger() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("1", serializeAsString(mapper, new AtomicInteger(1)));
+        assertEquals("-9", serializeAsString(mapper, new AtomicInteger(-9)));
+    }
+
+    public void testAtomicLong() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals("0", serializeAsString(mapper, new AtomicLong(0)));
+    }
+
+    public void testAtomicReference() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String[] strs = new String[] { "abc" };
+        assertEquals("[\"abc\"]", serializeAsString(mapper, new AtomicReference<String[]>(strs)));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java
new file mode 100644
index 0000000..5c8561e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestSimpleTypes.java
@@ -0,0 +1,133 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.math.BigInteger;
+
+
+import com.fasterxml.jackson.core.Base64Variants;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static org.junit.Assert.*;
+
+/**
+ * Unit tests for verifying serialization of simple basic non-structured
+ * types; primitives (and/or their wrappers), Strings.
+ */
+public class TestSimpleTypes
+    extends BaseMapTest
+{
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testBoolean() throws Exception
+    {
+        assertEquals("true", serializeAsString(MAPPER, Boolean.TRUE));
+        assertEquals("false", serializeAsString(MAPPER, Boolean.FALSE));
+    }
+
+    public void testBooleanArray() throws Exception
+    {
+        assertEquals("[true,false]", serializeAsString(MAPPER, new boolean[] { true, false} ));
+        assertEquals("[true,false]", serializeAsString(MAPPER, new Boolean[] { Boolean.TRUE, Boolean.FALSE} ));
+    }
+
+    public void testByteArray() throws Exception
+    {
+        byte[] data = { 1, 17, -3, 127, -128 };
+        Byte[] data2 = new Byte[data.length];
+        for (int i = 0; i < data.length; ++i) {
+            data2[i] = data[i]; // auto-boxing
+        }
+        // For this we need to deserialize, to get base64 codec
+        String str1 = serializeAsString(MAPPER, data);
+        String str2 = serializeAsString(MAPPER, data2);
+        assertArrayEquals(data, MAPPER.readValue(str1, byte[].class));
+        assertArrayEquals(data2, MAPPER.readValue(str2, Byte[].class));
+    }
+
+    // as per [Issue#42], allow Base64 variant use as well
+    public void testBase64Variants() throws Exception
+    {
+        final byte[] INPUT = "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890X".getBytes("UTF-8");
+        
+        // default encoding is "MIME, no linefeeds", so:
+        assertEquals(quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="), MAPPER.writeValueAsString(INPUT));
+        assertEquals(quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
+                MAPPER.writer(Base64Variants.MIME_NO_LINEFEEDS).writeValueAsString(INPUT));
+
+        // but others should be slightly different
+        assertEquals(quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1\\ndnd4eXoxMjM0NTY3ODkwWA=="),
+                MAPPER.writer(Base64Variants.MIME).writeValueAsString(INPUT));
+        assertEquals(quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA"), // no padding or LF
+                MAPPER.writer(Base64Variants.MODIFIED_FOR_URL).writeValueAsString(INPUT));
+        // PEM mandates 64 char lines:
+        assertEquals(quote("YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwYWJjZGVmZ2hpamts\\nbW5vcHFyc3R1dnd4eXoxMjM0NTY3ODkwWA=="),
+                MAPPER.writer(Base64Variants.PEM).writeValueAsString(INPUT));
+    }
+    
+    public void testShortArray() throws Exception
+    {
+        assertEquals("[0,1]", serializeAsString(MAPPER, new short[] { 0, 1 }));
+        assertEquals("[2,3]", serializeAsString(MAPPER, new Short[] { 2, 3 }));
+    }
+
+    public void testIntArray() throws Exception
+    {
+        assertEquals("[0,-3]", serializeAsString(MAPPER, new int[] { 0, -3 }));
+        assertEquals("[13,9]", serializeAsString(MAPPER, new Integer[] { 13, 9 }));
+    }
+
+    /* 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 testFloat() 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) {
+           float f = (float) d;
+    	   String expected = String.valueOf(f);
+           if (Float.isNaN(f) || Float.isInfinite(f)) {
+               expected = "\""+expected+"\"";
+       	   }
+           assertEquals(expected,serializeAsString(MAPPER, Float.valueOf(f)));
+        }
+    }
+
+    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 testClass() throws Exception
+    {
+        String result = MAPPER.writeValueAsString(java.util.List.class);
+        assertEquals("\"java.util.List\"", result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestStatics.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestStatics.java
new file mode 100644
index 0000000..1878bcd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestStatics.java
@@ -0,0 +1,62 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * This unit test suite verifies that static fields and methods are
+ * ignored wrt serialization
+ */
+public class TestStatics
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    final static class FieldBean
+    {
+        public int x = 1;
+
+        public static int y = 2;
+
+        // not even @JsonProperty should make statics usable...
+        @JsonProperty public static int z = 3;
+    }
+
+    final static class GetterBean
+    {
+        public int getX() { return 3; }
+
+        public static int getA() { return -3; }
+
+        // not even @JsonProperty should make statics usable...
+        @JsonProperty public static int getFoo() { return 123; }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testStaticFields() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new FieldBean());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(1), result.get("x"));
+    }
+
+    public void testStaticMethods() throws Exception
+    {
+        ObjectMapper m = new ObjectMapper();
+        Map<String,Object> result = writeAndMap(m, new GetterBean());
+        assertEquals(1, result.size());
+        assertEquals(Integer.valueOf(3), result.get("x"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java
new file mode 100644
index 0000000..cc8eb55
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestTreeSerialization.java
@@ -0,0 +1,112 @@
+package com.fasterxml.jackson.databind.ser;
+
+
+import java.io.*;
+import java.util.*;
+
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.node.*;
+
+/**
+ * This unit test suite tries to verify that JsonNode-based trees
+ * can be serialized as expected
+ */
+public class TestTreeSerialization
+    extends BaseMapTest
+{
+    final static class Bean {
+        public String getX() { return "y"; }
+        public int getY() { return 13; }
+    }
+
+    @SuppressWarnings("unchecked")
+	public void testSimpleViaObjectMapper()
+        throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // also need tree mapper to construct tree to serialize
+        ObjectNode n = mapper.getNodeFactory().objectNode();
+        n.put("number", 15);
+        n.put("string", "abc");
+        ObjectNode n2 = n.putObject("ob");
+        n2.putArray("arr");
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = mapper.getFactory().createGenerator(sw);
+        mapper.writeTree(jg, n);
+
+        Map<String,Object> result = (Map<String,Object>) mapper.readValue(sw.toString(), Map.class);
+
+        assertEquals(3, result.size());
+        assertEquals("abc", result.get("string"));
+        assertEquals(Integer.valueOf(15), result.get("number"));
+        Map<String,Object> ob = (Map<String,Object>) result.get("ob");
+        assertEquals(1, ob.size());
+        List<Object> list = (List<Object>) ob.get("arr");
+        assertEquals(0, list.size());
+    }
+
+    /**
+     * Simple test to verify that POJONodes (JsonNode wrapper around
+     * any old Java object) work with serialization
+     */
+    @SuppressWarnings("unchecked")
+	public void testPOJOString()
+        throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // also need tree mapper to construct tree to serialize
+        ObjectNode n = mapper.getNodeFactory().objectNode();
+        n.set("pojo", mapper.getNodeFactory().pojoNode("abc"));
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = mapper.getFactory().createGenerator(sw);
+        mapper.writeTree(jg, n);
+        Map<String,Object> result = (Map<String,Object>) mapper.readValue(sw.toString(), Map.class);
+        assertEquals(1, result.size());
+        assertEquals("abc", result.get("pojo"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testPOJOIntArray()
+        throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode n = mapper.getNodeFactory().objectNode();
+        n.set("pojo", mapper.getNodeFactory().pojoNode(new int[] { 1, 2, 3 }));
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = mapper.getFactory().createGenerator(sw);
+        mapper.writeTree(jg, n);
+
+        Map<String,Object> result = (Map<String,Object>) mapper.readValue(sw.toString(), Map.class);
+
+        assertEquals(1, result.size());
+        // int array becomes a list when mapped to general Object:
+        List<Object> list = (List<Object>) result.get("pojo");
+        assertEquals(3, list.size());
+        for (int i = 0; i < 3; ++i) {
+            assertEquals(Integer.valueOf(i+1), list.get(i));
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testPOJOBean()
+        throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // also need tree mapper to construct tree to serialize
+        ObjectNode n = mapper.getNodeFactory().objectNode();
+        n.set("pojo", mapper.getNodeFactory().pojoNode(new Bean()));
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = mapper.getFactory().createGenerator(sw);
+        mapper.writeTree(jg, n);
+
+        Map<String,Object> result = (Map<String,Object>) mapper.readValue(sw.toString(), Map.class);
+
+        assertEquals(1, result.size());
+        Map<String,Object> bean = (Map<String,Object>) result.get("pojo");
+        assertEquals(2, bean.size());
+        assertEquals("y", bean.get("x"));
+        assertEquals(Integer.valueOf(13), bean.get("y"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java
new file mode 100644
index 0000000..f442e26
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestTypedRootValueSerialization.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestTypedRootValueSerialization extends BaseMapTest
+{
+    // [JACKSON-822]
+    static interface Issue822Interface {
+        public int getA();
+    }
+
+    // If this annotation is added, things will work:
+    //@com.fasterxml.jackson.databind.annotation.JsonSerialize(as=Issue822Interface.class)
+    // but it should not be necessary when root type is passed
+    static class Issue822Impl implements Issue822Interface {
+        @Override
+        public int getA() { return 3; }
+        public int getB() { return 9; }
+    }
+
+    // First ensure that basic interface-override works:
+    public void testTypedSerialization() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String singleJson = mapper.writerWithType(Issue822Interface.class).writeValueAsString(new Issue822Impl());
+        // start with specific value case:
+        assertEquals("{\"a\":3}", singleJson);
+    }
+    
+    // [JACKSON-822]: ensure that type can be coerced
+    public void testTypedArrays() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+// Work-around when real solution not yet implemented:        
+//        mapper.enable(MapperFeature.USE_STATIC_TYPING);
+        assertEquals("[{\"a\":3}]", mapper.writerWithType(Issue822Interface[].class).writeValueAsString(
+                new Issue822Interface[] { new Issue822Impl() }));
+    }
+    
+    // [JACKSON-822]: ensure that type can be coerced
+    public void testTypedLists() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+     // Work-around when real solution not yet implemented:        
+//        mapper.enable(MapperFeature.USE_STATIC_TYPING);
+
+        List<Issue822Interface> list = new ArrayList<Issue822Interface>();
+        list.add(new Issue822Impl());
+        String listJson = mapper.writerWithType(new TypeReference<List<Issue822Interface>>(){})
+                .writeValueAsString(list);
+        assertEquals("[{\"a\":3}]", listJson);
+    }
+
+    public void testTypedMaps() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Issue822Interface> map = new HashMap<String,Issue822Interface>();
+        map.put("a", new Issue822Impl());
+        String listJson = mapper.writerWithType(new TypeReference<Map<String,Issue822Interface>>(){})
+                .writeValueAsString(map);
+        assertEquals("{\"a\":{\"a\":3}}", listJson);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java
new file mode 100644
index 0000000..e62f830
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/ser/TestUntypedSerialization.java
@@ -0,0 +1,103 @@
+package com.fasterxml.jackson.databind.ser;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * This unit test suite tries verify simplest aspects of
+ * "Native" java type mapper; basically that is can properly serialize
+ * core JDK objects to JSON.
+ */
+public class TestUntypedSerialization
+    extends BaseMapTest
+{
+    public void testFromArray()
+        throws Exception
+    {
+        ArrayList<Object> doc = new ArrayList<Object>();
+        doc.add("Elem1");
+        doc.add(Integer.valueOf(3));
+        Map<String,Object> struct = new LinkedHashMap<String, Object>();
+        struct.put("first", Boolean.TRUE);
+        struct.put("Second", new ArrayList<Object>());
+        doc.add(struct);
+        doc.add(Boolean.FALSE);
+
+        ObjectMapper mapper = new ObjectMapper();
+        JsonFactory f =  new JsonFactory();
+
+        // loop more than once, just to ensure caching works ok (during second round)
+        for (int i = 0; i < 3; ++i) {
+            String str = mapper.writeValueAsString(doc);
+            
+            JsonParser jp = f.createParser(str);
+            assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+            
+            assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+            assertEquals("Elem1", getAndVerifyText(jp));
+            
+            assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+            assertEquals(3, jp.getIntValue());
+            
+            assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+            assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+            assertEquals("first", getAndVerifyText(jp));
+            
+            assertEquals(JsonToken.VALUE_TRUE, jp.nextToken());
+            assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+            assertEquals("Second", getAndVerifyText(jp));
+            
+            if (jp.nextToken() != JsonToken.START_ARRAY) {
+                fail("Expected START_ARRAY: JSON == '"+str+"'");
+            }
+            assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+            assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+            
+            assertEquals(JsonToken.VALUE_FALSE, jp.nextToken());
+            
+            assertEquals(JsonToken.END_ARRAY, jp.nextToken());
+            assertNull(jp.nextToken());
+        }
+    }
+
+    public void testFromMap()
+        throws Exception
+    {
+        LinkedHashMap<String,Object> doc = new LinkedHashMap<String,Object>();
+        JsonFactory f =  new JsonFactory();
+
+        doc.put("a1", "\"text\"");
+        doc.put("int", Integer.valueOf(137));
+        doc.put("foo bar", Long.valueOf(1234567890L));
+
+        ObjectMapper mapper = new ObjectMapper();
+        for (int i = 0; i < 3; ++i) {
+            String str = mapper.writeValueAsString(doc);
+            JsonParser jp = f.createParser(str);
+            
+            assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+            
+            assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+            assertEquals("a1", getAndVerifyText(jp));
+            assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+            assertEquals("\"text\"", getAndVerifyText(jp));
+            
+            assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+            assertEquals("int", getAndVerifyText(jp));
+            assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+            assertEquals(137, jp.getIntValue());
+            
+            assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+            assertEquals("foo bar", getAndVerifyText(jp));
+            assertEquals(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+            assertEquals(1234567890L, jp.getLongValue());
+            
+            assertEquals(JsonToken.END_OBJECT, jp.nextToken());
+
+            assertNull(jp.nextToken());
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
new file mode 100644
index 0000000..344f7e4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestFormatForCollections.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestFormatForCollections extends BaseMapTest
+{
+    // [issue#40]: Allow serialization 'as POJO' (resulting in JSON Object) 
+    @JsonPropertyOrder({ "size", "value" })
+    @JsonFormat(shape=Shape.OBJECT)
+    @JsonIgnoreProperties({ "empty" }) // from 'isEmpty()'
+    static class CollectionAsPOJO
+        extends ArrayList<String>
+    {
+        private static final long serialVersionUID = 1L;
+
+        @JsonProperty("size")
+        public int foo() { return size(); }
+        
+        public List<String> getValues() {
+            return new ArrayList<String>(this);
+        }
+
+        public void setValues(List<String> v) {
+            addAll(v);
+        }
+        
+        // bogus setter to handle "size" property
+        public void setSize(int i) { }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();    
+
+
+    // [Issue#40]
+    public void testListAsObject() throws Exception
+    {
+        // First, serialize a "POJO-List"
+        CollectionAsPOJO list = new CollectionAsPOJO();
+        list.add("a");
+        list.add("b");
+        String json = MAPPER.writeValueAsString(list);
+        assertEquals("{\"size\":2,\"values\":[\"a\",\"b\"]}", json);
+
+        // and then bring it back!
+        CollectionAsPOJO result = MAPPER.readValue(json, CollectionAsPOJO.class);
+        assertEquals(2, result.size());
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectId.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectId.java
new file mode 100644
index 0000000..a4dc76c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectId.java
@@ -0,0 +1,139 @@
+package com.fasterxml.jackson.databind.struct;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+// related to [JACKSON-847]
+public class TestObjectId extends BaseMapTest
+{
+    @JsonPropertyOrder({"a", "b"})
+    static class Wrapper {
+        public ColumnMetadata a, b;
+    }
+    
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
+    static class ColumnMetadata {
+      private final String name;
+      private final String type;
+      private final String comment;
+
+      @JsonCreator
+      public ColumnMetadata(
+        @JsonProperty("name") String name,
+        @JsonProperty("type") String type,
+        @JsonProperty("comment") String comment
+      ) {
+        this.name = name;
+        this.type = type;
+        this.comment = comment;
+      }
+
+      @JsonProperty("name")
+      public String getName() {
+        return name;
+      }
+
+      @JsonProperty("type")
+      public String getType() {
+        return type;
+      }
+
+      @JsonProperty("comment")
+      public String getComment() {
+        return comment;
+      }    
+    }
+
+    /* Problem in which always-as-id reference may prevent initial
+     * serialization of a POJO.
+     */
+    
+    static class Company {
+        public List<Employee> employees;
+
+        public void add(Employee e) {
+            if (employees == null) {
+                employees = new ArrayList<Employee>();
+            }
+            employees.add(e);
+        }
+    }
+
+    @JsonIdentityInfo(property="id",
+            generator=ObjectIdGenerators.PropertyGenerator.class)
+    static class Employee {
+        public int id;
+     
+        public String name;
+     
+        @JsonIdentityReference(alwaysAsId=true)
+        public Employee manager;
+
+        @JsonIdentityReference(alwaysAsId=true)
+        public List<Employee> reports;
+    
+        public Employee() { }
+        public Employee(int id, String name, Employee manager) {
+            this.id = id;
+            this.name = name;
+            this.manager = manager;
+            reports = new ArrayList<Employee>();
+        }
+
+        public Employee addReport(Employee e) {
+            reports.add(e);
+            return this;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    
+    public void testColumnMetadata() throws Exception
+    {
+        ColumnMetadata col = new ColumnMetadata("Billy", "employee", "comment");
+        Wrapper w = new Wrapper();
+        w.a = col;
+        w.b = col;
+        String json = MAPPER.writeValueAsString(w);
+        
+        Wrapper deserialized = MAPPER.readValue(json, Wrapper.class);
+        assertNotNull(deserialized);
+        assertNotNull(deserialized.a);
+        assertNotNull(deserialized.b);
+        
+        assertEquals("Billy", deserialized.a.getName());
+        assertEquals("employee", deserialized.a.getType());
+        assertEquals("comment", deserialized.a.getComment());
+
+        assertSame(deserialized.a, deserialized.b);
+    }
+
+    // For Issue#188
+    public void testMixedRefsIssue188() throws Exception
+    {
+        Company comp = new Company();
+        Employee e1 = new Employee(1, "First", null);
+        Employee e2 = new Employee(2, "Second", e1);
+        e1.addReport(e2);
+        comp.add(e1);
+        comp.add(e2);
+
+        String json = MAPPER.writeValueAsString(comp);
+        
+        assertEquals("{\"employees\":["
+                +"{\"id\":1,\"name\":\"First\",\"manager\":null,\"reports\":[2]},"
+                +"{\"id\":2,\"name\":\"Second\",\"manager\":1,\"reports\":[]}"
+                +"]}",
+                json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
new file mode 100644
index 0000000..a65e793
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdDeserialization.java
@@ -0,0 +1,196 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit test to verify handling of Object Id deserialization
+ */
+public class TestObjectIdDeserialization extends BaseMapTest
+{
+    // // Classes for external id use
+    
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
+    static class Identifiable
+    {
+        public int value;
+
+        public Identifiable next;
+        
+        public Identifiable() { this(0); }
+        public Identifiable(int v) {
+            value = v;
+        }
+    }
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="#")
+    static class UUIDNode
+    {
+        public int value;
+        public UUIDNode parent;
+        public UUIDNode first;
+        public UUIDNode second;
+
+        public UUIDNode() { this(0); }
+        public UUIDNode(int v) { value = v; }
+    }
+    
+    // // Classes for external id from property annotations:
+    
+    static class IdWrapper
+    {
+        @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
+        public ValueNode node;
+
+        public IdWrapper() { }
+        public IdWrapper(int v) {
+            node = new ValueNode(v);
+        }
+    }
+
+    static class ValueNode {
+        public int value;
+        public IdWrapper next;
+        
+        public ValueNode() { this(0); }
+        public ValueNode(int v) { value = v; }
+    }
+
+    // // Classes for external id use
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="customId")
+    static class IdentifiableCustom
+    {
+        public int value;
+
+        public int customId;
+        
+        public IdentifiableCustom next;
+        
+        public IdentifiableCustom() { this(-1, 0); }
+        public IdentifiableCustom(int i, int v) {
+            customId = i;
+            value = v;
+        }
+    }
+
+    static class IdWrapperExt
+    {
+        @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class,
+        		property="customId")
+        public ValueNodeExt node;
+
+        public IdWrapperExt() { }
+        public IdWrapperExt(int v) {
+            node = new ValueNodeExt(v);
+        }
+    }
+
+    static class ValueNodeExt
+    {
+        public int value;
+        private int customId;
+        public IdWrapperExt next;
+        
+        public ValueNodeExt() { this(0); }
+        public ValueNodeExt(int v) { value = v; }
+
+        public void setCustomId(int i) {
+        	customId = i;
+        }
+    }
+    
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    /*
+    /*****************************************************
+    /* Unit tests, external id deserialization
+    /*****************************************************
+     */
+
+    private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
+
+    public void testSimpleDeserializationClass() throws Exception
+    {
+        // then bring back...
+        Identifiable result = mapper.readValue(EXP_SIMPLE_INT_CLASS, Identifiable.class);
+        assertEquals(13, result.value);
+        assertSame(result, result.next);
+    }
+
+    public void testSimpleUUIDForClassRoundTrip() throws Exception
+    {
+        UUIDNode root = new UUIDNode(1);
+        UUIDNode child1 = new UUIDNode(2);
+        UUIDNode child2 = new UUIDNode(3);
+        root.first = child1;
+        root.second = child2;
+        child1.parent = root;
+        child2.parent = root;
+        child1.first = child2;
+
+        String json = mapper.writeValueAsString(root);
+
+        // and should come back the same too...
+        UUIDNode result = mapper.readValue(json, UUIDNode.class);
+        assertEquals(1, result.value);
+        UUIDNode result2 = result.first;
+        UUIDNode result3 = result.second;
+        assertNotNull(result2);
+        assertNotNull(result3);
+        assertEquals(2, result2.value);
+        assertEquals(3, result3.value);
+
+        assertSame(result, result2.parent);
+        assertSame(result, result3.parent);
+        assertSame(result3, result2.first);
+    }
+
+    // Bit more complex, due to extra wrapping etc:
+    private final static String EXP_SIMPLE_INT_PROP = "{\"node\":{\"@id\":1,\"value\":7,\"next\":{\"node\":1}}}";
+        
+    public void testSimpleDeserializationProperty() throws Exception
+    {
+        IdWrapper result = mapper.readValue(EXP_SIMPLE_INT_PROP, IdWrapper.class);
+        assertEquals(7, result.node.value);
+        assertSame(result.node, result.node.next.node);
+    }
+
+    // Another test to ensure ordering is not required (i.e. can do front references)
+    public void testSimpleDeserWithForwardRefs() throws Exception
+    {
+        IdWrapper result = mapper.readValue("{\"node\":{\"value\":7,\"next\":{\"node\":1}, \"@id\":1}}"
+                ,IdWrapper.class);
+        assertEquals(7, result.node.value);
+        assertSame(result.node, result.node.next.node);
+    }
+    
+    /*
+    /*****************************************************
+    /* Unit tests, custom (property-based) id deserialization
+    /*****************************************************
+     */
+
+    private final static String EXP_CUSTOM_VIA_CLASS = "{\"customId\":123,\"value\":-900,\"next\":123}";
+
+    public void testCustomDeserializationClass() throws Exception
+    {
+        // then bring back...
+        IdentifiableCustom result = mapper.readValue(EXP_CUSTOM_VIA_CLASS, IdentifiableCustom.class);
+        assertEquals(-900, result.value);
+        assertSame(result, result.next);
+    }
+
+    private final static String EXP_CUSTOM_VIA_PROP = "{\"node\":{\"customId\":3,\"value\":99,\"next\":{\"node\":3}}}";
+    
+    public void testCustomDeserializationProperty() throws Exception
+    {
+        // then bring back...
+    	IdWrapperExt result = mapper.readValue(EXP_CUSTOM_VIA_PROP, IdWrapperExt.class);
+        assertEquals(99, result.node.value);
+        assertSame(result.node, result.node.next.node);
+        assertEquals(3, result.node.customId);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdSerialization.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdSerialization.java
new file mode 100644
index 0000000..c6d2f51
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdSerialization.java
@@ -0,0 +1,261 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonIdentityReference;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit test to verify handling of Object Id deserialization
+ */
+public class TestObjectIdSerialization extends BaseMapTest
+{
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
+    static class Identifiable
+    {
+        public int value;
+
+        public Identifiable next;
+        
+        public Identifiable() { this(0); }
+        public Identifiable(int v) {
+            value = v;
+        }
+    }
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="customId")
+    static class IdentifiableWithProp
+    {
+        public int value;
+
+        // Property that contains Object Id to use
+        public int customId;
+
+        public IdentifiableWithProp next;
+        
+        public IdentifiableWithProp() { this(0, 0); }
+        public IdentifiableWithProp(int id, int value) {
+            this.customId = id;
+            this.value = value;
+        }
+    }
+
+    // For property reference, need another class:
+    
+    static class IdWrapper
+    {
+        @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
+        public ValueNode node;
+
+        public IdWrapper() { }
+        public IdWrapper(int v) {
+            node = new ValueNode(v);
+        }
+    }
+
+    static class ValueNode {
+        public int value;
+        public IdWrapper next;
+        
+        public ValueNode() { this(0); }
+        public ValueNode(int v) { value = v; }
+    }
+
+    // Similarly for property-ref via property:
+    
+    protected static class IdWrapperCustom
+    {
+        @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+        public ValueNodeCustom node;
+
+        public IdWrapperCustom() { }
+        public IdWrapperCustom(int id, int value) {
+            node = new ValueNodeCustom(id, value);
+        }
+    }
+
+    protected static class ValueNodeCustom {
+        public int value;
+        private int id;
+        public IdWrapperCustom next;
+
+        public int getId() { return id; }
+        
+        public ValueNodeCustom() { this(0, 0); }
+        public ValueNodeCustom(int id, int value) {
+            this.id = id;
+            this.value = value;
+        }
+    }
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
+    static class AlwaysAsId
+    {
+        public int value;
+        
+        public AlwaysAsId() { this(0); }
+        public AlwaysAsId(int v) {
+            value = v;
+        }
+    }
+
+    // For [https://github.com/FasterXML/jackson-annotations/issues/4]
+    @JsonPropertyOrder(alphabetic=true)
+    static class AlwaysContainer
+    {
+        @JsonIdentityReference(alwaysAsId=true)
+        public AlwaysAsId a = new AlwaysAsId(13);
+        
+        @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
+        @JsonIdentityReference(alwaysAsId=true)
+        public Value b = new Value();
+    }
+
+    static class Value {
+        public int x = 3;
+    }
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+    static class TreeNode
+    {
+        public int id;
+        public String name;
+
+        @JsonIdentityReference(alwaysAsId=true)
+        public TreeNode parent;
+
+        // children serialized with ids if need be
+        public TreeNode child;
+
+        public TreeNode() { }
+        public TreeNode(TreeNode p, int id, String name) {
+            parent = p;
+            this.id = id;
+            this.name = name;
+        }
+    }
+
+    // // Let's also have one 'broken' test
+
+    // no "id" property
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+    static class Broken
+    {
+        public int value;
+        public int customId;
+    }
+    
+    /*
+    /*****************************************************
+    /* Unit tests, external id serialization
+    /*****************************************************
+     */
+
+    private final static String EXP_SIMPLE_INT_CLASS = "{\"id\":1,\"value\":13,\"next\":1}";
+    
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimpleSerializationClass() throws Exception
+    {
+        Identifiable src = new Identifiable(13);
+        src.next = src;
+        
+        // First, serialize:
+        String json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_SIMPLE_INT_CLASS, json);
+
+        // and ensure that state is cleared in-between as well:
+        json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_SIMPLE_INT_CLASS, json);
+    }
+    
+    // Bit more complex, due to extra wrapping etc:
+    private final static String EXP_SIMPLE_INT_PROP = "{\"node\":{\"@id\":1,\"value\":7,\"next\":{\"node\":1}}}";
+
+    public void testSimpleSerializationProperty() throws Exception
+    {
+        IdWrapper src = new IdWrapper(7);
+        src.node.next = src;
+        
+        // First, serialize:
+        String json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_SIMPLE_INT_PROP, json);
+        // and second time too, for a good measure
+        json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_SIMPLE_INT_PROP, json);
+    }
+
+    /*
+    /*****************************************************
+    /* Unit tests, custom (property) id serialization
+    /*****************************************************
+     */
+
+    private final static String EXP_CUSTOM_PROP = "{\"customId\":123,\"value\":-19,\"next\":123}";
+    // Test for verifying that custom
+    public void testCustomPropertyForClass() throws Exception
+    {
+        IdentifiableWithProp src = new IdentifiableWithProp(123, -19);
+        src.next = src;
+        
+        // First, serialize:
+        String json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_CUSTOM_PROP, json);
+
+        // and ensure that state is cleared in-between as well:
+        json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_CUSTOM_PROP, json);
+    }
+
+    private final static String EXP_CUSTOM_PROP_VIA_REF = "{\"node\":{\"id\":123,\"value\":7,\"next\":{\"node\":123}}}";
+    // Test for verifying that custom
+    public void testCustomPropertyViaProperty() throws Exception
+    {
+        IdWrapperCustom src = new IdWrapperCustom(123, 7);
+        src.node.next = src;
+        
+        // First, serialize:
+        String json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_CUSTOM_PROP_VIA_REF, json);
+        // and second time too, for a good measure
+        json = MAPPER.writeValueAsString(src);
+        assertEquals(EXP_CUSTOM_PROP_VIA_REF, json);
+    }
+
+    public void testAlwaysAsId() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new AlwaysContainer());
+        assertEquals("{\"a\":1,\"b\":2}", json);
+    }
+
+    public void testAlwaysIdForTree() throws Exception
+    {
+        TreeNode root = new TreeNode(null, 1, "root");     
+        TreeNode leaf = new TreeNode(root, 2, "leaf");
+        root.child = leaf;
+        String json = MAPPER.writeValueAsString(root);
+//        System.out.println(json);
+        assertEquals("{\"id\":1,\"name\":\"root\",\"parent\":null,\"child\":"
+                +"{\"id\":2,\"name\":\"leaf\",\"parent\":1,\"child\":null}}",
+                json);
+        		
+    }
+    
+    /*
+    /*****************************************************
+    /* Unit tests, error handling
+    /*****************************************************
+     */
+
+    public void testInvalidProp() throws Exception
+    {
+        try {
+            MAPPER.writeValueAsString(new Broken());
+            fail("Should have thrown an exception");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find property with name 'id'");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java
new file mode 100644
index 0000000..b0cb34c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestObjectIdWithPolymorphic.java
@@ -0,0 +1,164 @@
+package com.fasterxml.jackson.databind.struct;
+
+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;
+
+public class TestObjectIdWithPolymorphic extends BaseMapTest
+{
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
+    static abstract class Base
+    {
+        public int value;
+
+        public Base next;
+        
+        public Base() { this(0); }
+        public Base(int v) {
+            value = v;
+        }
+    }
+
+    static class Impl extends Base
+    {
+        public int extra;
+
+        public Impl() { this(0, 0); }
+        public Impl(int v, int e) {
+            super(v);
+            extra = e;
+        }
+    }
+
+    // [JACKSON-811] types
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
+    public static class Base811 {
+        public int id;
+        public Base811 owner;
+            
+        private Base811() {}
+        public Base811(Process owner) {
+            this.owner = owner;
+            if (owner == null) {
+                id = 0;
+            } else {
+                id = ++owner.childIdCounter;
+                owner.children.add(this);
+            }
+        }
+    }
+
+    public static class Process extends Base811 {
+        protected int childIdCounter = 0;
+        protected List<Base811> children = new ArrayList<Base811>();
+        
+        public Process() { super(null); }
+    }
+    
+    public static abstract class Activity extends Base811 {
+        protected Activity parent;
+        public Activity(Process owner, Activity parent) {
+                super(owner);
+                this.parent = parent;
+        }
+        private Activity() {
+                super();
+        }
+    }
+    
+    public static class Scope extends Activity {
+        public final List<FaultHandler> faultHandlers = new ArrayList<FaultHandler>();
+        public Scope(Process owner, Activity parent) {
+            super(owner, parent);
+        }
+        private Scope() {
+            super();
+        }
+    }
+    
+    public static class FaultHandler extends Base811 {
+        public final List<Catch> catchBlocks = new ArrayList<Catch>();
+        
+        public FaultHandler(Process owner) {
+            super(owner);
+        }
+
+        protected FaultHandler() {}
+    }
+    
+    public static class Catch extends Scope {
+        public Catch(Process owner, Activity parent) {
+            super(owner, parent);
+        }
+        protected Catch() {};
+    }
+
+    /*
+    /*****************************************************
+    /* Unit tests for polymorphic type handling
+    /*****************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    public void testPolymorphicRoundtrip() throws Exception
+    {
+        // create simple 2 node loop:
+        Impl in1 = new Impl(123, 456);
+        in1.next = new Impl(111, 222);
+        in1.next.next = in1;
+        
+        String json = mapper.writeValueAsString(in1);
+        
+        // then bring back...
+        Base result0 = mapper.readValue(json, Base.class);
+        assertNotNull(result0);
+        assertSame(Impl.class, result0.getClass());
+        Impl result = (Impl) result0;
+        assertEquals(123, result.value);
+        assertEquals(456, result.extra);
+        Impl result2 = (Impl) result.next;
+        assertEquals(111, result2.value);
+        assertEquals(222, result2.extra);
+        assertSame(result, result2.next);
+    }
+
+    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");
+    
+        Process p = new Process();
+        Scope s = new Scope(p, null);
+        FaultHandler fh = new FaultHandler(p);
+        Catch c = new Catch(p, s);
+        fh.catchBlocks.add(c);
+        s.faultHandlers.add(fh);
+        
+        String json = om.writeValueAsString(p);
+        Process restored = om.readValue(json, Process.class);
+        assertNotNull(restored);
+
+        assertEquals(0, p.id);
+        assertEquals(3, p.children.size());
+        assertSame(p, p.children.get(0).owner);
+        assertSame(p, p.children.get(1).owner);
+        assertSame(p, p.children.get(2).owner);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
new file mode 100644
index 0000000..33d2f54
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArray.java
@@ -0,0 +1,184 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+public class TestPOJOAsArray extends BaseMapTest
+{
+    static class Pojo
+    {
+        @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+        public PojoValue value;
+
+        public Pojo() { }
+        public Pojo(String name, int x, int y, boolean c) {
+            value = new PojoValue(name, x, y, c);
+        }
+    }
+
+    // note: must be serialized/deserialized alphabetically; fields NOT declared in that order
+    @JsonPropertyOrder(alphabetic=true)
+    static class PojoValue
+    {
+        public int x, y;
+        public String name;
+        public boolean complete;
+
+        public PojoValue() { }
+        public PojoValue(String name, int x, int y, boolean c) {
+            this.name = name;
+            this.x = x;
+            this.y = y;
+            this.complete = c;
+        }
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    static class FlatPojo
+    {
+        public int x, y;
+        public String name;
+        public boolean complete;
+
+        public FlatPojo() { }
+        public FlatPojo(String name, int x, int y, boolean c) {
+            this.name = name;
+            this.x = x;
+            this.y = y;
+            this.complete = c;
+        }
+    }
+
+    static class ForceArraysIntrospector extends JacksonAnnotationIntrospector
+    {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        public JsonFormat.Value findFormat(Annotated a) {
+            return new JsonFormat.Value().withShape(JsonFormat.Shape.ARRAY);
+        }
+    }
+
+    static class A {
+        public B value = new B();
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    static class B {
+        public int x = 1;
+        public int y = 2;
+    }
+
+    // for [JACKSON-805]
+    @JsonFormat(shape=Shape.ARRAY)
+    static class SingleBean {
+        public String name = "foo";
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    @JsonFormat(shape=Shape.ARRAY)
+    static class TwoStringsBean {
+        public String bar = null;
+        public String foo = "bar";
+    }
+    
+    /*
+    /*****************************************************
+    /* Basic tests
+    /*****************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+    
+    /**
+     * Test that verifies that property annotation works
+     */
+    public void testReadSimplePropertyValue() throws Exception
+    {
+        String json = "{\"value\":[true,\"Foobar\",42,13]}";
+        Pojo p = MAPPER.readValue(json, Pojo.class);
+        assertNotNull(p.value);
+        assertTrue(p.value.complete);
+        assertEquals("Foobar", p.value.name);
+        assertEquals(42, p.value.x);
+        assertEquals(13, p.value.y);
+    }
+
+    /**
+     * Test that verifies that Class annotation works
+     */
+    public void testReadSimpleRootValue() throws Exception
+    {
+        String json = "[false,\"Bubba\",1,2]";
+        FlatPojo p = MAPPER.readValue(json, FlatPojo.class);
+        assertFalse(p.complete);
+        assertEquals("Bubba", p.name);
+        assertEquals(1, p.x);
+        assertEquals(2, p.y);
+    }
+    
+    /**
+     * Test that verifies that property annotation works
+     */
+    public void testWriteSimplePropertyValue() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new Pojo("Foobar", 42, 13, true));
+        // will have wrapper POJO, then POJO-as-array..
+        assertEquals("{\"value\":[true,\"Foobar\",42,13]}", json);
+    }
+
+    /**
+     * Test that verifies that Class annotation works
+     */
+    public void testWriteSimpleRootValue() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new FlatPojo("Bubba", 1, 2, false));
+        // will have wrapper POJO, then POJO-as-array..
+        assertEquals("[false,\"Bubba\",1,2]", json);
+    }
+
+    // [Issue#223]
+    public void testNullColumn() throws Exception
+    {
+        assertEquals("[null,\"bar\"]", MAPPER.writeValueAsString(new TwoStringsBean()));
+    }
+
+    /*
+    /*****************************************************
+    /* 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);
+        String json = mapper.writeValueAsString(new SingleBean());
+        assertEquals("\"foo\"", json);
+    }
+    
+    /*
+    /*****************************************************
+    /* Round-trip tests
+    /*****************************************************
+     */
+
+    public void testAnnotationOverride() throws Exception
+    {
+        // by default, POJOs become JSON Objects;
+        assertEquals("{\"value\":{\"x\":1,\"y\":2}}", MAPPER.writeValueAsString(new A()));
+
+        // but override should change it:
+        ObjectMapper mapper2 = new ObjectMapper();
+        mapper2.setAnnotationIntrospector(new ForceArraysIntrospector());
+        assertEquals("[[1,2]]", mapper2.writeValueAsString(new A()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java
new file mode 100644
index 0000000..d2569c5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayAdvanced.java
@@ -0,0 +1,123 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestPOJOAsArrayAdvanced extends BaseMapTest
+{
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder(alphabetic=true)
+    static class CreatorAsArray
+    {
+        protected int x, y;
+        public int a, b;
+
+        @JsonCreator
+        public CreatorAsArray(@JsonProperty("x") int x, @JsonProperty("y") int y)
+        {
+            this.x = x;
+            this.y = y;
+        }
+
+        public int getX() { return x; }
+        public int getY() { return y; }
+    }
+
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder({"a","b","x","y"})
+    static class CreatorAsArrayShuffled
+    {
+        protected int x, y;
+        public int a, b;
+
+        @JsonCreator
+        public CreatorAsArrayShuffled(@JsonProperty("x") int x, @JsonProperty("y") int y)
+        {
+            this.x = x;
+            this.y = y;
+        }
+
+        public int getX() { return x; }
+        public int getY() { return y; }
+    }
+
+    static class ViewA { }
+    static class ViewB { }
+    
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder(alphabetic=true)
+    static class AsArrayWithView
+    {
+        @JsonView(ViewA.class)
+        public int a;
+        @JsonView(ViewB.class)
+        public int b;
+        public int c;
+    }
+    
+    /*
+    /*****************************************************
+    /* Basic tests
+    /*****************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testWithView() throws Exception
+    {
+        // Ok, first, ensure that serializer will "black out" filtered properties
+        AsArrayWithView input = new AsArrayWithView();
+        input.a = 1;
+        input.b = 2;
+        input.c = 3;
+        String json = MAPPER.writerWithView(ViewA.class).writeValueAsString(input);
+        assertEquals("[1,null,3]", json);
+
+        // and then that conversely deserializer does something similar
+        AsArrayWithView output = MAPPER.reader(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);
+    }
+
+    public void testWithCreatorsOrdered() throws Exception
+    {
+        CreatorAsArray input = new CreatorAsArray(3, 4);
+        input.a = 1;
+        input.b = 2;
+
+        // note: Creator properties get sorted ahead of others, hence not [1,2,3,4] but:
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals("[3,4,1,2]", json);
+
+        // and should get back in proper order, too
+        CreatorAsArray output = MAPPER.readValue(json, CreatorAsArray.class);
+        assertEquals(1, output.a);
+        assertEquals(2, output.b);
+        assertEquals(3, output.x);
+        assertEquals(4, output.y);
+    }
+
+    // Same as above, but ordering of properties different...
+    public void testWithCreatorsShuffled() throws Exception
+    {
+        CreatorAsArrayShuffled input = new CreatorAsArrayShuffled(3, 4);
+        input.a = 1;
+        input.b = 2;
+
+        // note: explicit ordering overrides implicit creators-first ordering:
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals("[1,2,3,4]", json);
+
+        // and should get back in proper order, too
+        CreatorAsArrayShuffled output = MAPPER.readValue(json, CreatorAsArrayShuffled.class);
+        assertEquals(1, output.a);
+        assertEquals(2, output.b);
+        assertEquals(3, output.x);
+        assertEquals(4, output.y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
new file mode 100644
index 0000000..6e37cd5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestPOJOAsArrayWithBuilder.java
@@ -0,0 +1,63 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+/**
+ * Unit tests for "POJO as array" feature using Builder-style
+ * POJO construction.
+ */
+public class TestPOJOAsArrayWithBuilder extends BaseMapTest
+{
+    @JsonDeserialize(builder=SimpleBuilderXY.class)
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder(alphabetic=true)
+    static class ValueClassXY
+    {
+        final int _x, _y;
+
+        protected ValueClassXY(int x, int y) {
+            _x = x+1;
+            _y = y+1;
+        }
+    }
+
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    static class SimpleBuilderXY
+    {
+        public int x, y;
+        
+        public SimpleBuilderXY withX(int x) {
+            this.x = x;
+            return this;
+        }
+
+        public SimpleBuilderXY withY(int y) {
+            this.y = y;
+            return this;
+        }
+
+        public ValueClassXY build() {
+            return new ValueClassXY(x, y);
+        }
+    }
+    
+    /*
+    /*****************************************************
+    /* Basic tests
+    /*****************************************************
+     */
+
+    private final static ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testSimpleBuilder() throws Exception
+    {
+        // Ok, first, ensure that serializer will "black out" filtered properties
+        ValueClassXY value = MAPPER.readValue("[1,2]", ValueClassXY.class);
+        assertEquals(2, value._x);
+        assertEquals(3, value._y);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java
new file mode 100644
index 0000000..8ddf6e2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrapped.java
@@ -0,0 +1,187 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying [JACKSON-132] implementation.
+ */
+public class TestUnwrapped extends BaseMapTest
+{
+    static class Unwrapping {
+        public String name;
+        @JsonUnwrapped
+        public Location location;
+
+        public Unwrapping() { }
+        public Unwrapping(String str, int x, int y) {
+            name = str;
+            location = new Location(x, y);
+        }
+    }
+
+    static class DeepUnwrapping
+    {
+        @JsonUnwrapped
+        public Unwrapping unwrapped;
+
+        public DeepUnwrapping() { }
+        public DeepUnwrapping(String str, int x, int y) {
+            unwrapped = new Unwrapping(str, x, y);
+        }
+    }
+    
+    static class UnwrappingWithCreator {
+        public String name;
+
+        @JsonUnwrapped
+        public Location location;
+
+        @JsonCreator
+        public UnwrappingWithCreator(@JsonProperty("name") String n) {
+            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 {
+        @JsonUnwrapped
+        public Location location;
+        @JsonUnwrapped
+        public Name name;
+
+        public TwoUnwrappedProperties() { }
+    }
+
+    static class Name {
+        public String first, last;
+    }
+
+    /*
+    /**********************************************************
+    /* Tests, serialization
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimpleUnwrappingSerialize() throws Exception
+    {
+        assertEquals("{\"name\":\"Tatu\",\"x\":1,\"y\":2}",
+                mapper.writeValueAsString(new Unwrapping("Tatu", 1, 2)));
+    }
+    public void testDeepUnwrappingSerialize() throws Exception
+    {
+        assertEquals("{\"name\":\"Tatu\",\"x\":1,\"y\":2}",
+                mapper.writeValueAsString(new DeepUnwrapping("Tatu", 1, 2)));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests, deserialization
+    /**********************************************************
+     */
+    
+    public void testSimpleUnwrappedDeserialize() throws Exception
+    {
+        Unwrapping bean = mapper.readValue("{\"name\":\"Tatu\",\"y\":7,\"x\":-13}",
+                Unwrapping.class);
+        assertEquals("Tatu", bean.name);
+        Location loc = bean.location;
+        assertNotNull(loc);
+        assertEquals(-13, loc.x);
+        assertEquals(7, loc.y);
+    }
+    
+    public void testDoubleUnwrapping() throws Exception
+    {
+        TwoUnwrappedProperties bean = mapper.readValue("{\"first\":\"Joe\",\"y\":7,\"last\":\"Smith\",\"x\":-13}",
+                TwoUnwrappedProperties.class);
+        Location loc = bean.location;
+        assertNotNull(loc);
+        assertEquals(-13, loc.x);
+        assertEquals(7, loc.y);
+        Name name = bean.name;
+        assertNotNull(name);
+        assertEquals("Joe", name.first);
+        assertEquals("Smith", name.last);
+    }
+    
+    public void testDeepUnwrapping() throws Exception
+    {
+        DeepUnwrapping bean = mapper.readValue("{\"x\":3,\"name\":\"Bob\",\"y\":27}",
+                DeepUnwrapping.class);
+        Unwrapping uw = bean.unwrapped;
+        assertNotNull(uw);
+        assertEquals("Bob", uw.name);
+        Location loc = uw.location;
+        assertNotNull(loc);
+        assertEquals(3, loc.x);
+        assertEquals(27, loc.y);
+    }
+    
+    public void testUnwrappedDeserializeWithCreator() throws Exception
+    {
+        UnwrappingWithCreator bean = mapper.readValue("{\"x\":1,\"y\":2,\"name\":\"Tatu\"}",
+                UnwrappingWithCreator.class);
+        assertEquals("Tatu", bean.name);
+        Location loc = bean.location;
+        assertNotNull(loc);
+        assertEquals(1, loc.x);
+        assertEquals(2, loc.y);
+    }
+
+    // 22-Apr-2013, tatu: Commented out as it can't be simply fixed; requires implementing
+    //    deep-update/merge. But leaving here to help with that effort, if/when it proceeds.
+    
+    /*
+    
+    // [Issue#211]: Actually just variant of #160
+    
+    static class Issue211Bean {
+        public String test1;
+
+        public String test2;
+        @JsonUnwrapped
+        public Issue211Unwrapped unwrapped;
+    }
+
+    static class Issue211Unwrapped {
+        public String test3;
+        public String test4;
+    }
+    
+    public void testIssue211() throws Exception
+    {
+         Issue211Bean bean = new Issue211Bean();
+         bean.test1 = "Field 1";
+         bean.test2 = "Field 2";
+         Issue211Unwrapped tJackson2 = new Issue211Unwrapped();
+         tJackson2.test3 = "Field 3";
+         tJackson2.test4 = "Field 4";
+         bean.unwrapped = tJackson2;
+ 
+         final String JSON = "{\"test1\": \"Field 1 merged\", \"test3\": \"Field 3 merged\"}";
+         ObjectMapper o = new ObjectMapper();
+         Issue211Bean result = o.readerForUpdating(bean).withType(Issue211Bean.class).readValue(JSON);
+         assertSame(bean, result);
+         assertEquals("Field 1 merged", result.test1);
+         assertEquals("Field 2", result.test2);
+         assertNotNull(result.unwrapped);
+         assertEquals("Field 3 merged", result.unwrapped.test3);
+         assertEquals("Field 4", result.unwrapped.test4);
+    }  
+    */
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java
new file mode 100644
index 0000000..5d439c5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/struct/TestUnwrappedWithPrefix.java
@@ -0,0 +1,249 @@
+package com.fasterxml.jackson.databind.struct;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestUnwrappedWithPrefix extends BaseMapTest
+{
+    static class Unwrapping {
+        public String name;
+        @JsonUnwrapped
+        public Location location;
+
+        public Unwrapping() { }
+        public Unwrapping(String str, int x, int y) {
+            name = str;
+            location = new Location(x, y);
+        }
+    }
+
+    static class DeepUnwrapping
+    {
+        @JsonUnwrapped
+        public Unwrapping unwrapped;
+
+        public DeepUnwrapping() { }
+        public DeepUnwrapping(String str, int x, int y) {
+            unwrapped = new Unwrapping(str, x, y);
+        }
+    }
+
+    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 unwrapping using prefixes
+    static class PrefixUnwrap
+    {
+        public String name;
+        @JsonUnwrapped(prefix="_")
+        public Location location;
+
+        public PrefixUnwrap() { }
+        public PrefixUnwrap(String str, int x, int y) {
+            name = str;
+            location = new Location(x, y);
+        }
+    }
+    
+    static class DeepPrefixUnwrap
+    {
+        @JsonUnwrapped(prefix="u.")
+        public PrefixUnwrap unwrapped;
+
+        public DeepPrefixUnwrap() { }
+        public DeepPrefixUnwrap(String str, int x, int y) {
+            unwrapped = new PrefixUnwrap(str, x, y);
+        }
+    }
+
+    // Let's actually test hierarchic names with unwrapping bit more:
+    
+    static class ConfigRoot
+    {
+        @JsonUnwrapped(prefix="general.")
+        public ConfigGeneral general = new ConfigGeneral();
+        
+        @JsonUnwrapped(prefix="misc.")
+        public ConfigMisc misc = new ConfigMisc();
+
+        public ConfigRoot() { }
+        public ConfigRoot(String name, int value)
+        {
+            general = new ConfigGeneral(name);
+            misc.value = value;
+        }
+    }
+
+    static class ConfigAlternate
+    {
+        @JsonUnwrapped
+        public ConfigGeneral general = new ConfigGeneral();
+        
+        @JsonUnwrapped(prefix="misc.")
+        public ConfigMisc misc = new ConfigMisc();
+
+        public int id;
+        
+        public ConfigAlternate() { }
+        public ConfigAlternate(int id, String name, int value)
+        {
+            this.id = id;
+            general = new ConfigGeneral(name);
+            misc.value = value;
+        }
+    }
+
+    static class ConfigGeneral
+    {
+        @JsonUnwrapped(prefix="names.")
+        public ConfigNames names = new ConfigNames();
+        
+        public ConfigGeneral() { }
+        public ConfigGeneral(String name) {
+            names.name = name;
+        }
+    }
+
+    static class ConfigNames {
+        public String name = "x";
+    }
+
+    static class ConfigMisc {
+        public int value;
+    }
+
+    // For [Issue#226]
+    static class Parent {
+        @JsonUnwrapped(prefix="c1.")
+        public Child c1;
+        @JsonUnwrapped(prefix="c2.")
+        public Child c2;
+      }
+
+    static class Child {
+        @JsonUnwrapped(prefix="sc2.")
+        public SubChild sc1;
+      }
+
+    static class SubChild {
+        public String value;
+    }
+    
+    // // // Reuse mapper to keep tests bit faster
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Tests, serialization
+    /**********************************************************
+     */
+
+    public void testPrefixedUnwrappingSerialize() throws Exception
+    {
+        assertEquals("{\"name\":\"Tatu\",\"_x\":1,\"_y\":2}",
+                MAPPER.writeValueAsString(new PrefixUnwrap("Tatu", 1, 2)));
+    }
+
+    public void testDeepPrefixedUnwrappingSerialize() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new DeepPrefixUnwrap("Bubba", 1, 1));
+        assertEquals("{\"u.name\":\"Bubba\",\"u._x\":1,\"u._y\":1}", json);
+    }
+
+    public void testHierarchicConfigSerialize() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new ConfigRoot("Fred", 25));
+        assertEquals("{\"general.names.name\":\"Fred\",\"misc.value\":25}", json);
+    }
+
+    /*
+    /**********************************************************
+    /* Tests, deserialization
+    /**********************************************************
+     */
+
+    public void testPrefixedUnwrapping() throws Exception
+    {
+        PrefixUnwrap bean = MAPPER.readValue("{\"name\":\"Axel\",\"_x\":4,\"_y\":7}", PrefixUnwrap.class);
+        assertNotNull(bean);
+        assertEquals("Axel", bean.name);
+        assertNotNull(bean.location);
+        assertEquals(4, bean.location.x);
+        assertEquals(7, bean.location.y);
+    }
+    
+    public void testDeepPrefixedUnwrappingDeserialize() throws Exception
+    {
+        DeepPrefixUnwrap bean = MAPPER.readValue("{\"u.name\":\"Bubba\",\"u._x\":2,\"u._y\":3}",
+                DeepPrefixUnwrap.class);
+        assertNotNull(bean.unwrapped);
+        assertNotNull(bean.unwrapped.location);
+        assertEquals(2, bean.unwrapped.location.x);
+        assertEquals(3, bean.unwrapped.location.y);
+        assertEquals("Bubba", bean.unwrapped.name);
+    }
+    
+    public void testHierarchicConfigDeserialize() throws Exception
+    {
+        ConfigRoot root = MAPPER.readValue("{\"general.names.name\":\"Bob\",\"misc.value\":3}",
+                ConfigRoot.class);
+        assertNotNull(root.general);
+        assertNotNull(root.general.names);
+        assertNotNull(root.misc);
+        assertEquals(3, root.misc.value);
+        assertEquals("Bob", root.general.names.name);
+    }
+
+    /*
+    /**********************************************************
+    /* Tests, round-trip
+    /**********************************************************
+     */
+
+    public void testHierarchicConfigRoundTrip() throws Exception
+    {
+        ConfigAlternate input = new ConfigAlternate(123, "Joe", 42);
+        String json = MAPPER.writeValueAsString(input);
+
+        ConfigAlternate root = MAPPER.readValue(json, ConfigAlternate.class);
+        assertEquals(123, root.id);
+        assertNotNull(root.general);
+        assertNotNull(root.general.names);
+        assertNotNull(root.misc);
+        assertEquals("Joe", root.general.names.name);
+        assertEquals(42, root.misc.value);
+    }
+
+    public void testIssue226() throws Exception
+    {
+        Parent input = new Parent();
+        input.c1 = new Child();
+        input.c1.sc1 = new SubChild();
+        input.c1.sc1.value = "a";
+        input.c2 = new Child();
+        input.c2.sc1 = new SubChild();
+        input.c2.sc1.value = "b";
+
+        String json = MAPPER.writeValueAsString(input);
+
+        Parent output = MAPPER.readValue(json, Parent.class);
+        assertNotNull(output.c1);
+        assertNotNull(output.c2);
+
+        assertNotNull(output.c1.sc1);
+        assertNotNull(output.c2.sc1);
+        
+        assertEquals("a", output.c1.sc1.value);
+        assertEquals("b", output.c2.sc1.value);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java
new file mode 100644
index 0000000..df551d9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestAnnotatedClass.java
@@ -0,0 +1,87 @@
+package com.fasterxml.jackson.databind.type;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
+import com.fasterxml.jackson.databind.introspect.AnnotatedField;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+/**
+ * Unit test for verifying that {@link AnnotatedClass}
+ * works as expected.
+ */
+public class TestAnnotatedClass
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Annotated helper classes
+    /**********************************************************
+     */
+
+    static class BaseClass
+    {
+        public int foo;
+
+        public BaseClass(int x, int y) { }
+
+        @JsonProperty public int x() { return 3; }
+    }
+
+    static class SubClass extends BaseClass
+    {
+        public SubClass() { this(1); }
+        public SubClass(int x) { super(x, 2); }
+
+        public int y() { return 3; }
+    }
+
+    static abstract class GenericBase<T extends Number>
+    {
+        public abstract void setX(T value);
+    }
+
+    static class NumberBean
+        extends GenericBase<Integer>
+    {
+        @Override
+        public void setX(Integer value) { }
+    }
+
+    /**
+     * Test class for checking that field introspection
+     * works as expected
+     */
+    @SuppressWarnings("unused")
+    static class FieldBean
+    {
+        // static, not to be included:
+        public static boolean DUMMY;
+
+        // not public, no annotations, shouldn't be included
+        private long bar;
+
+        @JsonProperty
+        private String props;
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    public void testFieldIntrospection()
+    {
+        // null -> no mix-in annotations
+        AnnotatedClass ac = AnnotatedClass.construct(FieldBean.class, new JacksonAnnotationIntrospector(), null);
+        // AnnotatedClass does not ignore non-visible fields, yet
+        assertEquals(2, ac.getFieldCount());
+        for (AnnotatedField f : ac.fields()) {
+            String fname = f.getName();
+            if (!"bar".equals(fname) && !"props".equals(fname)) {
+                fail("Unexpected field name '"+fname+"'");
+            }
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestGenericFieldInSubtype.java b/src/test/java/com/fasterxml/jackson/databind/type/TestGenericFieldInSubtype.java
new file mode 100644
index 0000000..b14cbce
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestGenericFieldInSubtype.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.databind.type;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestGenericFieldInSubtype extends BaseMapTest
+{
+    // [JACKSON-677]
+    public void test677() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        // and bit more checking as per later comments
+        JavaType t677 = mapper.constructType(Result677.Success677.class);
+        assertNotNull(t677);
+        Result677.Success677<Integer> s = new Result677.Success677<Integer>(Integer.valueOf(4));
+        String json = mapper.writeValueAsString(s);
+        assertEquals("{\"value\":4}", json);
+    }
+
+ // [JACKSON-887]
+    public void testInnerType() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        BaseType.SubType<?> r = mapper.readValue("{}", BaseType.SubType.class);
+        assertNotNull(r);
+    }
+
+}
+
+class Result677<T> {
+    public static class Success677<K> extends Result677<K> {
+     public K value;
+     
+     public Success677() { }
+     public Success677(K k) { value = k; }
+    }
+}
+
+abstract class BaseType<T> {
+    public T value;
+
+    public final static class SubType<T extends Number> extends BaseType<T>
+    {
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
new file mode 100644
index 0000000..7a3bfa2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestJavaType.java
@@ -0,0 +1,190 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Simple tests to verify that {@link JavaType} types work to
+ * some degree
+ */
+public class TestJavaType
+    extends com.fasterxml.jackson.test.BaseTest
+{
+    static class BaseType { }
+
+    static class SubType extends BaseType { }
+    
+    static enum MyEnum { A, B; }
+    static enum MyEnum2 {
+        A(1), B(2);
+        
+        private MyEnum2(int value) { }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testSimpleClass()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType baseType = tf.constructType(BaseType.class);
+        assertSame(BaseType.class, baseType.getRawClass());
+        assertTrue(baseType.hasRawClass(BaseType.class));
+
+        assertFalse(baseType.isArrayType());
+        assertFalse(baseType.isContainerType());
+        assertFalse(baseType.isEnumType());
+        assertFalse(baseType.isInterface());
+        assertFalse(baseType.isPrimitive());
+
+        assertNull(baseType.getContentType());
+        assertNull(baseType.getValueHandler());
+
+        /* both narrow and widen just return type itself (exact, not just
+         * equal)
+         * (also note that widen/narrow wouldn't work on basic simple
+         * class type otherwise)
+         */
+        assertSame(baseType, baseType.narrowBy(BaseType.class));
+        assertSame(baseType, baseType.widenBy(BaseType.class));
+
+        // Also: no narrowing for simple types (but should there be?)
+        try {
+            baseType.narrowBy(SubType.class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "should never be called");
+        }
+
+        // Also, let's try assigning bogus handler
+        /*
+        baseType.setValueHandler("xyz"); // untyped
+        assertEquals("xyz", baseType.getValueHandler());
+        // illegal to re-set
+        try {
+            baseType.setValueHandler("foobar");
+            fail("Shouldn't allow re-setting value handler");
+        } catch (IllegalStateException iae) {
+            verifyException(iae, "Trying to reset");
+        }
+        */
+    }
+
+    public void testMapType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType keyT = tf.constructType(String.class);
+        JavaType baseT = tf.constructType(BaseType.class);
+
+        MapType mapT = MapType.construct(Map.class, keyT, baseT);
+        assertNotNull(mapT);
+        assertTrue(mapT.isContainerType());
+
+        // NOPs:
+        assertSame(mapT, mapT.narrowContentsBy(BaseType.class));
+        assertSame(mapT, mapT.narrowKey(String.class));
+
+        assertTrue(mapT.equals(mapT));
+        assertFalse(mapT.equals(null));
+        assertFalse(mapT.equals("xyz"));
+
+        MapType mapT2 = MapType.construct(HashMap.class, keyT, baseT);
+        assertFalse(mapT.equals(mapT2));
+
+        // Also, must use map type constructor, not simple...
+        try {
+            SimpleType.construct(HashMap.class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "for a Map");
+        }
+    }
+
+    public void testArrayType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType arrayT = ArrayType.construct(tf.constructType(String.class), null, null);
+        assertNotNull(arrayT);
+        assertTrue(arrayT.isContainerType());
+
+        // NOPs:
+        assertSame(arrayT, arrayT.narrowContentsBy(String.class));
+
+        assertNotNull(arrayT.toString());
+
+        assertTrue(arrayT.equals(arrayT));
+        assertFalse(arrayT.equals(null));
+        assertFalse(arrayT.equals("xyz"));
+
+        assertTrue(arrayT.equals(ArrayType.construct(tf.constructType(String.class), null, null)));
+        assertFalse(arrayT.equals(ArrayType.construct(tf.constructType(Integer.class), null, null)));
+
+        // Also, must NOT try to create using simple type
+        try {
+            SimpleType.construct(String[].class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "for an array");
+        }
+    }
+
+    public void testCollectionType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // List<String>
+        JavaType collectionT = CollectionType.construct(List.class, tf.constructType(String.class));
+        assertNotNull(collectionT);
+        assertTrue(collectionT.isContainerType());
+
+        // NOPs:
+        assertSame(collectionT, collectionT.narrowContentsBy(String.class));
+
+        assertNotNull(collectionT.toString());
+
+        assertTrue(collectionT.equals(collectionT));
+        assertFalse(collectionT.equals(null));
+        assertFalse(collectionT.equals("xyz"));
+
+        assertTrue(collectionT.equals(CollectionType.construct(List.class, tf.constructType(String.class))));
+        assertFalse(collectionT.equals(CollectionType.construct(Set.class, tf.constructType(String.class))));
+
+        // Also, must NOT try to create using simple type
+        try {
+            SimpleType.construct(ArrayList.class);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "for a Collection");
+        }
+    }
+
+    public void testEnumType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        assertTrue(tf.constructType(MyEnum.class).isEnumType());
+        assertTrue(tf.constructType(MyEnum2.class).isEnumType());
+        assertTrue(tf.constructType(MyEnum.A.getClass()).isEnumType());
+        assertTrue(tf.constructType(MyEnum2.A.getClass()).isEnumType());
+    }
+
+    public void testClassKey()
+    {
+        ClassKey key = new ClassKey(String.class);
+        assertEquals(0, key.compareTo(key));
+        assertTrue(key.equals(key));
+        assertFalse(key.equals(null));
+        assertFalse(key.equals("foo"));
+        assertFalse(key.equals(new ClassKey(Integer.class)));
+        assertEquals(String.class.getName(), key.toString());
+    }
+
+    // [Issue#116]
+    public void testJavaTypeAsJLRType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType t1 = tf.constructType(getClass());
+        // should just get it back as-is:
+        JavaType t2 = tf.constructType(t1);
+        assertSame(t1, t2);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
new file mode 100644
index 0000000..1aaac42
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeBindings.java
@@ -0,0 +1,75 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+
+/**
+ * Simple tests to verify for generic type binding functionality
+ * implemented by {@link TypeBindings} class.
+ */
+public class TestTypeBindings
+    extends com.fasterxml.jackson.test.BaseTest
+{    
+    static class AbstractType<A,B> { }
+    
+    static class LongStringType extends AbstractType<Long,String> { }
+
+    static class InnerGenericTyping<K, V> extends AbstractMap<K, Collection<V>>
+    {
+        @Override
+        public Set<java.util.Map.Entry<K, Collection<V>>> entrySet() {
+            return null;
+        }
+        public class InnerClass extends AbstractMap<K, Collection<V>> {
+            @Override
+            public Set<java.util.Map.Entry<K, Collection<V>>> entrySet() {
+                return null;
+            }
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testAbstract() throws Exception
+    {
+        /* Abstract type does declare type parameters, but they are only
+         * known as 'Object.class' (via lower bound)
+         */
+        TypeFactory tf = TypeFactory.defaultInstance();
+        TypeBindings b = new TypeBindings(tf, AbstractType.class);
+        assertEquals(2, b.getBindingCount());
+        JavaType obType = tf.constructType(Object.class);
+        assertEquals(obType, b.findType("A"));
+        assertEquals(obType, b.findType("B"));
+    }
+
+    public void testSimple() throws Exception
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // concrete class does have bindings however
+        TypeBindings b = new TypeBindings(tf, LongStringType.class);
+        assertEquals(2, b.getBindingCount());
+        assertEquals(tf.constructType(Long.class), b.findType("A"));
+        assertEquals(tf.constructType(String.class), b.findType("B"));
+    }
+
+
+    // [JACKSON-677]
+    public void testInnerType() throws Exception
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(InnerGenericTyping.InnerClass.class);
+        assertEquals(MapType.class, type.getClass());
+        JavaType keyType = type.getKeyType();
+        assertEquals(Object.class, keyType.getRawClass());
+        JavaType valueType = type.getContentType();
+        assertEquals(Collection.class, valueType.getRawClass());
+        JavaType vt2 = valueType.getContentType();
+        assertEquals(Object.class, vt2.getRawClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
new file mode 100644
index 0000000..8b340d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeFactory.java
@@ -0,0 +1,537 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+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.HierarchicType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.SimpleType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.test.BaseTest;
+
+/**
+ * Simple tests to verify that the {@link TypeFactory} constructs
+ * type information as expected.
+ */
+public class TestTypeFactory
+    extends BaseTest
+{    
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    enum EnumForCanonical { YES, NO; }
+
+    static class SingleArgGeneric<X> { }
+
+    abstract static class MyMap extends IntermediateMap<String,Long> { }
+    abstract static class IntermediateMap<K,V> implements Map<K,V> { }
+
+    abstract static class MyList extends IntermediateList<Long> { }
+    abstract static class IntermediateList<E> implements List<E> { }
+
+    @SuppressWarnings("serial")
+    static class GenericList<T> extends ArrayList<T> { }
+    
+    interface MapInterface extends Cloneable, IntermediateInterfaceMap<String> { }
+    interface IntermediateInterfaceMap<FOO> extends Map<FOO, Integer> { }
+
+    @SuppressWarnings("serial")
+    static class MyStringIntMap extends MyStringXMap<Integer> { }
+    @SuppressWarnings("serial")
+    static class MyStringXMap<V> extends HashMap<String,V> { }
+
+    // And one more, now with obfuscated type names; essentially it's just Map<Int,Long>
+    static abstract class IntLongMap extends XLongMap<Integer> { }
+    // trick here is that V now refers to key type, not value type
+    static abstract class XLongMap<V> extends XXMap<V,Long> { }
+    static abstract class XXMap<K,V> implements Map<K,V> { }
+
+    static class SneakyBean {
+        public IntLongMap intMap;
+        public MyList longList;
+    }
+
+    static class SneakyBean2 {
+        // self-reference; should be resolved as "Comparable<Object>"
+        public <T extends Comparable<T>> T getFoobar() { return null; }
+    }
+    
+    @SuppressWarnings("serial")
+    public static class LongValuedMap<K> extends HashMap<K, Long> { }
+
+    static class StringLongMapBean {
+        public LongValuedMap<String> value;
+    }
+
+    static class StringListBean {
+        public GenericList<String> value;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    public void testSimpleTypes()
+    {
+        Class<?>[] classes = new Class<?>[] {
+            boolean.class, byte.class, char.class,
+                short.class, int.class, long.class,
+                float.class, double.class,
+
+            Boolean.class, Byte.class, Character.class,
+                Short.class, Integer.class, Long.class,
+                Float.class, Double.class,
+
+                String.class,
+                Object.class,
+
+                Calendar.class,
+                Date.class,
+        };
+
+        TypeFactory tf = TypeFactory.defaultInstance();
+        for (Class<?> clz : classes) {
+            assertSame(clz, tf.constructType(clz).getRawClass());
+            assertSame(clz, tf.constructType(clz).getRawClass());
+        }
+    }
+
+    public void testArrays()
+    {
+        Class<?>[] classes = new Class<?>[] {
+            boolean[].class, byte[].class, char[].class,
+                short[].class, int[].class, long[].class,
+                float[].class, double[].class,
+
+                String[].class, Object[].class,
+                Calendar[].class,
+        };
+
+        TypeFactory tf = TypeFactory.defaultInstance();
+        for (Class<?> clz : classes) {
+            assertSame(clz, tf.constructType(clz).getRawClass());
+            Class<?> elemType = clz.getComponentType();
+            assertSame(clz, tf.constructArrayType(elemType).getRawClass());
+        }
+    }
+
+    public void testCollections()
+    {
+        // Ok, first: let's test what happens when we pass 'raw' Collection:
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType t = tf.constructType(ArrayList.class);
+        assertEquals(CollectionType.class, t.getClass());
+        assertSame(ArrayList.class, t.getRawClass());
+
+        // And then the proper way
+        t = tf.constructType(new TypeReference<ArrayList<String>>() { });
+        assertEquals(CollectionType.class, t.getClass());
+        assertSame(ArrayList.class, t.getRawClass());
+
+        JavaType elemType = ((CollectionType) t).getContentType();
+        assertNotNull(elemType);
+        assertSame(SimpleType.class, elemType.getClass());
+        assertSame(String.class, elemType.getRawClass());
+
+        // And alternate method too
+        t = tf.constructCollectionType(ArrayList.class, String.class);
+        assertEquals(CollectionType.class, t.getClass());
+        assertSame(String.class, ((CollectionType) t).getContentType().getRawClass());
+    }
+    
+    public void testMaps()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // Ok, first: let's test what happens when we pass 'raw' Map:
+        JavaType t = tf.constructType(HashMap.class);
+        assertEquals(MapType.class, t.getClass());
+        assertSame(HashMap.class, t.getRawClass());
+
+        // Then explicit construction
+        t = tf.constructMapType(TreeMap.class, String.class, Integer.class);
+        assertEquals(MapType.class, t.getClass());
+        assertSame(String.class, ((MapType) t).getKeyType().getRawClass());
+        assertSame(Integer.class, ((MapType) t).getContentType().getRawClass());
+
+        // And then with TypeReference
+        t = tf.constructType(new TypeReference<HashMap<String,Integer>>() { });
+        assertEquals(MapType.class, t.getClass());
+        assertSame(HashMap.class, t.getRawClass());
+        MapType mt = (MapType) t;
+        assertEquals(tf.constructType(String.class), mt.getKeyType());
+        assertEquals(tf.constructType(Integer.class), mt.getContentType());
+
+        t = tf.constructType(new TypeReference<LongValuedMap<Boolean>>() { });
+        assertEquals(MapType.class, t.getClass());
+        assertSame(LongValuedMap.class, t.getRawClass());
+        mt = (MapType) t;
+        assertEquals(tf.constructType(Boolean.class), mt.getKeyType());
+        assertEquals(tf.constructType(Long.class), mt.getContentType());
+    }
+
+    public void testIterator()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType t = tf.constructType(new TypeReference<Iterator<String>>() { });
+        assertEquals(SimpleType.class, t.getClass());
+        assertSame(Iterator.class, t.getRawClass());
+        assertEquals(1, t.containedTypeCount());
+        assertEquals(tf.constructType(String.class), t.containedType(0));
+        assertNull(t.containedType(1));
+    }
+
+    /**
+     * Test for verifying that parametric types can be constructed
+     * programmatically
+     * 
+     * @since 1.5
+     */
+    public void testParametricTypes()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        // first, simple class based
+        JavaType t = tf.constructParametricType(ArrayList.class, String.class); // ArrayList<String>
+        assertEquals(CollectionType.class, t.getClass());
+        JavaType strC = tf.constructType(String.class);
+        assertEquals(1, t.containedTypeCount());
+        assertEquals(strC, t.containedType(0));
+        assertNull(t.containedType(1));
+
+        // Then using JavaType
+        JavaType t2 = tf.constructParametricType(Map.class, strC, t); // Map<String,ArrayList<String>>
+        // should actually produce a MapType
+        assertEquals(MapType.class, t2.getClass());
+        assertEquals(2, t2.containedTypeCount());
+        assertEquals(strC, t2.containedType(0));
+        assertEquals(t, t2.containedType(1));
+        assertNull(t2.containedType(2));
+
+        // and then custom generic type as well
+        JavaType custom = tf.constructParametricType(SingleArgGeneric.class, String.class);
+        assertEquals(SimpleType.class, custom.getClass());
+        assertEquals(1, custom.containedTypeCount());
+        assertEquals(strC, custom.containedType(0));
+        assertNull(custom.containedType(1));
+        // should also be able to access variable name:
+        assertEquals("X", custom.containedTypeName(0));
+
+        // And finally, ensure that we can't create invalid combinations
+        try {
+            // Maps must take 2 type parameters, not just one
+            tf.constructParametricType(Map.class, strC);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "Need exactly 2 parameter types for Map types");
+        }
+
+        try {
+            // Type only accepts one type param
+            tf.constructParametricType(SingleArgGeneric.class, strC, strC);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "expected 1 parameters, was given 2");
+        }
+    }
+
+    /**
+     * Test for checking that canonical name handling works ok
+     */
+    public void testCanonicalNames()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType t = tf.constructType(java.util.Calendar.class);
+        String can = t.toCanonical();
+        assertEquals("java.util.Calendar", can);
+        assertEquals(t, tf.constructFromCanonical(can));
+
+        // Generic maps and collections will default to Object.class if type-erased
+        t = tf.constructType(java.util.ArrayList.class);
+        can = t.toCanonical();
+        assertEquals("java.util.ArrayList<java.lang.Object>", can);
+        assertEquals(t, tf.constructFromCanonical(can));
+
+        t = tf.constructType(java.util.TreeMap.class);
+        can = t.toCanonical();
+        assertEquals("java.util.TreeMap<java.lang.Object,java.lang.Object>", can);
+        assertEquals(t, tf.constructFromCanonical(can));
+
+        // And then EnumMap (actual use case for us)
+        t = tf.constructMapType(EnumMap.class, EnumForCanonical.class, String.class);
+        can = t.toCanonical();
+        assertEquals("java.util.EnumMap<com.fasterxml.jackson.databind.type.TestTypeFactory$EnumForCanonical,java.lang.String>",
+                can);
+        assertEquals(t, tf.constructFromCanonical(can));
+        
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests: low-level inheritance resolution
+    /**********************************************************
+     */
+    
+    /**
+     * @since 1.6
+     */
+    public void testSuperTypeDetectionClass()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        HierarchicType sub = tf._findSuperTypeChain(MyStringIntMap.class, HashMap.class);
+        assertNotNull(sub);
+        assertEquals(2, _countSupers(sub));
+        assertSame(MyStringIntMap.class, sub.getRawClass());
+        HierarchicType sup = sub.getSuperType();
+        assertSame(MyStringXMap.class, sup.getRawClass());
+        HierarchicType sup2 = sup.getSuperType();
+        assertSame(HashMap.class, sup2.getRawClass());
+        assertNull(sup2.getSuperType());
+    }
+    
+    /**
+     * @since 1.6
+     */
+    public void testSuperTypeDetectionInterface()
+    {
+        // List first
+        TypeFactory tf = TypeFactory.defaultInstance();
+        HierarchicType sub = tf._findSuperTypeChain(MyList.class, List.class);
+        assertNotNull(sub);
+        assertEquals(2, _countSupers(sub));
+        assertSame(MyList.class, sub.getRawClass());
+        HierarchicType sup = sub.getSuperType();
+        assertSame(IntermediateList.class, sup.getRawClass());
+        HierarchicType sup2 = sup.getSuperType();
+        assertSame(List.class, sup2.getRawClass());
+        assertNull(sup2.getSuperType());
+        
+        // Then Map
+        sub = tf._findSuperTypeChain(MyMap.class, Map.class);
+        assertNotNull(sub);
+        assertEquals(2, _countSupers(sub));
+        assertSame(MyMap.class, sub.getRawClass());
+        sup = sub.getSuperType();
+        assertSame(IntermediateMap.class, sup.getRawClass());
+        sup2 = sup.getSuperType();
+        assertSame(Map.class, sup2.getRawClass());
+        assertNull(sup2.getSuperType());
+    }
+
+    /**
+     * @since 1.6
+     */
+    public void testAtomicArrayRefParameterDetection()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(new TypeReference<AtomicReference<long[]>>() { });
+        HierarchicType sub = tf._findSuperTypeChain(type.getRawClass(), AtomicReference.class);
+        assertNotNull(sub);
+        assertEquals(0, _countSupers(sub));
+        assertTrue(AtomicReference.class.isAssignableFrom(type.getRawClass()));
+        assertNull(sub.getSuperType());
+    }
+
+    private int _countSupers(HierarchicType t)
+    {
+        int depth = 0;
+        for (HierarchicType sup = t.getSuperType(); sup != null; sup = sup.getSuperType()) {
+            ++depth;
+        }
+        return depth;
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests: map/collection type parameter resolution
+    /**********************************************************
+     */
+    
+    /**
+     * @since 1.6
+     */
+    public void testMapTypesSimple()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(new TypeReference<Map<String,Boolean>>() { });
+        MapType mapType = (MapType) type;
+        assertEquals(tf.constructType(String.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Boolean.class), mapType.getContentType());
+    }
+
+    /**
+     * @since 1.6
+     */
+    public void testMapTypesRaw()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(HashMap.class);
+        MapType mapType = (MapType) type;
+        assertEquals(tf.constructType(Object.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Object.class), mapType.getContentType());        
+    }
+
+    /**
+     * @since 1.6
+     */
+    public void testMapTypesAdvanced()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(MyMap.class);
+        MapType mapType = (MapType) type;
+        assertEquals(tf.constructType(String.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Long.class), mapType.getContentType());
+
+        type = tf.constructType(MapInterface.class);
+        mapType = (MapType) type;
+        assertEquals(tf.constructType(String.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Integer.class), mapType.getContentType());
+
+        type = tf.constructType(MyStringIntMap.class);
+        mapType = (MapType) type;
+        assertEquals(tf.constructType(String.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Integer.class), mapType.getContentType());
+    }
+
+    /**
+     * Specific test to verify that complicate name mangling schemes
+     * do not fool type resolver
+     * 
+     * @since 1.6
+     */
+    public void testMapTypesSneaky()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(IntLongMap.class);
+        MapType mapType = (MapType) type;
+        assertEquals(tf.constructType(Integer.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Long.class), mapType.getContentType());
+    }    
+    
+    /**
+     * Plus sneaky types may be found via introspection as well.
+     * 
+     * @since 1.7
+     */
+    public void testSneakyFieldTypes() throws Exception
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        Field field = SneakyBean.class.getDeclaredField("intMap");
+        JavaType type = tf.constructType(field.getGenericType());
+        assertTrue(type instanceof MapType);
+        MapType mapType = (MapType) type;
+        assertEquals(tf.constructType(Integer.class), mapType.getKeyType());
+        assertEquals(tf.constructType(Long.class), mapType.getContentType());
+
+        field = SneakyBean.class.getDeclaredField("longList");
+        type = tf.constructType(field.getGenericType());
+        assertTrue(type instanceof CollectionType);
+        CollectionType collectionType = (CollectionType) type;
+        assertEquals(tf.constructType(Long.class), collectionType.getContentType());
+    }    
+    
+    /**
+     * Looks like type handling actually differs for properties, too.
+     * 
+     * @since 1.7
+     */
+    public void testSneakyBeanProperties() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        StringLongMapBean bean = mapper.readValue("{\"value\":{\"a\":123}}", StringLongMapBean.class);
+        assertNotNull(bean);
+        Map<String,Long> map = bean.value;
+        assertEquals(1, map.size());
+        assertEquals(Long.valueOf(123), map.get("a"));
+
+        StringListBean bean2 = mapper.readValue("{\"value\":[\"...\"]}", StringListBean.class);
+        assertNotNull(bean2);
+        List<String> list = bean2.value;
+        assertSame(GenericList.class, list.getClass());
+        assertEquals(1, list.size());
+        assertEquals("...", list.get(0));
+    }
+    
+    public void testAtomicArrayRefParameters()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(new TypeReference<AtomicReference<long[]>>() { });
+        JavaType[] params = tf.findTypeParameters(type, AtomicReference.class);
+        assertNotNull(params);
+        assertEquals(1, params.length);
+        assertEquals(tf.constructType(long[].class), params[0]);
+    }
+
+    public void testSneakySelfRefs() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        String json = mapper.writeValueAsString(new SneakyBean2());
+        assertEquals("{\"foobar\":null}", json);
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests: construction of "raw" types
+    /**********************************************************
+     */
+
+    public void testRawCollections()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructRawCollectionType(ArrayList.class);
+        assertTrue(type.isContainerType());
+        assertEquals(TypeFactory.unknownType(), type.getContentType());
+
+        type = tf.constructRawCollectionLikeType(String.class); // class doesn't really matter
+        assertTrue(type.isCollectionLikeType());
+        assertEquals(TypeFactory.unknownType(), type.getContentType());
+    }
+
+    public void testRawMaps()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructRawMapType(HashMap.class);
+        assertTrue(type.isContainerType());
+        assertEquals(TypeFactory.unknownType(), type.getKeyType());
+        assertEquals(TypeFactory.unknownType(), type.getContentType());
+
+        type = tf.constructRawMapLikeType(String.class); // class doesn't really matter
+        assertTrue(type.isMapLikeType());
+        assertEquals(TypeFactory.unknownType(), type.getKeyType());
+        assertEquals(TypeFactory.unknownType(), type.getContentType());
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests: other
+    /**********************************************************
+     */
+    
+    public void testMoreSpecificType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+
+        JavaType t1 = tf.constructCollectionType(Collection.class, Object.class);
+        JavaType t2 = tf.constructCollectionType(List.class, Object.class);
+        assertSame(t2, tf.moreSpecificType(t1, t2));
+        assertSame(t2, tf.moreSpecificType(t2, t1));
+
+        t1 = tf.constructType(Double.class);
+        t2 = tf.constructType(Number.class);
+        assertSame(t1, tf.moreSpecificType(t1, t2));
+        assertSame(t1, tf.moreSpecificType(t2, t1));
+
+        // and then unrelated, return first
+        t1 = tf.constructType(Double.class);
+        t2 = tf.constructType(String.class);
+        assertSame(t1, tf.moreSpecificType(t1, t2));
+        assertSame(t2, tf.moreSpecificType(t2, t1));
+    }
+}
+        
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java
new file mode 100644
index 0000000..30be92e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/type/TestTypeResolution.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.databind.type;
+
+import java.util.*;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+ at SuppressWarnings("serial")
+public class TestTypeResolution extends com.fasterxml.jackson.test.BaseTest
+{
+    public static class LongValuedMap<K> extends HashMap<K, Long> { }
+
+    static class GenericList<X> extends ArrayList<X> { }
+    static class GenericList2<Y> extends GenericList<Y> { }
+
+    static class LongList extends GenericList2<Long> { }
+    static class MyLongList<T> extends LongList { }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testMaps()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType t = tf.constructType(new TypeReference<LongValuedMap<String>>() { });
+        MapType type = (MapType) t;
+        assertSame(LongValuedMap.class, type.getRawClass());
+        assertEquals(tf.constructType(String.class), type.getKeyType());
+        assertEquals(tf.constructType(Long.class), type.getContentType());        
+    }
+
+    public void testList()
+    {
+        JavaType t;
+
+        TypeFactory tf = TypeFactory.defaultInstance();
+        t = tf.constructType(new TypeReference<MyLongList<Integer>>() {});
+        CollectionType type = (CollectionType) t;
+        assertSame(MyLongList.class, type.getRawClass());
+        assertEquals(tf.constructType(Long.class), type.getContentType());        
+
+        t = tf.constructType(LongList.class);
+        type = (CollectionType) t;
+        assertSame(LongList.class, type.getRawClass());
+        assertEquals(tf.constructType(Long.class), type.getContentType());        
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java
new file mode 100644
index 0000000..a788e28
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ArrayBuildersTest.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.databind.util;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+
+public class ArrayBuildersTest extends BaseMapTest
+{
+	// Test for [Issue#157]
+	public void testInsertInListNoDup()
+	{
+        String [] arr = new String[]{"me", "you", "him"};
+        String [] newarr;
+        
+        newarr = ArrayBuilders.insertInListNoDup(arr, "you");
+        Assert.assertArrayEquals(new String[]{"you", "me", "him"}, newarr);
+
+        newarr = ArrayBuilders.insertInListNoDup(arr, "me");
+        Assert.assertArrayEquals(new String[]{"me", "you","him"}, newarr);
+
+        newarr = ArrayBuilders.insertInListNoDup(arr, "him");
+        Assert.assertArrayEquals(new String[]{"him", "me", "you"}, newarr);
+
+        newarr = ArrayBuilders.insertInListNoDup(arr, "foobar");
+        Assert.assertArrayEquals(new String[]{"foobar", "me", "you", "him"}, newarr);
+	}
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java
new file mode 100644
index 0000000..5b98e2a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601DateFormatTest.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.text.DateFormat;
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+
+/**
+ * @see ISO8601DateFormat
+ */
+public class ISO8601DateFormatTest extends BaseMapTest
+{
+    private ISO8601DateFormat df;
+    private Date date;
+
+    @Override
+    public void setUp()
+    {
+        Calendar cal = new GregorianCalendar(2007, 8 - 1, 13, 19, 51, 23);
+        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
+        cal.set(Calendar.MILLISECOND, 0);
+        date = cal.getTime();
+        df = new ISO8601DateFormat();
+    }
+
+    public void testFormat() {
+        String result = df.format(date);
+        assertEquals("2007-08-13T19:51:23Z", result);
+    }
+
+    public void testParse() throws Exception {
+        Date result = df.parse("2007-08-13T19:51:23Z");
+        assertEquals(date, result);
+    }
+
+    public void testCloneObject() throws Exception {
+        DateFormat clone = (DateFormat)df.clone();
+        assertSame(df, clone);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
new file mode 100644
index 0000000..9a4275f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/ISO8601UtilsTest.java
@@ -0,0 +1,61 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
+
+/**
+ * @see ISO8601Utils
+ */
+public class ISO8601UtilsTest extends BaseMapTest
+{
+    private Date date;
+    private Date dateZeroMillis;
+
+    @Override
+    public void setUp()
+    {
+        Calendar cal = new GregorianCalendar(2007, 8 - 1, 13, 19, 51, 23);
+        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
+        cal.set(Calendar.MILLISECOND, 789);
+        date = cal.getTime();
+        cal.set(Calendar.MILLISECOND, 0);
+        dateZeroMillis = cal.getTime();
+    }
+
+    public void testFormat() {
+        String result = ISO8601Utils.format(date);
+        assertEquals("2007-08-13T19:51:23Z", result);
+    }
+
+    public void testFormatMillis() {
+        String result = ISO8601Utils.format(date, true);
+        assertEquals("2007-08-13T19:51:23.789Z", result);
+
+        result = ISO8601Utils.format(date, false);
+        assertEquals("2007-08-13T19:51:23Z", result);
+    }
+
+    public void testFormatTimeZone() {
+        String result = ISO8601Utils.format(date, false, TimeZone.getTimeZone("GMT+02:00"));
+        assertEquals("2007-08-13T21:51:23+02:00", result);
+        result = ISO8601Utils.format(date, true, TimeZone.getTimeZone("GMT+02:00"));
+        assertEquals("2007-08-13T21:51:23.789+02:00", result);
+        result = ISO8601Utils.format(date, true, TimeZone.getTimeZone("GMT"));
+        assertEquals("2007-08-13T19:51:23.789Z", result);
+    }
+
+    public void testParse() {
+        Date d = ISO8601Utils.parse("2007-08-13T19:51:23.789Z");
+        assertEquals(date, d);
+
+        d = ISO8601Utils.parse("2007-08-13T19:51:23Z");
+        assertEquals(dateZeroMillis, d);
+
+        d = ISO8601Utils.parse("2007-08-13T21:51:23.789+02:00");
+        assertEquals(date, d);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java b/src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java
new file mode 100644
index 0000000..ee51f3e
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestClassUtil.java
@@ -0,0 +1,140 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+import static org.junit.Assert.*;
+
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.util.ClassUtil;
+
+public class TestClassUtil
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Test classes, enums
+    /**********************************************************
+     */
+
+    /* Test classes and interfaces needed for testing class util
+     * methods
+     */
+    static abstract class BaseClass implements Comparable<BaseClass>,
+        BaseInt
+    {
+        BaseClass(String str) { }
+    }
+
+    interface BaseInt { }
+
+    interface SubInt extends BaseInt { }
+
+    enum TestEnum { A; }
+
+    abstract class InnerNonStatic { }
+
+    static class Inner {
+        protected Inner() {
+            throw new IllegalStateException("test");
+        }
+    }
+
+    static abstract class SubClass
+        extends BaseClass
+        implements SubInt {
+        SubClass() { super("x"); }
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    public void testSuperTypes()
+    {
+        Collection<Class<?>> result = ClassUtil.findSuperTypes(SubClass.class, null);
+        Class<?>[] classes = result.toArray(new Class<?>[result.size()]);
+        Class<?>[] exp = new Class[] {
+            SubInt.class, BaseInt.class,
+            BaseClass.class,
+            Comparable.class
+        };
+        assertArrayEquals(exp, classes);
+    }
+
+    public void testSuperInterfaces()
+    {
+        Collection<Class<?>> result = ClassUtil.findSuperTypes(SubInt.class, null);
+        Class<?>[] classes = result.toArray(new Class<?>[result.size()]);
+        Class<?>[] exp = new Class[] {
+            BaseInt.class
+        };
+        assertArrayEquals(exp, classes);
+    }
+    
+    public void testIsConcrete()
+    {
+        assertTrue(ClassUtil.isConcrete(getClass()));
+        assertFalse(ClassUtil.isConcrete(BaseClass.class));
+        assertFalse(ClassUtil.isConcrete(BaseInt.class));
+    }
+
+    public void testCanBeABeanType()
+    {
+        assertEquals("annotation", ClassUtil.canBeABeanType(java.lang.annotation.Retention.class));
+        assertEquals("array", ClassUtil.canBeABeanType(String[].class));
+        assertEquals("enum", ClassUtil.canBeABeanType(TestEnum.class));
+        assertEquals("primitive", ClassUtil.canBeABeanType(Integer.TYPE));
+        assertNull(ClassUtil.canBeABeanType(Integer.class));
+
+        assertEquals("non-static member class", ClassUtil.isLocalType(InnerNonStatic.class, false));
+        assertNull(ClassUtil.isLocalType(Integer.class, false));
+    }
+
+    public void testExceptionHelpers()
+    {
+        RuntimeException e = new RuntimeException("test");
+        RuntimeException wrapper = new RuntimeException(e);
+
+        assertSame(e, ClassUtil.getRootCause(wrapper));
+
+        try {
+            ClassUtil.throwAsIAE(e);
+            fail("Shouldn't get this far");
+        } catch (RuntimeException e2) {
+            assertSame(e, e2);
+        }
+
+        try {
+            ClassUtil.unwrapAndThrowAsIAE(wrapper);
+            fail("Shouldn't get this far");
+        } catch (RuntimeException e2) {
+            assertSame(e, e2);
+        }
+    }
+
+    public void testFailedCreateInstance()
+    {
+        try {
+            ClassUtil.createInstance(BaseClass.class, true);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "has no default");
+        }
+
+        try {
+            // false means ctor would need to be public
+            ClassUtil.createInstance(Inner.class, false);
+        } catch (IllegalArgumentException e) {
+            verifyException(e, "is not accessible");
+        }
+
+        // and finally, check that we'll get expected exception...
+        try {
+            ClassUtil.createInstance(Inner.class, true);
+        } catch (IllegalStateException e) {
+            verifyException(e, "test");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java b/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java
new file mode 100644
index 0000000..e709136
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestObjectBuffer.java
@@ -0,0 +1,71 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.util.*;
+
+import com.fasterxml.jackson.test.BaseTest;
+
+public class TestObjectBuffer
+    extends BaseTest
+{
+    /**
+     * First a test that treats results as plain old Object[]
+     */
+    public void testUntyped()
+    {
+        _testObjectBuffer(null);
+    }
+
+    public void testTyped()
+    {
+        _testObjectBuffer(Integer.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void _testObjectBuffer(Class<?> clz)
+    {
+        int[] SIZES = new int[] {
+            3, 19, 99, 1007, 79000, 256001
+        };
+
+        // Let's loop separately for reused instance, new instance
+        for (int reuse = 0; reuse < 2; ++reuse) {
+            ObjectBuffer buf = (reuse == 0) ? null : new ObjectBuffer();
+
+            // then distinct sizes
+            for (int sizeIndex = 0; sizeIndex < SIZES.length; ++sizeIndex) {
+                int size = SIZES[sizeIndex];
+                Random r = new Random(size);
+                ObjectBuffer thisBuf = (buf == null) ? new ObjectBuffer() : buf;
+                Object[] chunk = thisBuf.resetAndStart();
+                int ix = 0;
+
+                for (int i = 0; i < size; ++i) {
+                    if (ix >= chunk.length) {
+                        chunk = thisBuf.appendCompletedChunk(chunk);
+                        ix = 0;
+                    }
+                    chunk[ix++] = Integer.valueOf(r.nextInt());
+                }
+
+                Object[] result;
+                
+                if (clz == null) {
+                    result = thisBuf.completeAndClearBuffer(chunk, ix);
+                } else {
+                    result = thisBuf.completeAndClearBuffer(chunk, ix, clz);
+                }
+                assertEquals(size, result.length);
+
+                r = new Random(size);
+                for (int i = 0; i < size; ++i) {
+                    assertEquals(r.nextInt(), ((Integer) result[i]).intValue());
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
new file mode 100644
index 0000000..c17d5d7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/util/TestTokenBuffer.java
@@ -0,0 +1,298 @@
+package com.fasterxml.jackson.databind.util;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.util.JsonParserSequence;
+
+public class TestTokenBuffer extends com.fasterxml.jackson.test.BaseTest
+{
+    /*
+    /**********************************************************
+    /* Basic TokenBuffer tests
+    /**********************************************************
+     */
+    
+    /**
+     * Test writing of individual simple values
+     */
+    public void testSimpleWrites() throws IOException
+    {
+        TokenBuffer buf = new TokenBuffer(null); // no ObjectCodec
+
+        // First, with empty buffer
+        JsonParser jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertNull(jp.nextToken());
+        jp.close();
+
+        // Then with simple text
+        buf.writeString("abc");
+
+        // First, simple text
+        jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals("abc", jp.getText());
+        assertNull(jp.nextToken());
+        jp.close();
+
+        // Then, let's append at root level
+        buf.writeNumber(13);
+        jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(13, jp.getIntValue());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+    }
+
+    public void testSimpleArray() throws IOException
+    {
+        TokenBuffer buf = new TokenBuffer(null); // no ObjectCodec
+
+        // First, empty array
+        assertTrue(buf.getOutputContext().inRoot());
+        buf.writeStartArray();
+        assertTrue(buf.getOutputContext().inArray());
+        buf.writeEndArray();
+        assertTrue(buf.getOutputContext().inRoot());
+
+        JsonParser jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertTrue(jp.getParsingContext().inRoot());
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertTrue(jp.getParsingContext().inArray());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertTrue(jp.getParsingContext().inRoot());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+
+        // Then one with simple contents
+        buf = new TokenBuffer(null);
+        buf.writeStartArray();
+        buf.writeBoolean(true);
+        buf.writeNull();
+        buf.writeEndArray();
+        jp = buf.asParser();
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertTrue(jp.getBooleanValue());
+        assertToken(JsonToken.VALUE_NULL, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+
+        // And finally, with array-in-array
+        buf = new TokenBuffer(null);
+        buf.writeStartArray();
+        buf.writeStartArray();
+        buf.writeBinary(new byte[3]);
+        buf.writeEndArray();
+        buf.writeEndArray();
+        jp = buf.asParser();
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        // TokenBuffer exposes it as embedded object...
+        assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, jp.nextToken());
+        Object ob = jp.getEmbeddedObject();
+        assertNotNull(ob);
+        assertTrue(ob instanceof byte[]);
+        assertEquals(3, ((byte[]) ob).length);
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+    }
+
+    public void testSimpleObject() throws IOException
+    {
+        TokenBuffer buf = new TokenBuffer(null);
+
+        // First, empty JSON Object
+        assertTrue(buf.getOutputContext().inRoot());
+        buf.writeStartObject();
+        assertTrue(buf.getOutputContext().inObject());
+        buf.writeEndObject();
+        assertTrue(buf.getOutputContext().inRoot());
+
+        JsonParser jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertTrue(jp.getParsingContext().inRoot());
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertTrue(jp.getParsingContext().inObject());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertTrue(jp.getParsingContext().inRoot());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+
+        // Then one with simple contents
+        buf = new TokenBuffer(null);
+        buf.writeStartObject();
+        buf.writeNumberField("num", 1.25);
+        buf.writeEndObject();
+
+        jp = buf.asParser();
+        assertNull(jp.getCurrentToken());
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertNull(jp.getCurrentName());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        // and override should also work:
+        jp.overrideCurrentName("bah");
+        assertEquals("bah", jp.getCurrentName());
+        
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+        assertEquals(1.25, jp.getDoubleValue());
+        // should still have access to (overridden) name
+        assertEquals("bah", jp.getCurrentName());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        // but not any more
+        assertNull(jp.getCurrentName());
+        assertNull(jp.nextToken());
+        jp.close();
+        buf.close();
+    }
+
+    /**
+     * Verify handling of that "standard" test document (from JSON
+     * specification)
+     */
+    public void testWithJSONSampleDoc() throws Exception
+    {
+        // First, copy events from known good source (StringReader)
+        JsonParser jp = createParserUsingReader(SAMPLE_DOC_JSON_SPEC);
+        TokenBuffer tb = new TokenBuffer(null);
+        while (jp.nextToken() != null) {
+            tb.copyCurrentEvent(jp);
+        }
+
+        // And then request verification; first structure only:
+        verifyJsonSpecSampleDoc(tb.asParser(), false);
+
+        // then content check too:
+        verifyJsonSpecSampleDoc(tb.asParser(), true);
+        tb.close();
+    }
+
+    public void testAppend() throws IOException
+    {
+        TokenBuffer buf1 = new TokenBuffer(null);
+        buf1.writeStartObject();
+        buf1.writeFieldName("a");
+        buf1.writeBoolean(true);
+        
+        TokenBuffer buf2 = new TokenBuffer(null);
+        buf2.writeFieldName("b");
+        buf2.writeNumber(13);
+        buf2.writeEndObject();
+        
+        buf1.append(buf2);
+        
+        // and verify that we got it all...
+        JsonParser jp = buf1.asParser();
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("a", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("b", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(13, jp.getIntValue());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        jp.close();
+        buf1.close();
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests to verify interaction of TokenBuffer and JsonParserSequence
+    /**********************************************************
+     */
+    
+    public void testWithJsonParserSequenceSimple() throws IOException
+    {
+        // Let's join a TokenBuffer with JsonParser first
+        TokenBuffer buf = new TokenBuffer(null);
+        buf.writeStartArray();
+        buf.writeString("test");
+        JsonParser jp = createParserUsingReader("[ true, null ]");
+        
+        JsonParserSequence seq = JsonParserSequence.createFlattened(buf.asParser(), jp);
+        assertEquals(2, seq.containedParsersCount());
+
+        assertFalse(jp.isClosed());
+        
+        assertFalse(seq.hasCurrentToken());
+        assertNull(seq.getCurrentToken());
+        assertNull(seq.getCurrentName());
+
+        assertToken(JsonToken.START_ARRAY, seq.nextToken());
+        assertToken(JsonToken.VALUE_STRING, seq.nextToken());
+        assertEquals("test", seq.getText());
+        // end of first parser input, should switch over:
+        
+        assertToken(JsonToken.START_ARRAY, seq.nextToken());
+        assertToken(JsonToken.VALUE_TRUE, seq.nextToken());
+        assertToken(JsonToken.VALUE_NULL, seq.nextToken());
+        assertToken(JsonToken.END_ARRAY, seq.nextToken());
+
+        /* 17-Jan-2009, tatus: At this point, we may or may not get an
+         *   exception, depending on how underlying parsers work.
+         *   Ideally this should be fixed, probably by asking underlying
+         *   parsers to disable checking for balanced start/end markers.
+         */
+
+        // for this particular case, we won't get an exception tho...
+        assertNull(seq.nextToken());
+        // not an error to call again...
+        assertNull(seq.nextToken());
+
+        // also: original parsers should be closed
+        assertTrue(jp.isClosed());
+        jp.close();
+        buf.close();
+    }
+    
+    /**
+     * Test to verify that TokenBuffer and JsonParserSequence work together
+     * as expected.
+     */
+    public void testWithMultipleJsonParserSequences() throws IOException
+    {
+        TokenBuffer buf1 = new TokenBuffer(null);
+        buf1.writeStartArray();
+        TokenBuffer buf2 = new TokenBuffer(null);
+        buf2.writeString("a");
+        TokenBuffer buf3 = new TokenBuffer(null);
+        buf3.writeNumber(13);
+        TokenBuffer buf4 = new TokenBuffer(null);
+        buf4.writeEndArray();
+
+        JsonParserSequence seq1 = JsonParserSequence.createFlattened(buf1.asParser(), buf2.asParser());
+        assertEquals(2, seq1.containedParsersCount());
+        JsonParserSequence seq2 = JsonParserSequence.createFlattened(buf3.asParser(), buf4.asParser());
+        assertEquals(2, seq2.containedParsersCount());
+        JsonParserSequence combo = JsonParserSequence.createFlattened(seq1, seq2);
+        // should flatten it to have 4 underlying parsers
+        assertEquals(4, combo.containedParsersCount());
+
+        assertToken(JsonToken.START_ARRAY, combo.nextToken());
+        assertToken(JsonToken.VALUE_STRING, combo.nextToken());
+        assertEquals("a", combo.getText());
+        assertToken(JsonToken.VALUE_NUMBER_INT, combo.nextToken());
+        assertEquals(13, combo.getIntValue());
+        assertToken(JsonToken.END_ARRAY, combo.nextToken());
+        assertNull(combo.nextToken());        
+        buf1.close();
+        buf2.close();
+        buf3.close();
+        buf4.close();
+    }    
+}
diff --git a/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
new file mode 100644
index 0000000..e5e4edc
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewDeserialization.java
@@ -0,0 +1,104 @@
+package com.fasterxml.jackson.databind.views;
+
+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 { }
+    static class ViewB { }
+    static class ViewBB extends ViewB { }
+    
+    static class Bean
+    {
+        @JsonView(ViewA.class)
+        public int a;
+
+        @JsonView({ViewAA.class, ViewB.class})
+        public String aa;
+
+        protected int b;
+        
+        @JsonView(ViewB.class)
+        public void setB(int value) { b = value; }
+    }
+
+    static class DefaultsBean
+    {
+        public int a;
+
+        @JsonView(ViewA.class)
+        public int b;
+    }
+
+    /*
+    /************************************************************************ 
+    /* Tests
+    /************************************************************************ 
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+    
+    public void testSimple() throws Exception
+    {
+        // by default, should have it all...
+        Bean bean = mapper
+                .readValue("{\"a\":3, \"aa\":\"foo\", \"b\": 9 }", Bean.class);
+        assertEquals(3, bean.a);
+        assertEquals("foo", bean.aa);
+        assertEquals(9, bean.b);
+        
+        // but with different views, different contents
+        bean = mapper.readerWithView(ViewAA.class)
+                .withType(Bean.class)
+                .readValue("{\"a\":3, \"aa\":\"foo\", \"b\": 9 }");
+        // should include 'a' and 'aa' (as per view)
+        assertEquals(3, bean.a);
+        assertEquals("foo", bean.aa);
+        // but not 'b'
+        assertEquals(0, bean.b);
+
+        bean = mapper.readerWithView(ViewA.class)
+                .withType(Bean.class)
+                .readValue("{\"a\":1, \"aa\":\"x\", \"b\": 3 }");
+        assertEquals(1, bean.a);
+        assertNull(bean.aa);
+        assertEquals(0, bean.b);
+        
+        bean = mapper.reader(Bean.class)
+                .withView(ViewB.class)
+                .readValue("{\"a\":-3, \"aa\":\"y\", \"b\": 2 }");
+        assertEquals(0, bean.a);
+        assertEquals("y", bean.aa);
+        assertEquals(2, bean.b);
+    }
+
+    public void testWithoutDefaultInclusion() throws Exception
+    {
+        // without active view, all included still:
+        DefaultsBean bean = mapper
+                .readValue("{\"a\":3, \"b\": 9 }", DefaultsBean.class);
+        assertEquals(3, bean.a);
+        assertEquals(9, bean.b);
+
+        ObjectMapper myMapper = new ObjectMapper();
+        myMapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
+
+        // but with, say, AA, will not get 'b'
+        bean = myMapper.readerWithView(ViewAA.class)
+                .withType(DefaultsBean.class)
+                .readValue("{\"a\":1, \"b\": 2 }");
+        // 'a' not there any more
+        assertEquals(0, bean.a);
+        assertEquals(2, bean.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
new file mode 100644
index 0000000..c92065b
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewSerialization.java
@@ -0,0 +1,183 @@
+package com.fasterxml.jackson.databind.views;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Unit tests for verifying JSON view functionality: ability to declaratively
+ * suppress subset of properties from being serialized.
+ */
+public class TestViewSerialization
+    extends BaseMapTest
+{
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+
+    // Classes that represent views
+    static class ViewA { }
+    static class ViewAA extends ViewA { }
+    static class ViewB { }
+    static class ViewBB extends ViewB { }
+    
+    static class Bean
+    {
+        @JsonView(ViewA.class)
+        public String a = "1";
+
+        @JsonView({ViewAA.class, ViewB.class})
+        public String aa = "2";
+
+        @JsonView(ViewB.class)
+        public String getB() { return "3"; }
+    }
+
+    /**
+     * Bean with mix of explicitly annotated
+     * properties, and implicit ones that may or may
+     * not be included in views.
+     */
+    static class MixedBean
+    {
+        @JsonView(ViewA.class)
+        public String a = "1";
+
+        public String getB() { return "2"; }
+    }
+
+    /**
+     * As indicated by [JACKSON-261], @JsonView should imply
+     * that associated element (method, field) is to be considered
+     * a property
+     */
+    static class ImplicitBean {
+        @JsonView(ViewA.class)
+        private int a = 1;
+    }
+
+    static class VisibilityBean {
+        @JsonProperty protected String id = "id";
+    
+        @JsonView(ViewA.class)
+        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
+    /**********************************************************
+     */    
+    
+    @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);
+        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);
+        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);
+        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);
+        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);
+        assertEquals(2, map.size());
+        assertEquals("2", map.get("aa"));
+        assertEquals("3", map.get("b"));
+    }
+
+    /**
+     * Unit test to verify implementation of [JACKSON-232], to
+     * allow "opt-in" handling for JSON Views: that is, that
+     * default for properties is to exclude unless included in
+     * a view.
+     */
+    @SuppressWarnings("unchecked")
+    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);
+        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();
+        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
+        // with this setting, only explicit inclusions count:
+        String 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"));
+    }
+
+    /**
+     * As per [JACKSON-261], @JsonView annotation should imply that associated
+     * method/field does indicate a property.
+     */
+    public void testImplicitAutoDetection() throws Exception
+    {
+    	assertEquals("{\"a\":1}", serializeAsString(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);
+        //json = mapper.writeValueAsString(bean);
+        assertEquals("{\"id\":\"id\"}", json);
+    }
+
+    // [JACKSON-868]
+    public void test() throws IOException
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
+        String json = mapper.writerWithView(OtherView.class).writeValueAsString(new Foo());
+        assertEquals(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
new file mode 100644
index 0000000..eac65b2
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/databind/views/TestViewsSerialization2.java
@@ -0,0 +1,161 @@
+package com.fasterxml.jackson.databind.views;
+
+import java.io.*;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestViewsSerialization2 extends BaseMapTest
+{
+    /*
+    /************************************************************************ 
+    /* Tests
+    /************************************************************************ 
+     */
+    
+  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 ComplexTestData
+  {
+    String nameNull = null;
+
+    String nameComplex = "complexValue";
+
+    String nameComplexHidden = "nameComplexHiddenValue";
+
+    SimpleTestData testData = new SimpleTestData( );
+
+    SimpleTestData[] testDataArray = new SimpleTestData[] { new SimpleTestData( ), null };
+
+    @JsonView( Views.View.class )
+    public String getNameNull()
+    {
+      return nameNull;
+    }
+
+    public void setNameNull( String nameNull )
+    {
+      this.nameNull = nameNull;
+    }
+
+    @JsonView( Views.View.class )
+    public String getNameComplex()
+    {
+      return nameComplex;
+    }
+
+    public void setNameComplex( String nameComplex )
+    {
+      this.nameComplex = nameComplex;
+    }
+
+    public String getNameComplexHidden()
+    {
+      return nameComplexHidden;
+    }
+
+    public void setNameComplexHidden( String nameComplexHidden )
+    {
+      this.nameComplexHidden = nameComplexHidden;
+    }
+
+    @JsonView( Views.View.class )
+    public SimpleTestData getTestData()
+    {
+      return testData;
+    }
+
+    public void setTestData( SimpleTestData testData )
+    {
+      this.testData = testData;
+    }
+
+    @JsonView( Views.View.class )
+    public SimpleTestData[] getTestDataArray()
+    {
+      return testDataArray;
+    }
+
+    public void setTestDataArray( SimpleTestData[] testDataArray )
+    {
+      this.testDataArray = testDataArray;
+    }
+  }
+
+  static class SimpleTestData
+  {
+    String name = "shown";
+
+    String nameHidden = "hidden";
+
+    @JsonView( Views.View.class )
+    public String getName()
+    {
+      return name;
+    }
+
+    public void setName( String name )
+    {
+      this.name = name;
+    }
+
+    public String getNameHidden( )
+    {
+      return nameHidden;
+    }
+
+    public void setNameHidden( String nameHidden )
+    {
+      this.nameHidden = nameHidden;
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestBackRefsWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/failing/TestBackRefsWithPolymorphic.java
new file mode 100644
index 0000000..5d499db
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestBackRefsWithPolymorphic.java
@@ -0,0 +1,220 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+// Unit test for [JACKSON-890]
+public class TestBackRefsWithPolymorphic extends BaseMapTest
+{
+    private final String CLASS_NAME = getClass().getName();
+
+    // NOTE: order is arbitrary, test is fragile... has to work for now
+    private final String JSON =
+        "{\"@class\":\""+CLASS_NAME+"$PropertySheetImpl\",\"id\":0,\"properties\":{\"p1name\":{\"@class\":"
+            +"\"" +CLASS_NAME+ "$StringPropertyImpl\",\"id\":0,\"name\":\"p1name\",\"value\":\"p1value\"},"
+            +"\"p2name\":{\"@class\":\""+CLASS_NAME+"$StringPropertyImpl\",\"id\":0,"
+            +"\"name\":\"p2name\",\"value\":\"p2value\"}}}";
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    public void testDeserialize() throws IOException
+    {
+        PropertySheet input = MAPPER.readValue(JSON, PropertySheet.class);
+        assertEquals(JSON, MAPPER.writeValueAsString(input));
+    }
+
+    public void testSerialize() throws IOException
+    {
+        PropertySheet sheet = new PropertySheetImpl();
+
+        sheet.addProperty(new StringPropertyImpl("p1name", "p1value"));
+        sheet.addProperty(new StringPropertyImpl("p2name", "p2value"));
+        String actual = MAPPER.writeValueAsString(sheet);
+        assertEquals(JSON, actual);
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    interface Entity
+    {
+        @JsonIgnore String getEntityType();
+        Long getId();
+        void setId(Long id);
+        @JsonIgnore void setPersistable();
+    }
+
+    @JsonDeserialize(as = NestedPropertySheetImpl.class)
+    interface NestedPropertySheet
+        extends Property<PropertySheet>
+    {
+        @Override PropertySheet getValue();
+        void setValue(PropertySheet propertySheet);
+    }
+
+    @JsonDeserialize(as = AbstractProperty.class)
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,
+        include  = JsonTypeInfo.As.PROPERTY,
+        property = "@class")
+    interface Property<T> extends Entity
+    {
+        String getName();
+        PropertySheet getParentSheet();
+        T getValue();
+        void setName(String name);
+        void setParentSheet(PropertySheet parentSheet);
+    }
+
+    @JsonDeserialize(as = PropertySheetImpl.class)
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS,
+        include  = JsonTypeInfo.As.PROPERTY,
+        property = "@class")
+    @SuppressWarnings("rawtypes")
+    interface PropertySheet extends Entity
+    {
+        void addProperty(Property property);
+        Map<String, Property> getProperties();
+        void setProperties(Map<String, Property> properties);
+    }
+
+    @JsonDeserialize(as = StringPropertyImpl.class)
+    interface StringProperty
+        extends Property<String>
+    {
+        @Override String getValue();
+        void setValue(String value);
+    }
+
+    static class AbstractEntity implements Entity
+    {
+        private long id;
+
+        @Override public String getEntityType() {
+            return "";
+        }
+
+        @Override public Long getId() {
+            return id;
+        }
+
+        @Override public void setId(Long id)
+        {
+            this.id = id;
+        }
+
+        @Override public void setPersistable() { }
+    }
+
+    abstract static class AbstractProperty<T>
+        extends AbstractEntity
+        implements Property<T>
+    {
+        private String        m_name;
+        private PropertySheet m_parentSheet;
+
+        protected AbstractProperty() { }
+
+        protected AbstractProperty(String name) {
+            m_name = name;
+        }
+
+        @Override public String getName() {
+            return m_name;
+        }
+
+        @JsonBackReference("propertySheet-properties")
+        @Override public PropertySheet getParentSheet() {
+            return m_parentSheet;
+        }
+
+        @Override public void setName(String name) {
+            m_name = name;
+        }
+
+        @Override public void setParentSheet(PropertySheet parentSheet) {
+            m_parentSheet = parentSheet;
+        }
+    }
+
+    @JsonPropertyOrder(alphabetic=true)
+    static class NestedPropertySheetImpl
+        extends AbstractProperty<PropertySheet>
+        implements NestedPropertySheet
+    {
+        private PropertySheet m_propertySheet;
+
+        protected NestedPropertySheetImpl(String name,
+                PropertySheet propertySheet)
+        {
+            super(name);
+            m_propertySheet = propertySheet;
+        }
+
+        NestedPropertySheetImpl() { }
+
+        @Override public PropertySheet getValue() {
+            return m_propertySheet;
+        }
+
+        @Override public void setValue(PropertySheet propertySheet) {
+            m_propertySheet = propertySheet;
+        }
+    }
+
+    @SuppressWarnings("rawtypes")
+    static class PropertySheetImpl
+        extends AbstractEntity
+        implements PropertySheet
+    {
+        private Map<String, Property> m_properties;
+
+        @Override public void addProperty(Property property)
+        {
+            if (m_properties == null) {
+                m_properties = new TreeMap<String, Property>();
+            }
+            property.setParentSheet(this);
+            m_properties.put(property.getName(), property);
+        }
+
+        @JsonDeserialize(as = TreeMap.class,
+            keyAs     = String.class,
+            contentAs = Property.class)
+        @JsonManagedReference("propertySheet-properties")
+        @Override public Map<String, Property> getProperties() {
+            return m_properties;
+        }
+
+        @Override public void setProperties(Map<String, Property> properties) {
+            m_properties = properties;
+        }
+    }
+
+    static class StringPropertyImpl
+        extends AbstractProperty<String>
+        implements StringProperty
+    {
+        private String m_value;
+
+        public StringPropertyImpl(String name, String value) {
+            super(name);
+            m_value = value;
+        }
+
+        StringPropertyImpl() { }
+
+        @Override public String getValue() {
+            return m_value;
+        }
+
+        @Override public void setValue(String value) {
+            m_value = value;
+        }
+    }
+
+    static class YetAnotherClass extends StringPropertyImpl { }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java b/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java
new file mode 100644
index 0000000..ab68534
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestExternalTypeId.java
@@ -0,0 +1,110 @@
+package com.fasterxml.jackson.failing;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+public class TestExternalTypeId extends BaseMapTest
+{
+	@SuppressWarnings("unused")
+	public void testTypes() throws IOException {
+        final ObjectMapper mapper = new ObjectMapper();
+        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+        final Point _date = new Point(new Date());
+        final Point _integer = new Point(12231321);
+        final Point _boolean = new Point(Boolean.TRUE);
+        final Point _long = new Point(1234L);
+
+        final Point _pojo = new Point(new Pojo(1));
+        final String s_date = mapper.writeValueAsString(_date);
+        final String s_integer = mapper.writeValueAsString(_integer);
+
+//System.err.println("Int -> "+s_integer);   
+    
+        final String s_boolean = mapper.writeValueAsString(_boolean);
+        final String s_long = mapper.writeValueAsString(_long);
+        final String s_pojo = mapper.writeValueAsString(_pojo);
+
+        final Point d_date = mapper.readValue(s_date, Point.class);
+        final Point d_long = mapper.readValue(s_long, Point.class);
+        final Point d_pojo = mapper.readValue(s_pojo, Point.class);
+        final Point d_integer = mapper.readValue(s_integer, Point.class);
+        final Point d_boolean = mapper.readValue(s_boolean, Point.class);
+    }
+
+    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY,
+        getterVisibility = JsonAutoDetect.Visibility.NONE,
+        setterVisibility = JsonAutoDetect.Visibility.NONE)
+    static class Point {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+        property = "t",
+        visible = true,
+        include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
+        defaultImpl = String.class)
+        @JsonSubTypes({
+        @JsonSubTypes.Type(value = Date.class, name = "date"),
+        @JsonSubTypes.Type(value = Integer.class, name = "int"),
+        @JsonSubTypes.Type(value = Long.class, name = "long"),
+        @JsonSubTypes.Type(value = Boolean.class, name = "bool"),
+        @JsonSubTypes.Type(value = Pojo.class, name = "pojo"),
+        @JsonSubTypes.Type(value = String.class, name = "")
+        })
+        private final Object v;
+    
+        @JsonCreator
+        public Point(@JsonProperty("v") Object v) {
+            this.v = v;
+        }
+    
+        public Object getValue() {
+            return v;
+        }
+    }
+     
+
+    static class Pojo {
+        public final int p;
+
+        @JsonCreator
+        private Pojo(@JsonProperty("p") int p) {
+            this.p = p;
+        }
+    }
+
+    // [Issue#222]
+    static class Issue222Bean
+    {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
+                property = "type",
+                include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
+        public Issue222BeanB value;
+
+        public String type = "foo";
+        
+        public Issue222Bean() { }
+        public Issue222Bean(int v) {
+            value = new Issue222BeanB(v);
+        }
+    }
+
+    static class Issue222BeanB
+    {
+        public int x;
+        
+        public Issue222BeanB() { }
+        public Issue222BeanB(int value) { x = value; }
+    }
+
+    public void testIssue222() throws Exception
+    {
+        final ObjectMapper mapper = new ObjectMapper();
+        Issue222Bean input = new Issue222Bean(13);
+        String json = mapper.writeValueAsString(input);
+        assertEquals("{\"value\":{\"x\":13},\"type\":\"foo\"}", json);
+    }
+    
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestInnerClass.java b/src/test/java/com/fasterxml/jackson/failing/TestInnerClass.java
new file mode 100644
index 0000000..cb4bce4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestInnerClass.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestInnerClass extends BaseMapTest
+{
+    public static class Dog2
+    {
+        public String name;
+        public List<Leg> legs;
+
+        // NOTE: non-static on purpose!
+        public class Leg {
+            public int length;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests
+    /**********************************************************
+     */
+    
+    // core/[Issue#32]
+    public void testInnerList() throws Exception
+    {
+        Dog2 dog = new Dog2();
+        dog.name = "Spike";
+        dog.legs = new ArrayList<Dog2.Leg>();
+        dog.legs.add(dog.new Leg());
+        dog.legs.add(dog.new Leg());
+        dog.legs.get(0).length = 5;
+        dog.legs.get(1).length = 4;
+
+        ObjectMapper mapper = new ObjectMapper();
+
+        String dogJson = mapper.writeValueAsString(dog);
+//        System.out.println(dogJson);
+      // output: {"name":"Spike","legs":[{length: 5}, {length: 4}]}
+
+        // currently throws JsonMappingException
+        Dog2 dogCopy = mapper.readValue(dogJson, Dog2.class);
+        assertEquals(dogCopy.legs.get(1).length, 4);
+        // prefer fully populated Dog instance
+    }
+}
+
+
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestIssueGH113.java b/src/test/java/com/fasterxml/jackson/failing/TestIssueGH113.java
new file mode 100644
index 0000000..2670743
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestIssueGH113.java
@@ -0,0 +1,65 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestIssueGH113 extends BaseMapTest
+{
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_class")
+    @JsonSubTypes({  @JsonSubTypes.Type(Dog.class) })
+    public static abstract class Animal {
+        public final static String ID = "id";
+
+        private String id;
+        public Animal() {
+        }
+
+        @JsonCreator
+        public Animal(@JsonProperty(ID) String id) {
+            this.id = id;
+        }
+
+        @JsonProperty(ID)
+        public String getId() {
+            return id;
+        }
+    }
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class Dog extends Animal {
+        public Dog() {
+            super();
+        }
+
+        @JsonCreator
+        public Dog(@JsonProperty(ID) String id) {
+            super(id);
+        }
+    }
+
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    public static class AnimalWrapper {
+        private Animal animal;
+
+        @JsonCreator
+        public AnimalWrapper(@JsonProperty("animal") Animal animal) {
+            this.animal = animal;
+        }
+
+        public Animal getAnimal() {
+            return animal;
+        }
+    }
+
+    public void testSubtypes() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+
+        String id = "nice dogy";
+        String serializedDog = mapper.writeValueAsString(new AnimalWrapper(new Dog(id)));
+        AnimalWrapper wrapper = mapper.readValue(serializedDog, AnimalWrapper.class);
+        assertEquals(id, wrapper.getAnimal().getId());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestJavaType.java b/src/test/java/com/fasterxml/jackson/failing/TestJavaType.java
new file mode 100644
index 0000000..64f7dfe
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestJavaType.java
@@ -0,0 +1,24 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Failing test related to [Issue#76]
+ */
+public class TestJavaType
+    extends com.fasterxml.jackson.test.BaseTest
+{
+    @SuppressWarnings("serial")
+    static class HashTree<K, V> extends HashMap<K, HashTree<K, V>> { }
+
+    public void testRecursiveType()
+    {
+        TypeFactory tf = TypeFactory.defaultInstance();
+        JavaType type = tf.constructType(HashTree.class);
+        assertNotNull(type);
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey.java b/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey.java
new file mode 100644
index 0000000..0be19b3
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestMapJsonValueKey.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.HashMap;
+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.ObjectMapper;
+import com.fasterxml.jackson.test.BaseTest;
+
+public class TestMapJsonValueKey extends BaseTest
+{
+    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 "[Wat: " + wat + "]";
+        }
+    }
+
+    public void testMapJsonValueKey()
+    throws Exception
+    {
+        Map<Wat, Boolean> map = new HashMap<Wat, Boolean>();
+        map.put(new Wat("3"), true);
+        map.put(new Wat("x"), false);
+
+        TypeReference<Map<Wat, Boolean>> type = new TypeReference<Map<Wat, Boolean>>(){};
+
+        ObjectMapper mapper = new ObjectMapper();
+        assertEquals(map, mapper.readValue(mapper.writeValueAsBytes(map), type));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestNameConflicts.java b/src/test/java/com/fasterxml/jackson/failing/TestNameConflicts.java
new file mode 100644
index 0000000..e5eec34
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestNameConflicts.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestNameConflicts extends BaseMapTest
+{
+    static class Bean193
+    {
+        @JsonProperty("val1")
+        private int x;
+        @JsonIgnore
+        private int value2;
+        
+        public Bean193(@JsonProperty("val1")int value1,
+                    @JsonProperty("val2")int value2)
+        {
+            this.x = value1;
+            this.value2 = value2;
+        }
+        
+        @JsonProperty("val2")
+        int x()
+        {
+            return value2;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    public void testIssue193() throws Exception
+    {
+        String json = objectWriter().writeValueAsString(new Bean193(1, 2));
+        assertNotNull(json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestObjectIdSerialization.java b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdSerialization.java
new file mode 100644
index 0000000..aa9b9a6
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestObjectIdSerialization.java
@@ -0,0 +1,94 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JsonIdentityInfo;
+import com.fasterxml.jackson.annotation.JsonIdentityReference;
+import com.fasterxml.jackson.annotation.ObjectIdGenerators;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Tests for github ussues #138
+ * https://github.com/FasterXML/jackson-databind/issues/138
+ */
+public class TestObjectIdSerialization extends BaseMapTest {
+
+    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+    static protected class Obj {
+
+        public int id;
+        @JsonIdentityReference(alwaysAsId = true)
+        public SetContainer objGroup;
+
+        public Obj() {
+        }
+
+        public Obj(int id) {
+            this.id = id;
+        }
+    }
+
+    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+    static protected class SetContainer {
+
+        public int id;
+
+        @JsonIdentityReference(alwaysAsId = true)
+        // NOTE: uncommenting this makes test pass:
+//        @com.fasterxml.jackson.databind.annotation.JsonSerialize(contentAs = Obj.class)
+        public Set<Obj> objs = new LinkedHashSet<Obj>();
+
+        public SetContainer() { }
+
+        public SetContainer(int id) {
+            this.id = id;
+        }
+    }
+
+    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
+    static protected class ArrContainer {
+
+        public int id;
+        @JsonIdentityReference(alwaysAsId = true)
+        public Obj[] objs;
+
+        public ArrContainer() {
+        }
+
+        public ArrContainer(int id) {
+            this.id = id;
+        }
+    }
+    private final ObjectMapper MAPPER = new ObjectMapper();
+    private final static String EXP_ARR_SET = "{\"id\":4,\"objs\":[1,7]}";
+
+    public void testArraySerialization() throws Exception {
+        Obj o = new Obj(1);
+        Obj o2 = new Obj(7);
+        ArrContainer oa = new ArrContainer(4);
+        oa.objs = new Obj[]{o, o2};
+        String json = MAPPER.writeValueAsString(oa);
+        assertEquals(EXP_ARR_SET, json);
+    }
+
+    public void testCollectionSerialization() throws Exception {
+        Obj o = new Obj(1);
+        Obj o2 = new Obj(7);
+        SetContainer os = new SetContainer(4);
+        os.objs.add(o);
+        os.objs.add(o2);
+        String json = MAPPER.writeValueAsString(os);
+        assertEquals(EXP_ARR_SET, json);
+    }
+
+    public void testDeserialization() throws Exception {
+        Obj o = new Obj(1);
+        o.objGroup = new SetContainer(4);
+        String json = MAPPER.writeValueAsString(o);
+        Obj deser = MAPPER.readValue(json, Obj.class);
+        assertEquals(deser.id, 1);
+        assertEquals(deser.objGroup.id, 4);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java b/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java
new file mode 100644
index 0000000..31fde8d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestPOJOAsArray.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonFormat.Shape;
+
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TestPOJOAsArray extends BaseMapTest
+{
+    // for [JACKSON-805]
+    @JsonFormat(shape=Shape.ARRAY)
+    static class SingleBean {
+        public String name = "foo";
+    }
+
+    
+    // for [JACKSON-805]
+    public void testBeanAsArrayUnwrapped() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+        SingleBean result = mapper.readValue(quote("foobar"), SingleBean.class);
+        assertNotNull(result);
+        assertEquals("foobar", result.name);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestSerializationFiltering.java b/src/test/java/com/fasterxml/jackson/failing/TestSerializationFiltering.java
new file mode 100644
index 0000000..bad5901
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestSerializationFiltering.java
@@ -0,0 +1,51 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.HashMap;
+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.ser.FilterProvider;
+import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
+import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
+
+/**
+ * Currently (1.8) generic filtering does not work for "any getter": it should,
+ * ideally, so here's the test.
+ */
+public class TestSerializationFiltering extends BaseMapTest
+{
+    @JsonFilter("anyFilter")
+    public static class AnyBean
+    {
+        private Map<String, String> properties = new HashMap<String, String>();
+        {
+          properties.put("a", "1");
+          properties.put("b", "2");
+        }
+
+        @JsonAnyGetter
+        public Map<String, String> anyProperties()
+        {
+          return properties;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+    
+    // should also work for @JsonAnyGetter, as per [JACKSON-516]
+    public void testAnyGetterFiltering() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        FilterProvider prov = new SimpleFilterProvider().addFilter("anyFilter",
+                SimpleBeanPropertyFilter.filterOutAllExcept("b"));
+        assertEquals("{\"a\":\"1\"}", mapper.writer(prov).writeValueAsString(new AnyBean()));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestTreeWithType.java b/src/test/java/com/fasterxml/jackson/failing/TestTreeWithType.java
new file mode 100644
index 0000000..5842792
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestTreeWithType.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.failing;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.*;
+
+public class TestTreeWithType extends BaseMapTest
+{
+    public static class Foo {
+        public String bar;
+
+        public Foo() { }
+
+        public Foo(String bar) {
+            this.bar = bar;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Unit tests
+    /**********************************************************
+     */
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    public void testValueAsStringWithoutDefaultTyping() throws Exception {
+
+        Foo foo = new Foo("baz");
+        String json = mapper.writeValueAsString(foo);
+
+        JsonNode jsonNode = mapper.readTree(json);
+        assertEquals(jsonNode.get("bar").textValue(), foo.bar);
+    }
+
+    public void testValueAsStringWithDefaultTyping() throws Exception {
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+
+        Foo foo = new Foo("baz");
+        String json = mapper.writeValueAsString(foo);
+
+        JsonNode jsonNode = mapper.readTree(json);
+        assertEquals(jsonNode.get("bar").textValue(), foo.bar);
+    }
+
+    public void testReadTreeWithDefaultTyping() throws Exception
+    {
+        final String CLASS = Foo.class.getName();
+
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL,
+                JsonTypeInfo.As.PROPERTY);
+        String json = "{\"@class\":\""+CLASS+"\",\"bar\":\"baz\"}";
+        JsonNode jsonNode = mapper.readTree(json);
+        assertEquals(jsonNode.get("bar").textValue(), "baz");
+    }
+
+    public void testValueToTreeWithoutDefaultTyping() throws Exception {
+
+        Foo foo = new Foo("baz");
+        JsonNode jsonNode = mapper.valueToTree(foo);
+        assertEquals(jsonNode.get("bar").textValue(), foo.bar);
+    }
+
+    public void testValueToTreeWithDefaultTyping() throws Exception {
+        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+
+        Foo foo = new Foo("baz");
+        JsonNode jsonNode = mapper.valueToTree(foo);
+        assertEquals(jsonNode.get("bar").textValue(), foo.bar);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestTypeAliases.java b/src/test/java/com/fasterxml/jackson/failing/TestTypeAliases.java
new file mode 100644
index 0000000..49c82a4
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestTypeAliases.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.*;
+
+import com.fasterxml.jackson.databind.*;
+
+public class TestTypeAliases
+    extends BaseMapTest
+{
+
+    // Helper types for [JACKSON-743]
+    
+    public static abstract class Base<T> {
+        public T inconsequential = null;
+    }
+
+    public static abstract class BaseData<T> {
+        public T dataObj;
+    }
+   
+    public static class Child extends Base<Long> {
+        public static class ChildData extends BaseData<List<String>> { }
+    }
+
+    /*
+    /*******************************************************
+    /* Unit tests
+    /*******************************************************
+     */
+
+
+    // Reproducing issue 743
+    public void testResolution743() throws Exception
+    {
+        String s3 = "{\"dataObj\" : [ \"one\", \"two\", \"three\" ] }";
+        ObjectMapper m = new ObjectMapper();
+   
+        Child.ChildData d = m.readValue(s3, Child.ChildData.class);
+        assertNotNull(d.dataObj);
+        assertEquals(3, d.dataObj.size());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithPrefix.java b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithPrefix.java
new file mode 100644
index 0000000..f672608
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/failing/TestUnwrappedWithPrefix.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.failing;
+
+import java.util.Collections;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonUnwrapped;
+import com.fasterxml.jackson.databind.BaseMapTest;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+// Tests for [#171]
+public class TestUnwrappedWithPrefix extends BaseMapTest
+{
+    static class MapUnwrap {
+
+        public MapUnwrap() { }
+        public MapUnwrap(String key, Object value) {
+            map = Collections.singletonMap(key, value);
+        }
+
+        @JsonUnwrapped(prefix="map.")
+        public Map<String, Object> map;
+    }
+    
+    // // // Reuse mapper to keep tests bit faster
+
+    private final ObjectMapper MAPPER = new ObjectMapper();
+
+    /*
+    /**********************************************************
+    /* Tests, serialization
+    /**********************************************************
+     */
+
+    public void testMapUnwrapSerialize() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new MapUnwrap("test", 6));
+        assertEquals("{\"map.test\": 6}", json);
+    }
+
+    /*
+    /**********************************************************
+    /* Tests, deserialization
+    /**********************************************************
+     */
+
+    public void testMapUnwrapDeserialize() throws Exception
+    {
+        MapUnwrap root = MAPPER.readValue("{\"map.test\": 6}", MapUnwrap.class);
+
+        assertEquals(1, root.map.size());
+        assertEquals(6, ((Number)root.map.get("test")).intValue());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/test/BaseTest.java b/src/test/java/com/fasterxml/jackson/test/BaseTest.java
new file mode 100644
index 0000000..9e8453a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/test/BaseTest.java
@@ -0,0 +1,411 @@
+package com.fasterxml.jackson.test;
+
+import java.io.*;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import com.fasterxml.jackson.core.*;
+
+//import static org.junit.Assert.*;
+
+public abstract class BaseTest
+    extends TestCase
+{
+    /*
+    /**********************************************************
+    /* Some sample documents:
+    /**********************************************************
+     */
+
+    protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800;
+    protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600;
+    protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor";
+    protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943";
+    protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125;
+    protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100";
+    protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116;
+    protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943;
+    protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234;
+    protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793;
+
+    protected final static String SAMPLE_DOC_JSON_SPEC = 
+        "{\n"
+        +"  \"Image\" : {\n"
+        +"    \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n"
+        +"    \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+","
+        +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n"
+        +"    \"Thumbnail\" : {\n"
+        +"      \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n"
+        +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n"
+        +"      \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n"
+        +"    },\n"
+        +"    \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n"
+        +"  }"
+        +"}"
+        ;
+
+    /*
+    /**********************************************************
+    /* Helper classes (beans)
+    /**********************************************************
+     */
+    
+    /**
+     * Sample class from Jackson tutorial ("JacksonInFiveMinutes")
+     */
+    protected static class FiveMinuteUser {
+        public enum Gender { MALE, FEMALE };
+
+        public static class Name
+        {
+          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 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;
+        private Name _name;
+        private boolean _isVerified;
+        private byte[] _userImage;
+
+        public FiveMinuteUser() { }
+
+        public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data)
+        {
+            _name = new Name(first, last);
+            _isVerified = verified;
+            _gender = g;
+            _userImage = data;
+        }
+        
+        public Name getName() { return _name; }
+        public boolean isVerified() { return _isVerified; }
+        public Gender getGender() { return _gender; }
+        public byte[] getUserImage() { return _userImage; }
+
+        public void setName(Name n) { _name = n; }
+        public void setVerified(boolean b) { _isVerified = b; }
+        public void setGender(Gender g) { _gender = g; }
+        public void setUserImage(byte[] b) { _userImage = b; }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o == this) return true;
+            if (o == null || o.getClass() != getClass()) return false;
+            FiveMinuteUser other = (FiveMinuteUser) o;
+            if (_isVerified != other._isVerified) return false;
+            if (_gender != other._gender) return false; 
+            if (!_name.equals(other._name)) return false;
+            byte[] otherImage = other._userImage;
+            if (otherImage.length != _userImage.length) return false;
+            for (int i = 0, len = _userImage.length; i < len; ++i) {
+                if (_userImage[i] != otherImage[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* High-level helpers
+    /**********************************************************
+     */
+
+    protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents)
+        throws IOException
+    {
+        verifyJsonSpecSampleDoc(jp, verifyContents, true);
+    }
+
+    protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents,
+            boolean requireNumbers)
+        throws IOException
+    {
+        if (!jp.hasCurrentToken()) {
+            jp.nextToken();
+        }
+        assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); // main object
+
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image'
+        if (verifyContents) {
+            verifyFieldName(jp, "Image");
+        }
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object
+
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+        if (verifyContents) {
+            verifyFieldName(jp, "Width");
+        }
+
+        verifyIntToken(jp.nextToken(), requireNumbers);
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH);
+        }
+
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+        if (verifyContents) {
+            verifyFieldName(jp, "Height");
+        }
+
+        verifyIntToken(jp.nextToken(), requireNumbers);
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT);
+        }
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title'
+        if (verifyContents) {
+            verifyFieldName(jp, "Title");
+        }
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp));
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail'
+        if (verifyContents) {
+            verifyFieldName(jp, "Thumbnail");
+        }
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url'
+        if (verifyContents) {
+            verifyFieldName(jp, "Url");
+        }
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        if (verifyContents) {
+            assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp));
+        }
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height'
+        if (verifyContents) {
+            verifyFieldName(jp, "Height");
+        }
+        verifyIntToken(jp.nextToken(), requireNumbers);
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT);
+        }
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width'
+        if (verifyContents) {
+            verifyFieldName(jp, "Width");
+        }
+        // Width value is actually a String in the example
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        if (verifyContents) {
+            assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp));
+        }
+
+        assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs'
+        assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array
+        verifyIntToken(jp.nextToken(), requireNumbers); // ids[0]
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1);
+        }
+        verifyIntToken(jp.nextToken(), requireNumbers); // ids[1]
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2);
+        }
+        verifyIntToken(jp.nextToken(), requireNumbers); // ids[2]
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3);
+        }
+        verifyIntToken(jp.nextToken(), requireNumbers); // ids[3]
+        if (verifyContents) {
+            verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4);
+        }
+        assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array
+
+        assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object
+
+        assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object
+    }
+
+    private void verifyIntToken(JsonToken t, boolean requireNumbers)
+    {
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            return;
+        }
+        if (requireNumbers) { // to get error
+            assertToken(JsonToken.VALUE_NUMBER_INT, t);
+        }
+        // if not number, must be String
+        if (t != JsonToken.VALUE_STRING) {
+            fail("Expected INT or STRING value, got "+t);
+        }
+    }
+    
+    protected void verifyFieldName(JsonParser jp, String expName)
+        throws IOException
+    {
+        assertEquals(expName, jp.getText());
+        assertEquals(expName, jp.getCurrentName());
+    }
+
+    protected void verifyIntValue(JsonParser jp, long expValue)
+        throws IOException
+    {
+        // First, via textual
+        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
+    /**********************************************************
+     */
+
+    protected JsonParser createParserUsingReader(String input)
+        throws IOException, JsonParseException
+    {
+        return createParserUsingReader(new JsonFactory(), input);
+    }
+
+    protected JsonParser createParserUsingReader(JsonFactory f, String input)
+        throws IOException, JsonParseException
+    {
+        return f.createParser(new StringReader(input));
+    }
+
+    protected JsonParser createParserUsingStream(String input, String encoding)
+        throws IOException, JsonParseException
+    {
+        return createParserUsingStream(new JsonFactory(), input, encoding);
+    }
+
+    protected JsonParser createParserUsingStream(JsonFactory f,
+                                                 String input, String encoding)
+        throws IOException, JsonParseException
+    {
+
+        /* 23-Apr-2008, tatus: UTF-32 is not supported by JDK, have to
+         *   use our own codec too (which is not optimal since there's
+         *   a chance both encoder and decoder might have bugs, but ones
+         *   that cancel each other out or such)
+         */
+        byte[] data;
+        if (encoding.equalsIgnoreCase("UTF-32")) {
+            data = encodeInUTF32BE(input);
+        } else {
+            data = input.getBytes(encoding);
+        }
+        InputStream is = new ByteArrayInputStream(data);
+        return f.createParser(is);
+    }
+
+    /*
+    /**********************************************************
+    /* Additional assertion methods
+    /**********************************************************
+     */
+
+    protected void assertToken(JsonToken expToken, JsonToken actToken)
+    {
+        if (actToken != expToken) {
+            fail("Expected token "+expToken+", current token "+actToken);
+        }
+    }
+
+    protected void assertToken(JsonToken expToken, JsonParser jp)
+    {
+        assertToken(expToken, jp.getCurrentToken());
+    }
+
+    protected void assertType(Object ob, Class<?> expType)
+    {
+        if (ob == null) {
+            fail("Expected an object of type "+expType.getName()+", got null");
+        }
+        Class<?> cls = ob.getClass();
+        if (!expType.isAssignableFrom(cls)) {
+            fail("Expected type "+expType.getName()+", got "+cls.getName());
+        }
+    }
+
+    protected void verifyException(Throwable e, String... matches)
+    {
+        String msg = e.getMessage();
+        String lmsg = (msg == null) ? "" : msg.toLowerCase();
+        for (String match : matches) {
+            String lmatch = match.toLowerCase();
+            if (lmsg.indexOf(lmatch) >= 0) {
+                return;
+            }
+        }
+        fail("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\"");
+    }
+
+    /**
+     * Method that gets textual contents of the current token using
+     * available methods, and ensures results are consistent, before
+     * returning them
+     */
+    protected String getAndVerifyText(JsonParser jp)
+        throws IOException, JsonParseException
+    {
+        // Ok, let's verify other accessors
+        int actLen = jp.getTextLength();
+        char[] ch = jp.getTextCharacters();
+        String str2 = new String(ch, jp.getTextOffset(), actLen);
+        String str = jp.getText();
+
+        if (str.length() !=  actLen) {
+            fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen);
+        }
+        assertEquals("String access via getText(), getTextXxx() must be the same", str, str2);
+
+        return str;
+    }
+
+    /*
+    /**********************************************************
+    /* And other helpers
+    /**********************************************************
+     */
+
+    protected byte[] encodeInUTF32BE(String input)
+    {
+        int len = input.length();
+        byte[] result = new byte[len * 4];
+        int ptr = 0;
+        for (int i = 0; i < len; ++i, ptr += 4) {
+            char c = input.charAt(i);
+            result[ptr] = result[ptr+1] = (byte) 0;
+            result[ptr+2] = (byte) (c >> 8);
+            result[ptr+3] = (byte) c;
+        }
+        return result;
+    }
+
+    public String quote(String str) {
+        return '"'+str+'"';
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java b/src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java
new file mode 100644
index 0000000..0022290
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/test/BrokenStringReader.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.test;
+
+import java.io.*;
+
+public class BrokenStringReader
+    extends FilterReader
+{
+    final String _message;
+
+    public BrokenStringReader(String content, String msg)
+    {
+        super(new StringReader(content));
+        _message = msg;
+    }
+
+    @Override
+    public int read(char[] cbuf, int off, int len)
+        throws IOException
+    {
+        int i = super.read(cbuf, off, len);
+        if (i < 0) {
+            throw new IOException(_message);
+        }
+        return i;
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java b/src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java
new file mode 100644
index 0000000..8bf4248
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/test/BrokenStringWriter.java
@@ -0,0 +1,32 @@
+package com.fasterxml.jackson.test;
+
+import java.io.*;
+
+public class BrokenStringWriter
+    extends FilterWriter
+{
+    final String _message;
+
+    public BrokenStringWriter(String msg) {
+        super(new StringWriter());
+        _message = msg;
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException
+    {
+        throw new IOException(_message);
+    }
+    
+    @Override
+    public void write(int c) throws IOException
+    {
+        throw new IOException(_message);
+    }
+    
+    @Override
+    public void write(String str, int off, int len)  throws IOException
+    {
+        throw new IOException(_message);
+    }
+}
diff --git a/src/test/java/perf/ManualObjectWriterPerf.java b/src/test/java/perf/ManualObjectWriterPerf.java
new file mode 100644
index 0000000..06c1205
--- /dev/null
+++ b/src/test/java/perf/ManualObjectWriterPerf.java
@@ -0,0 +1,113 @@
+package perf;
+
+import com.fasterxml.jackson.databind.*;
+
+public class ManualObjectWriterPerf
+{
+    protected int hash;
+    
+    private <T1, T2> void test(ObjectMapper mapper,
+            T1 inputValue1, Class<T1> inputClass1,
+            T2 inputValue2, Class<T2> inputClass2)
+        throws Exception
+    {
+        final int REPS;
+        {
+            final byte[] input1 = mapper.writeValueAsBytes(inputValue1);
+            final byte[] input2 = mapper.writeValueAsBytes(inputValue2);
+            
+            // Let's try to guestimate suitable size, N megs of output
+            REPS = (int) ((double) (9 * 1000 * 1000) / (double) input1.length);
+            System.out.printf("Read %d bytes to bind (%d as array); will do %d repetitions\n",
+                    input1.length, input2.length, REPS);
+        }
+
+        final ObjectWriter writer0 = mapper.writer().with(SerializationFeature.EAGER_SERIALIZER_FETCH);
+        final ObjectWriter jsonWriter = writer0.withType(inputClass1);
+        final ObjectWriter arrayWriter = writer0.withType(inputClass2);
+        
+        int i = 0;
+        int roundsDone = 0;
+        final int TYPES = 2;
+        final int WARMUP_ROUNDS = 5;
+
+        final long[] times = new long[TYPES];
+
+        while (true) {
+            final NopOutputStream out = new NopOutputStream();
+            try {  Thread.sleep(100L); } catch (InterruptedException ie) { }
+            int round = (i++ % TYPES);
+
+            String msg;
+            boolean lf = (round == 0);
+
+            long msecs;
+            ObjectWriter writer;
+            Object value;
+            
+            switch (round) {
+            case 0:
+                msg = "JSON-as-Object";
+                writer = jsonWriter;
+                value = inputValue1;
+                break;
+            case 1:
+                msg = "JSON-as-Array";
+                writer = arrayWriter;
+                value = inputValue2;
+                break;
+            default:
+            	out.close(); // silly eclipse juno
+                throw new Error();
+            }
+            msecs = testSer(REPS, value, writer, out);
+
+            // skip first 5 rounds to let results stabilize
+            if (roundsDone >= WARMUP_ROUNDS) {
+                times[round] += msecs;
+            }
+            
+            System.out.printf("Test '%s' [hash: 0x%s] -> %d msecs\n", msg, this.hash, msecs);
+            if (lf) {
+                ++roundsDone;
+                if ((roundsDone % 3) == 0 && roundsDone > WARMUP_ROUNDS) {
+                    double den = (double) (roundsDone - WARMUP_ROUNDS);
+                    System.out.printf("Averages after %d rounds (pre / no): %.1f / %.1f msecs\n",
+                            (int) den,
+                            times[0] / den, times[1] / den);
+                            
+                }
+                System.out.println();
+            }
+            if ((i % 17) == 0) {
+                System.out.println("[GC]");
+                Thread.sleep(100L);
+                System.gc();
+                Thread.sleep(100L);
+            }
+        }
+    }
+
+    private final long testSer(int REPS, Object value, ObjectWriter writer, NopOutputStream out) throws Exception
+    {
+        long start = System.currentTimeMillis();
+        while (--REPS >= 0) {
+            writer.writeValue(out, value);
+        }
+        hash = out.size();
+        return System.currentTimeMillis() - start;
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        if (args.length != 0) {
+            System.err.println("Usage: java ...");
+            System.exit(1);
+        }
+        Record input1 = new Record(44, "BillyBob", "Bumbler", 'm', true);
+        RecordAsArray input2 = new RecordAsArray(44, "BillyBob", "Bumbler", 'm', true);
+        ObjectMapper m = new ObjectMapper();
+        new ManualObjectWriterPerf().test(m,
+                input1, Record.class, input2, RecordAsArray.class);
+    }
+}
diff --git a/src/test/java/perf/ManualReadPerfWithMedia.java b/src/test/java/perf/ManualReadPerfWithMedia.java
new file mode 100644
index 0000000..b20f5f3
--- /dev/null
+++ b/src/test/java/perf/ManualReadPerfWithMedia.java
@@ -0,0 +1,46 @@
+package perf;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+
+public class ManualReadPerfWithMedia extends ObjectReaderBase
+{
+ 
+    public static void main(String[] args) throws Exception
+    {
+        if (args.length != 0) {
+            System.err.println("Usage: java ...");
+            System.exit(1);
+        }
+        MediaItem.Content content = new MediaItem.Content();
+        content.setTitle("Performance micro-benchmark, to be run manually");
+        content.addPerson("William");
+        content.addPerson("Robert");
+        content.setWidth(900);
+        content.setHeight(120);
+        content.setBitrate(256000);
+        content.setDuration(3600 * 1000L);
+        content.setCopyright("none");
+        content.setPlayer(MediaItem.Player.FLASH);
+        content.setUri("http://whatever.biz");
+
+        MediaItem input = new MediaItem(content);
+        input.addPhoto(new MediaItem.Photo("http://a.com", "title1", 200, 100, MediaItem.Size.LARGE));
+        input.addPhoto(new MediaItem.Photo("http://b.org", "title2", 640, 480, MediaItem.Size.SMALL));
+
+        ObjectMapper m1 = new ObjectMapper();
+        m1.setAnnotationIntrospector(new NoFormatIntrospector());
+        ObjectMapper m2 = new ObjectMapper();
+        new ManualReadPerfWithRecord().test(m1, "JSON-as-Object", input, MediaItem.class,
+                m2, "JSON-as-Array", input, MediaItem.class);
+    }
+
+    final static class NoFormatIntrospector extends JacksonAnnotationIntrospector
+    {
+        private static final long serialVersionUID = 1L;
+        @Override
+        public JsonFormat.Value findFormat(Annotated a) { return null; }
+    }
+}
diff --git a/src/test/java/perf/ManualReadPerfWithRecord.java b/src/test/java/perf/ManualReadPerfWithRecord.java
new file mode 100644
index 0000000..a5de358
--- /dev/null
+++ b/src/test/java/perf/ManualReadPerfWithRecord.java
@@ -0,0 +1,24 @@
+package perf;
+
+import com.fasterxml.jackson.databind.*;
+
+/**
+ * Simple manually run micro-benchmark for checking effects of (de)serializer
+ * pre-fetching
+ */
+public class ManualReadPerfWithRecord extends ObjectReaderBase
+{
+ 
+    public static void main(String[] args) throws Exception
+    {
+        if (args.length != 0) {
+            System.err.println("Usage: java ...");
+            System.exit(1);
+        }
+        Record input = new Record(44, "BillyBob", "Bumbler", 'm', true);
+        RecordAsArray input2 = new RecordAsArray(44, "BillyBob", "Bumbler", 'm', true);
+        ObjectMapper m = new ObjectMapper();
+        new ManualReadPerfWithRecord().test(m, "JSON-as-Object", input, Record.class,
+                m, "JSON-as-Array", input2, RecordAsArray.class);
+    }
+}
diff --git a/src/test/java/perf/MediaItem.java b/src/test/java/perf/MediaItem.java
new file mode 100644
index 0000000..9e09c28
--- /dev/null
+++ b/src/test/java/perf/MediaItem.java
@@ -0,0 +1,126 @@
+package perf;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+ at JsonFormat(shape=JsonFormat.Shape.ARRAY)
+ at JsonPropertyOrder({"content", "images"})
+public class MediaItem
+{
+    public enum Player { JAVA, FLASH;  }
+    public enum Size { SMALL, LARGE; }
+
+    private List<Photo> _photos;
+    private Content _content;
+
+    public MediaItem() { }
+
+    public MediaItem(Content c)
+    {
+        _content = c;
+    }
+
+    public void addPhoto(Photo p) {
+        if (_photos == null) {
+            _photos = new ArrayList<Photo>();
+        }
+        _photos.add(p);
+    }
+    
+    public List<Photo> getImages() { return _photos; }
+    public void setImages(List<Photo> p) { _photos = p; }
+
+    public Content getContent() { return _content; }
+    public void setContent(Content c) { _content = c; }
+
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+    
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder({"uri","title","width","height","size"})
+    public static class Photo
+    {
+        private String _uri;
+        private String _title;
+        private int _width;
+        private int _height;
+        private Size _size;
+    
+        public Photo() {}
+        public Photo(String uri, String title, int w, int h, Size s)
+        {
+          _uri = uri;
+          _title = title;
+          _width = w;
+          _height = h;
+          _size = s;
+        }
+    
+      public String getUri() { return _uri; }
+      public String getTitle() { return _title; }
+      public int getWidth() { return _width; }
+      public int getHeight() { return _height; }
+      public Size getSize() { return _size; }
+    
+      public void setUri(String u) { _uri = u; }
+      public void setTitle(String t) { _title = t; }
+      public void setWidth(int w) { _width = w; }
+      public void setHeight(int h) { _height = h; }
+      public void setSize(Size s) { _size = s; }
+    }
+      
+    @JsonFormat(shape=JsonFormat.Shape.ARRAY)
+    @JsonPropertyOrder({"uri","title","width","height","format","duration","size","bitrate","persons","player","copyright"})
+    public static class Content
+    {
+        private Player _player;
+        private String _uri;
+        private String _title;
+        private int _width;
+        private int _height;
+        private String _format;
+        private long _duration;
+        private long _size;
+        private int _bitrate;
+        private List<String> _persons;
+        private String _copyright;
+    
+        public Content() { }
+
+        public void addPerson(String p) {
+            if (_persons == null) {
+                _persons = new ArrayList<String>();
+            }
+            _persons.add(p);
+        }
+        
+        public Player getPlayer() { return _player; }
+        public String getUri() { return _uri; }
+        public String getTitle() { return _title; }
+        public int getWidth() { return _width; }
+        public int getHeight() { return _height; }
+        public String getFormat() { return _format; }
+        public long getDuration() { return _duration; }
+        public long getSize() { return _size; }
+        public int getBitrate() { return _bitrate; }
+        public List<String> getPersons() { return _persons; }
+        public String getCopyright() { return _copyright; }
+    
+        public void setPlayer(Player p) { _player = p; }
+        public void setUri(String u) {  _uri = u; }
+        public void setTitle(String t) {  _title = t; }
+        public void setWidth(int w) {  _width = w; }
+        public void setHeight(int h) {  _height = h; }
+        public void setFormat(String f) {  _format = f;  }
+        public void setDuration(long d) {  _duration = d; }
+        public void setSize(long s) {  _size = s; }
+        public void setBitrate(int b) {  _bitrate = b; }
+        public void setPersons(List<String> p) {  _persons = p; }
+        public void setCopyright(String c) {  _copyright = c; }
+    }
+}
diff --git a/src/test/java/perf/NopOutputStream.java b/src/test/java/perf/NopOutputStream.java
new file mode 100644
index 0000000..4ef7f92
--- /dev/null
+++ b/src/test/java/perf/NopOutputStream.java
@@ -0,0 +1,22 @@
+package perf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class NopOutputStream extends OutputStream
+{
+    protected int size = 0;
+    
+    public NopOutputStream() { }
+
+    @Override
+    public void write(int b) throws IOException { ++size; }
+
+    @Override
+    public void write(byte[] b) throws IOException { size += b.length; }
+
+    @Override
+    public void write(byte[] b, int offset, int len) throws IOException { size += len; }
+
+    public int size() { return size; }
+}
diff --git a/src/test/java/perf/ObjectReaderBase.java b/src/test/java/perf/ObjectReaderBase.java
new file mode 100644
index 0000000..80082f1
--- /dev/null
+++ b/src/test/java/perf/ObjectReaderBase.java
@@ -0,0 +1,106 @@
+package perf;
+
+import com.fasterxml.jackson.databind.*;
+
+abstract class ObjectReaderBase
+{
+    protected int hash;
+
+    protected <T1, T2> void test(ObjectMapper mapper1, String desc1,
+            T1 inputValue1, Class<T1> inputClass1,
+            ObjectMapper mapper2, String desc2,
+            T2 inputValue2, Class<T2> inputClass2)
+        throws Exception
+    {
+        final byte[] byteInput1 = mapper1.writeValueAsBytes(inputValue1);
+        final byte[] byteInput2 = mapper2.writeValueAsBytes(inputValue2);
+
+        desc1 = String.format("%s (%d bytes)", desc1, byteInput1.length);
+        desc2 = String.format("%s (%d bytes)", desc2, byteInput2.length);
+
+        // sanity check:
+        {
+            /*T1 back1 =*/ mapper1.readValue(byteInput1, inputClass1);
+            /*T2 back2 =*/ mapper2.readValue(byteInput2, inputClass2);
+            System.out.println("Input successfully round-tripped for both styles...");
+        }
+
+        // Let's try to guestimate suitable size... to get to N megs to process
+        final int REPS = (int) ((double) (8 * 1000 * 1000) / (double) byteInput1.length);
+
+        System.out.printf("Read %d bytes to bind (%d as array); will do %d repetitions\n",
+                byteInput1.length, byteInput2.length, REPS);
+
+        final ObjectReader jsonReader = mapper1.reader()
+                .with(DeserializationFeature.EAGER_DESERIALIZER_FETCH)
+                .withType(inputClass1);
+        final ObjectReader arrayReader = mapper2.reader()
+                .with(DeserializationFeature.EAGER_DESERIALIZER_FETCH)
+                .withType(inputClass2);
+        
+        int i = 0;
+        int roundsDone = 0;
+        final int TYPES = 2;
+        final int WARMUP_ROUNDS = 5;
+
+        final long[] times = new long[TYPES];
+        
+        while (true) {
+            try {  Thread.sleep(100L); } catch (InterruptedException ie) { }
+            int round = (i++ % TYPES);
+
+            String msg;
+            boolean lf = (round == 0);
+
+            long msecs;
+            
+            switch (round) {
+            case 0:
+                msg = desc1;
+                msecs = testDeser(REPS, byteInput1, jsonReader);
+                break;
+            case 1:
+                msg = desc2;
+                msecs = testDeser(REPS, byteInput2, arrayReader);
+                break;
+            default:
+                throw new Error();
+            }
+
+            // skip first 5 rounds to let results stabilize
+            if (roundsDone >= WARMUP_ROUNDS) {
+                times[round] += msecs;
+            }
+            
+            System.out.printf("Test '%s' [hash: 0x%s] -> %d msecs\n", msg, this.hash, msecs);
+            if (lf) {
+                ++roundsDone;
+                if ((roundsDone % 3) == 0 && roundsDone > WARMUP_ROUNDS) {
+                    double den = (double) (roundsDone - WARMUP_ROUNDS);
+                    System.out.printf("Averages after %d rounds (Object / Array): %.1f / %.1f msecs\n",
+                            (int) den,
+                            times[0] / den, times[1] / den);
+                            
+                }
+                System.out.println();
+            }
+            if ((i % 17) == 0) {
+                System.out.println("[GC]");
+                Thread.sleep(100L);
+                System.gc();
+                Thread.sleep(100L);
+            }
+        }
+    }
+
+    private final long testDeser(int REPS, byte[] input, ObjectReader reader) throws Exception
+    {
+        long start = System.currentTimeMillis();
+        Object result = null;
+        while (--REPS >= 0) {
+            result = reader.readValue(input);
+        }
+        hash = result.hashCode();
+        return System.currentTimeMillis() - start;
+    }
+}
diff --git a/src/test/java/perf/Record.java b/src/test/java/perf/Record.java
new file mode 100644
index 0000000..a7a7d00
--- /dev/null
+++ b/src/test/java/perf/Record.java
@@ -0,0 +1,14 @@
+package perf;
+
+/**
+ * Simple test class 
+ */
+final class Record extends RecordBase
+{
+    protected Record() { super(); }
+    
+    public Record(int a, String fn, String ln, char g, boolean ins)
+    {
+        super(a, fn, ln, g, ins);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/perf/RecordAsArray.java b/src/test/java/perf/RecordAsArray.java
new file mode 100644
index 0000000..af76bc3
--- /dev/null
+++ b/src/test/java/perf/RecordAsArray.java
@@ -0,0 +1,14 @@
+package perf;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+ at JsonFormat(shape=JsonFormat.Shape.ARRAY)
+final class RecordAsArray extends RecordBase
+{
+    protected RecordAsArray() { super(); }
+    
+    public RecordAsArray(int a, String fn, String ln, char g, boolean ins)
+    {
+        super(a, fn, ln, g, ins);
+    }
+}
diff --git a/src/test/java/perf/RecordBase.java b/src/test/java/perf/RecordBase.java
new file mode 100644
index 0000000..1d9ad01
--- /dev/null
+++ b/src/test/java/perf/RecordBase.java
@@ -0,0 +1,19 @@
+package perf;
+
+public class RecordBase
+{
+    public int age;
+    public String firstName, lastName;
+    public char gender;
+    public boolean insured;
+
+    protected RecordBase() { }
+    protected RecordBase(int a, String fn, String ln, char g, boolean ins)
+    {
+        age = a;
+        firstName = fn;
+        lastName = ln;
+        gender = g;
+        insured = ins;
+    }
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jackson-databind.git



More information about the pkg-java-commits mailing list