[jackson-datatype-guava] 01/05: Imported Upstream version 2.4.2

Tim Potter tpot-guest at moszumanska.debian.org
Mon Nov 3 23:25:19 UTC 2014


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

tpot-guest pushed a commit to branch master
in repository jackson-datatype-guava.

commit 3dd3f5da291745f5b4ac78fe63df0b7e890afd87
Author: Tim Potter <tpot at hp.com>
Date:   Fri Oct 24 11:56:14 2014 +1100

    Imported Upstream version 2.4.2
---
 .gitignore                                         |  22 ++
 DEV/contributor-agreement.pdf                      | Bin 0 -> 45328 bytes
 README.md                                          |  42 ++++
 pom.xml                                            |  92 ++++++++
 release-notes/VERSION                              |  91 ++++++++
 .../jackson/datatype/guava/GuavaDeserializers.java | 237 ++++++++++++++++++++
 .../jackson/datatype/guava/GuavaModule.java        |  39 ++++
 .../jackson/datatype/guava/GuavaSerializers.java   |  52 +++++
 .../jackson/datatype/guava/GuavaTypeModifier.java  |  64 ++++++
 .../jackson/datatype/guava/PackageVersion.java.in  |  20 ++
 .../guava/deser/GuavaCollectionDeserializer.java   | 129 +++++++++++
 .../GuavaImmutableCollectionDeserializer.java      |  76 +++++++
 .../guava/deser/GuavaImmutableMapDeserializer.java |  57 +++++
 .../datatype/guava/deser/GuavaMapDeserializer.java | 135 ++++++++++++
 .../guava/deser/GuavaMultisetDeserializer.java     |  70 ++++++
 .../guava/deser/GuavaOptionalDeserializer.java     | 126 +++++++++++
 .../guava/deser/HashMultisetDeserializer.java      |  31 +++
 .../guava/deser/HostAndPortDeserializer.java       |  42 ++++
 .../guava/deser/ImmutableBiMapDeserializer.java    |  26 +++
 .../guava/deser/ImmutableListDeserializer.java     |  37 ++++
 .../guava/deser/ImmutableMapDeserializer.java      |  31 +++
 .../guava/deser/ImmutableMultisetDeserializer.java |  27 +++
 .../guava/deser/ImmutableSetDeserializer.java      |  30 +++
 .../deser/ImmutableSortedMapDeserializer.java      |  36 ++++
 .../deser/ImmutableSortedSetDeserializer.java      |  38 ++++
 .../deser/LinkedHashMultisetDeserializer.java      |  26 +++
 .../datatype/guava/deser/RangeDeserializer.java    | 181 ++++++++++++++++
 .../guava/deser/TreeMultisetDeserializer.java      |  29 +++
 .../deser/multimap/GuavaMultimapDeserializer.java  | 183 ++++++++++++++++
 .../list/ArrayListMultimapDeserializer.java        |  43 ++++
 .../list/LinkedListMultimapDeserializer.java       |  43 ++++
 .../multimap/set/HashMultimapDeserializer.java     |  43 ++++
 .../set/LinkedHashMultimapDeserializer.java        |  43 ++++
 .../datatype/guava/deser/util/RangeFactory.java    | 181 ++++++++++++++++
 .../guava/ser/GuavaBeanSerializerModifier.java     |  26 +++
 .../guava/ser/GuavaOptionalBeanPropertyWriter.java |  26 +++
 .../guava/ser/GuavaOptionalSerializer.java         |  55 +++++
 .../datatype/guava/ser/MultimapSerializer.java     | 177 +++++++++++++++
 .../datatype/guava/ser/RangeSerializer.java        | 112 ++++++++++
 src/main/resources/META-INF/LICENSE                |   8 +
 .../services/com.fasterxml.jackson.databind.Module |   1 +
 .../jackson/datatype/guava/FluentIterableTest.java |  45 ++++
 .../jackson/datatype/guava/HostAndPortTest.java    |  41 ++++
 .../jackson/datatype/guava/IterablesTest.java      |  16 ++
 .../jackson/datatype/guava/ModuleTestBase.java     |  40 ++++
 .../jackson/datatype/guava/TestImmutables.java     | 240 +++++++++++++++++++++
 .../jackson/datatype/guava/TestMultimaps.java      | 205 ++++++++++++++++++
 .../jackson/datatype/guava/TestMultisets.java      | 110 ++++++++++
 .../jackson/datatype/guava/TestOptional.java       | 176 +++++++++++++++
 .../guava/TestOptionalWithPolymorphic.java         | 106 +++++++++
 .../jackson/datatype/guava/TestRange.java          |  78 +++++++
 .../jackson/datatype/guava/TestVersions.java       |  38 ++++
 52 files changed, 3822 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..66c4cfe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+# use glob syntax.
+syntax: glob
+*.class
+*~
+*.bak
+*.off
+*.old
+.DS_Store
+
+# building
+target
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# IDEA
+*.iml
+*.ipr
+*.iws
+.idea/
diff --git a/DEV/contributor-agreement.pdf b/DEV/contributor-agreement.pdf
new file mode 100644
index 0000000..fe36036
Binary files /dev/null and b/DEV/contributor-agreement.pdf differ
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..009b175
--- /dev/null
+++ b/README.md
@@ -0,0 +1,42 @@
+Project to build [Jackson](http://jackson.codehaus.org) module (jar)
+to support JSON serialization and deserialization of
+[Guava](http://code.google.com/p/guava-libraries/) collection types.
+
+[![Build Status](https://fasterxml.ci.cloudbees.com/job/jackson-datatype-guava-master/badge/icon)](https://fasterxml.ci.cloudbees.com/job/jackson-datatype-guava-master/)
+
+## Status
+
+As of version 2.3, module is production ready. Not all datatypes of Guava are support due to sheer
+size of the library; new support is added based on contributions.
+
+## Usage
+
+### Maven dependency
+
+To use module on Maven-based projects, use following dependency:
+
+```xml
+<dependency>
+  <groupId>com.fasterxml.jackson.datatype</groupId>
+  <artifactId>jackson-datatype-guava</artifactId>
+  <version>2.4.0</version>
+</dependency>
+```
+
+(or whatever version is most up-to-date at the moment)
+
+### Registering module
+
+Like all standard Jackson modules (libraries that implement Module interface), registration is done as follows:
+
+```java
+ObjectMapper mapper = new ObjectMapper();
+mapper.registerModule(new GuavaModule());
+```
+
+after which functionality is available for all normal Jackson operations.
+
+## More
+
+See [Wiki](jackson-datatype-guava/wiki) for more information (javadocs, downloads).
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a7593bf
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,92 @@
+<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.jackson</groupId>
+    <artifactId>jackson-parent</artifactId>
+    <version>2.4</version>
+  </parent>
+  <groupId>com.fasterxml.jackson.datatype</groupId>
+  <artifactId>jackson-datatype-guava</artifactId>
+  <name>Jackson-datatype-Guava</name>
+  <version>2.4.2</version>
+  <packaging>bundle</packaging>
+  <description>Add-on datatype-support module for Jackson (http://jackson.codehaus.org) that handles
+Guava (http://code.google.com/p/guava-libraries/) types (currently mostly just collection ones)
+  </description>
+  <url>http://wiki.fasterxml.com/JacksonModuleGuava</url>
+  <scm>
+    <connection>scm:git:git at github.com:FasterXML/jackson-datatype-guava.git</connection>
+    <developerConnection>scm:git:git at github.com:FasterXML/jackson-datatype-guava.git</developerConnection>
+    <url>http://github.com/FasterXML/jackson-datatype-guava</url>    
+    <tag>jackson-datatype-guava-2.4.2</tag>
+  </scm>
+
+  <contributors>
+    <contributor>
+      <name>Steven Schlansker</name>
+      <email>steven at nesscomputing.com</email>
+    </contributor>
+  </contributors>
+
+  <properties>
+    <version.jackson>2.4.2</version.jackson>
+
+    <!-- Generate PackageVersion.java into this directory. -->
+    <packageVersion.dir>com/fasterxml/jackson/datatype/guava</packageVersion.dir>
+    <packageVersion.package>${project.groupId}.guava</packageVersion.package>
+      <osgi.export>${project.groupId}.guava;version=${project.version},
+${project.groupId}.guava.*;version=${project.version}
+      </osgi.export>
+      <osgi.import>
+com.google.common.collect,
+com.google.common.base,
+com.google.common.cache,
+com.google.common.net,
+com.fasterxml.jackson.core,
+com.fasterxml.jackson.core.util,
+com.fasterxml.jackson.databind,
+com.fasterxml.jackson.databind.deser,
+com.fasterxml.jackson.databind.deser.std,
+com.fasterxml.jackson.databind.introspect,
+com.fasterxml.jackson.databind.jsonFormatVisitors,
+com.fasterxml.jackson.databind.jsontype,
+com.fasterxml.jackson.databind.ser,
+com.fasterxml.jackson.databind.ser.std,
+com.fasterxml.jackson.databind.type
+      </osgi.import>
+  </properties>
+
+  <dependencies>
+    <!-- Extends Jackson; supports Guava datatypes, so: -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>${version.jackson}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>${version.jackson}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>15.0</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>com.google.code.maven-replacer-plugin</groupId>
+        <artifactId>replacer</artifactId>
+        <executions>
+          <execution>
+            <id>process-packageVersion</id>
+            <phase>generate-sources</phase>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/release-notes/VERSION b/release-notes/VERSION
new file mode 100644
index 0000000..19d7e6a
--- /dev/null
+++ b/release-notes/VERSION
@@ -0,0 +1,91 @@
+Project: jackson-datatype-guava
+Version: 2.4.2 (15-Aug-2014)
+
+#46: Can not serialize guava Iterables
+ (reported by chisui at github)
+
+------------------------------------------------------------------------
+=== History: ===
+------------------------------------------------------------------------
+
+2.4.1 (17-Jun-2014)
+
+No changes since 2.4.0.
+
+2.4.0 (03-Jun-2014)
+
+#43: Add support for `HostAndPort`
+
+2.3.3 (14-Apr-2014)
+
+#37: `Optional` not correctly deserialized from JSON null, if inside a Collection
+ (reported by JYang-Addepar at github)
+#41: `Multimap` serializer does not honor @JsonInclude(JsonInclude.Include.NON_EMPTY) 
+ (reported by Olve S-H)
+
+2.3.2 (01-Mar-2014)
+
+#36: Improve Range deserializer to work with older Guava versions (10-)
+ (contribtued by ispringer at github)
+
+2.3.1 (28-Dec-2013)
+
+#33: Add support for `Range` values
+ (contribute by ispringer at github)
+#34: Use Optional type parameter if present to create JSON schema
+ (contributed by cponomaryov at github)
+
+2.3.0 (14-Nov-2013)
+
+#29: Empty ImmutableMap not deserialized correctly, when type info included
+ (reported by pbergn at github)
+- Add support for `DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY` for
+  `ImmutableSet` and `MultiSet`
+
+2.2.3 (25-Aug-2013)
+2.2.2 (27-May-2013)
+2.2.1 (03-May-2013)
+
+No functional changes.
+
+2.2.0 (23-Apr-2013)
+
+New minor version, no functional changes.
+
+2.1.2 (08-Dec-2012)
+
+No functional changes.
+
+2.1.1 (13-Nov-2012)
+
+* More improvements to handling of Optional values
+
+2.1.0 (08-Oct-2012)
+
+* [Issue#9]: Handling of Optional values, wrt NON_NULL inclusion
+
+2.0.6 (30-Sep-2012)
+
+  No functional changes, just dependency updates.
+
+2.0.4, 2.0.5: not released
+
+2.0.3 (14-Jun-2012)
+
+* Issue-6: An NPE in MultimapDeserializer
+
+2.0.2 (16-May-2012)
+
+Fixes:
+
+* Issue-3: (Tree)MultiMaps not serialized properly
+
+Improvements:
+
+* Add for a wider set of Collections
+ (contributed by Pascal G)
+
+
+2.0.0 (25-Mar-2012)
+
+The official 2.0 release...
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaDeserializers.java b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaDeserializers.java
new file mode 100644
index 0000000..71bd9b0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaDeserializers.java
@@ -0,0 +1,237 @@
+package com.fasterxml.jackson.datatype.guava;
+
+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.CollectionType;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.datatype.guava.deser.*;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.list.ArrayListMultimapDeserializer;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.list.LinkedListMultimapDeserializer;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.set.HashMultimapDeserializer;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.set.LinkedHashMultimapDeserializer;
+import com.google.common.base.Optional;
+import com.google.common.collect.*;
+import com.google.common.net.HostAndPort;
+
+/**
+ * Custom deserializers module offers.
+ */
+public class GuavaDeserializers
+    extends Deserializers.Base
+{
+    /**
+     * We have plenty of collection types to support...
+     */
+    @Override
+    public JsonDeserializer<?> findCollectionDeserializer(CollectionType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+
+        // ImmutableXxx types?
+        if (ImmutableCollection.class.isAssignableFrom(raw)) {
+            if (ImmutableList.class.isAssignableFrom(raw)) {
+                return new ImmutableListDeserializer(type,
+                        elementTypeDeserializer, elementDeserializer);
+            }
+            if (ImmutableMultiset.class.isAssignableFrom(raw)) {
+                // 15-May-2012, pgelinas: There is no ImmutableSortedMultiset
+                // available yet
+                return new ImmutableMultisetDeserializer(type, elementTypeDeserializer, elementDeserializer);
+            }
+            if (ImmutableSet.class.isAssignableFrom(raw)) {
+                // sorted one?
+                if (ImmutableSortedSet.class.isAssignableFrom(raw)) {
+                    /* 28-Nov-2010, tatu: With some more work would be able to use other things
+                     *   than natural ordering; but that'll have to do for now...
+                     */
+                    Class<?> elemType = type.getContentType().getRawClass();
+                    if (!Comparable.class.isAssignableFrom(elemType)) {
+                        throw new IllegalArgumentException("Can not handle ImmutableSortedSet with elements that are not Comparable<?> ("
+                                +raw.getName()+")");
+                    }
+                    return new ImmutableSortedSetDeserializer(type,
+                            elementTypeDeserializer, elementDeserializer);
+                }
+                // nah, just regular one
+                return new ImmutableSetDeserializer(type,
+                        elementTypeDeserializer, elementDeserializer);
+            }
+            // TODO: make configurable (for now just default blindly to a list)
+            return new ImmutableListDeserializer(type, elementTypeDeserializer, elementDeserializer);
+        }
+
+        // Multi-xxx collections?
+        if (Multiset.class.isAssignableFrom(raw)) {
+            // Quite a few variations...
+            if (LinkedHashMultiset.class.isAssignableFrom(raw)) {
+                return new LinkedHashMultisetDeserializer(type, elementTypeDeserializer, elementDeserializer);
+            }
+            if (HashMultiset.class.isAssignableFrom(raw)) {
+                return new HashMultisetDeserializer(type, elementTypeDeserializer, elementDeserializer);
+            }
+            if (EnumMultiset.class.isAssignableFrom(raw)) {
+                // !!! TODO
+            }
+            if (TreeMultiset.class.isAssignableFrom(raw)) {
+                return new TreeMultisetDeserializer(type, elementTypeDeserializer, elementDeserializer);
+            }
+
+            // TODO: make configurable (for now just default blindly)
+            return new HashMultisetDeserializer(type, elementTypeDeserializer, elementDeserializer);
+        }
+
+        return null;
+    }
+
+    /**
+     * A few Map types to support.
+     */
+    @Override
+    public JsonDeserializer<?> findMapDeserializer(MapType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+
+        // ImmutableXxxMap types?
+        if (ImmutableMap.class.isAssignableFrom(raw)) {
+            if (ImmutableSortedMap.class.isAssignableFrom(raw)) {
+                return new ImmutableSortedMapDeserializer(type, keyDeserializer, elementTypeDeserializer,
+                        elementDeserializer);
+            }
+            if (ImmutableBiMap.class.isAssignableFrom(raw)) {
+                return new ImmutableBiMapDeserializer(type, keyDeserializer, elementTypeDeserializer,
+                        elementDeserializer);
+            }
+            // Otherwise, plain old ImmutableMap...
+            return new ImmutableMapDeserializer(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
+        }
+
+        // XxxBiMap types?
+        if (BiMap.class.isAssignableFrom(raw)) {
+            if (EnumBiMap.class.isAssignableFrom(raw)) {
+                // !!! TODO
+            }
+            if (EnumHashBiMap.class.isAssignableFrom(raw)) {
+                // !!! TODO
+            }
+            if (HashBiMap.class.isAssignableFrom(raw)) {
+                // !!! TODO
+            }
+            // !!! TODO default
+        }
+
+
+        return null;
+    }
+
+    @Override
+    public JsonDeserializer<?> findMapLikeDeserializer(MapLikeType type,
+            DeserializationConfig config, BeanDescription beanDesc,
+            KeyDeserializer keyDeserializer, TypeDeserializer elementTypeDeserializer,
+            JsonDeserializer<?> elementDeserializer)
+        throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+
+        // ListMultimaps
+        if (ListMultimap.class.isAssignableFrom(raw)) {
+            if (ImmutableListMultimap.class.isAssignableFrom(raw)) {
+                // TODO
+            }
+            if (ArrayListMultimap.class.isAssignableFrom(raw)) {
+                return new ArrayListMultimapDeserializer(type, keyDeserializer,
+                        elementTypeDeserializer, elementDeserializer);
+            }
+            if (LinkedListMultimap.class.isAssignableFrom(raw)) {
+                return new LinkedListMultimapDeserializer(type, keyDeserializer,
+                        elementTypeDeserializer, elementDeserializer);
+            }
+            if (ForwardingListMultimap.class.isAssignableFrom(raw)) {
+                // TODO
+            }
+
+            // TODO: Remove the default fall-through once all implementations are in place.
+            return new ArrayListMultimapDeserializer(type, keyDeserializer,
+                    elementTypeDeserializer, elementDeserializer);
+        }
+
+        // SetMultimaps
+        if (SetMultimap.class.isAssignableFrom(raw)) {
+
+            // SortedSetMultimap
+            if (SortedSetMultimap.class.isAssignableFrom(raw)) {
+                if (TreeMultimap.class.isAssignableFrom(raw)) {
+                    // TODO
+                }
+                if (ForwardingSortedSetMultimap.class.isAssignableFrom(raw)) {
+                    // TODO
+                }
+            }
+
+            if (ImmutableSetMultimap.class.isAssignableFrom(raw)) {
+                // TODO
+            }
+            if (HashMultimap.class.isAssignableFrom(raw)) {
+                return new HashMultimapDeserializer(type, keyDeserializer, elementTypeDeserializer,
+                        elementDeserializer);
+            }
+            if (LinkedHashMultimap.class.isAssignableFrom(raw)) {
+                return new LinkedHashMultimapDeserializer(type, keyDeserializer,
+                        elementTypeDeserializer, elementDeserializer);
+            }
+            if (ForwardingSetMultimap.class.isAssignableFrom(raw)) {
+                // TODO
+            }
+
+            // TODO: Remove the default fall-through once all implementations are covered.
+            return new HashMultimapDeserializer(type, keyDeserializer, elementTypeDeserializer,
+                    elementDeserializer);
+        }
+
+        // Handle the case where nothing more specific was provided.
+        if (Multimap.class.isAssignableFrom(raw)) {
+            return new LinkedListMultimapDeserializer(type, keyDeserializer,
+                    elementTypeDeserializer, elementDeserializer);
+        }
+
+        if (Table.class.isAssignableFrom(raw)) {
+            // !!! TODO
+        }
+
+        return null;
+    }
+
+    @Override
+    public JsonDeserializer<?> findBeanDeserializer(final JavaType type, DeserializationConfig config,
+            BeanDescription beanDesc) throws JsonMappingException
+    {
+        Class<?> raw = type.getRawClass();
+        if (raw == Optional.class){
+            JavaType[] types = config.getTypeFactory().findTypeParameters(type, Optional.class);
+            JavaType refType = (types == null) ? TypeFactory.unknownType() : types[0];
+            JsonDeserializer<?> valueDeser = type.getValueHandler();
+            TypeDeserializer typeDeser = type.getTypeHandler();
+            // [Issue#42]: Polymorphic types need type deserializer
+            if (typeDeser == null) {
+                typeDeser = config.findTypeDeserializer(refType);
+            }
+            return new GuavaOptionalDeserializer(type, refType, typeDeser, valueDeser);
+        }
+        if (raw == Range.class) {
+            return new RangeDeserializer(type);
+        }
+        if (raw == HostAndPort.class) {
+            return HostAndPortDeserializer.std;
+        }
+        return super.findBeanDeserializer(type, config, beanDesc);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaModule.java b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaModule.java
new file mode 100644
index 0000000..73a5ff8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaModule.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.core.Version;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.datatype.guava.ser.GuavaBeanSerializerModifier;
+
+public class GuavaModule extends Module // can't use just SimpleModule, due to generic types
+{
+    private final String NAME = "GuavaModule";
+    
+    public GuavaModule() {
+        super();
+    }
+
+    @Override public String getModuleName() { return NAME; }
+    @Override public Version version() { return PackageVersion.VERSION; }
+    
+    @Override
+    public void setupModule(SetupContext context)
+    {
+        context.addDeserializers(new GuavaDeserializers());
+        context.addSerializers(new GuavaSerializers());
+        context.addTypeModifier(new GuavaTypeModifier());
+        context.addBeanSerializerModifier(new GuavaBeanSerializerModifier());
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return GuavaModule.class.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return this == o;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaSerializers.java b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaSerializers.java
new file mode 100644
index 0000000..4ca1880
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaSerializers.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.google.common.base.Optional;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheBuilderSpec;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Range;
+import com.google.common.net.HostAndPort;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.datatype.guava.ser.GuavaOptionalSerializer;
+import com.fasterxml.jackson.datatype.guava.ser.MultimapSerializer;
+import com.fasterxml.jackson.datatype.guava.ser.RangeSerializer;
+
+public class GuavaSerializers extends Serializers.Base
+{
+    @Override
+    public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc)
+    {
+        Class<?> raw = type.getRawClass();
+        if(Optional.class.isAssignableFrom(raw)){
+            return new GuavaOptionalSerializer(type);
+        }
+        if (Range.class.isAssignableFrom(raw)) {
+            return new RangeSerializer(type);
+        }
+        // since 2.4
+        if (HostAndPort.class.isAssignableFrom(raw)) {
+            return ToStringSerializer.instance;
+        }
+        // not sure how useful, but why not?
+        if (CacheBuilderSpec.class.isAssignableFrom(raw) || CacheBuilder.class.isAssignableFrom(raw)) {
+            return ToStringSerializer.instance;
+        }
+        return super.findSerializer(config, type, beanDesc);
+    }
+
+    @Override
+    public JsonSerializer<?> findMapLikeSerializer(SerializationConfig config,
+            MapLikeType type, BeanDescription beanDesc, JsonSerializer<Object> keySerializer,
+            TypeSerializer elementTypeSerializer, JsonSerializer<Object> elementValueSerializer)
+    {
+        if (Multimap.class.isAssignableFrom(type.getRawClass())) {
+            return new MultimapSerializer(config, type, beanDesc, keySerializer,
+                    elementTypeSerializer, elementValueSerializer);
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaTypeModifier.java b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaTypeModifier.java
new file mode 100644
index 0000000..dc017b5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/GuavaTypeModifier.java
@@ -0,0 +1,64 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.lang.reflect.Type;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.type.TypeBindings;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import com.fasterxml.jackson.databind.type.TypeModifier;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Multimap;
+
+public class GuavaTypeModifier extends TypeModifier
+{
+    @Override
+    public JavaType modifyType(JavaType type, Type jdkType, TypeBindings context, TypeFactory typeFactory)
+    {
+        final Class<?> raw = type.getRawClass();
+        if (Multimap.class.isAssignableFrom(raw)) {
+            JavaType keyType = type.containedType(0);
+            JavaType contentType = type.containedType(1);
+
+            if (keyType == null) {
+                keyType = TypeFactory.unknownType();
+            }
+            if (contentType == null) {
+                contentType = TypeFactory.unknownType();
+            }
+            return typeFactory.constructMapLikeType(type.getRawClass(), keyType, contentType);
+        }
+        /* Guava 12 changed the implementation of their {@link FluentIterable} to include a method named "isEmpty."  This method
+         * causes Jackson to treat FluentIterables as a Bean instead of an {@link Iterable}.  Serialization of FluentIterables by
+         * default result in a string like "{\"empty\":true}."  This module modifies the JavaType of FluentIterable to be
+         * the same as Iterable.
+         */
+        /* Hmmh. This won't work too well for deserialization. But I guess it'll
+         * have to do for now...
+         */
+        if (FluentIterable.class.isAssignableFrom(raw)) {
+            JavaType elemType = null;
+            JavaType[] types;
+            try {
+                types = typeFactory.findTypeParameters(type, Iterable.class);
+                if (types != null && types.length > 0) {
+                    elemType = types[0];
+                }
+            } catch (IllegalArgumentException e) {
+                /* 07-Aug-2015, tatu: Nasty hack, but until we get 100% functioning
+                 *   type resolution (from ClassMate project, f.ex.), need to work around
+                 *   edge cases with aliasing and/or unresolved type variables.
+                 *   So... here we go:
+                 */
+                String msg = e.getMessage();
+                if (msg == null || !msg.contains("Type variable 'T' can not be resolved")) {
+                    throw e;
+                }
+            }
+            if (elemType == null) {
+                elemType = TypeFactory.unknownType();
+            }
+            return typeFactory.constructParametricType(Iterable.class, elemType);
+        }
+        return type;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/PackageVersion.java.in b/src/main/java/com/fasterxml/jackson/datatype/guava/PackageVersion.java.in
new file mode 100644
index 0000000..7860aa1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/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/datatype/guava/deser/GuavaCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaCollectionDeserializer.java
new file mode 100644
index 0000000..051022b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaCollectionDeserializer.java
@@ -0,0 +1,129 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+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;
+import com.fasterxml.jackson.databind.type.CollectionType;
+
+public abstract class GuavaCollectionDeserializer<T>
+    extends StdDeserializer<T>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final CollectionType _containerType;
+    
+    /**
+     * Deserializer used for values contained in collection being deserialized;
+     * either assigned on constructor, or during resolve().
+     */
+    protected final JsonDeserializer<?> _valueDeserializer;
+
+    /**
+     * If value instances have polymorphic type information, this
+     * is the type deserializer that can deserialize required type
+     * information
+     */
+    protected final TypeDeserializer _typeDeserializerForValue;
+    
+    protected GuavaCollectionDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        super(type);
+        _containerType = type;
+        _typeDeserializerForValue = typeDeser;
+        _valueDeserializer = deser;
+    }
+
+    /**
+     * Overridable fluent factory method used for creating contextual
+     * instances.
+     */
+    public abstract GuavaCollectionDeserializer<T> withResolved(
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser);
+    
+    /*
+    /**********************************************************
+    /* 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 JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<?> deser = _valueDeserializer;
+        TypeDeserializer typeDeser = _typeDeserializerForValue;
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(_containerType.getContentType(), property);
+        }
+        if (typeDeser != null) {
+            typeDeser = typeDeser.forProperty(property);
+        }
+        if (deser == _valueDeserializer && typeDeser == _typeDeserializerForValue) {
+            return this;
+        }
+        return withResolved(typeDeser, deser);
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization interface
+    /**********************************************************
+     */
+    
+    /**
+     * 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.deserializeTypedFromArray(jp, ctxt);
+    }
+    
+    @Override
+    public T deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // Should usually point to START_ARRAY
+        if (jp.isExpectedStartArrayToken()) {
+            return _deserializeContents(jp, ctxt);
+        }
+        // But may support implicit arrays from single values?
+        if (ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
+            return _deserializeFromSingleValue(jp, ctxt);
+        }
+        throw ctxt.mappingException(_containerType.getRawClass());
+    }
+
+    /*
+    /**********************************************************************
+    /* Abstract methods for impl classes
+    /**********************************************************************
+     */
+
+    protected abstract T _deserializeContents(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException;
+
+    /**
+     * Method used to support implicit coercion from a single non-array value
+     * into single-element collection.
+     * 
+     * @since 2.3
+     */
+    protected abstract T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableCollectionDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableCollectionDeserializer.java
new file mode 100644
index 0000000..b5de17c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableCollectionDeserializer.java
@@ -0,0 +1,76 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+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.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.ImmutableCollection;
+
+abstract class GuavaImmutableCollectionDeserializer<T extends ImmutableCollection<Object>>
+        extends GuavaCollectionDeserializer<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    GuavaImmutableCollectionDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    protected abstract ImmutableCollection.Builder<Object> createBuilder();
+
+    @Override
+    protected T _deserializeContents(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException {
+        JsonDeserializer<?> valueDes = _valueDeserializer;
+        JsonToken t;
+        final TypeDeserializer typeDeser = _typeDeserializerForValue;
+        // No way to pass actual type parameter; but does not matter, just
+        // compiler-time fluff:
+        ImmutableCollection.Builder<Object> builder = createBuilder();
+
+        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);
+            }
+            builder.add(value);
+        }
+        // No class outside of the package will be able to subclass us,
+        // and we provide the proper builder for the subclasses we implement.
+        @SuppressWarnings("unchecked")
+        T collection = (T) builder.build();
+        return collection;
+    }
+
+    @Override
+    protected T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        JsonDeserializer<?> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _typeDeserializerForValue;
+        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);
+        }
+        @SuppressWarnings("unchecked")
+        T result = (T) createBuilder().add(value).build();
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableMapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableMapDeserializer.java
new file mode 100644
index 0000000..95d4c7b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaImmutableMapDeserializer.java
@@ -0,0 +1,57 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+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.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.google.common.collect.ImmutableMap;
+
+abstract class GuavaImmutableMapDeserializer<T extends ImmutableMap<Object, Object>> extends
+        GuavaMapDeserializer<T> {
+
+    GuavaImmutableMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
+            JsonDeserializer<?> deser) {
+        super(type, keyDeser, typeDeser, deser);
+    }
+
+    protected abstract ImmutableMap.Builder<Object, Object> createBuilder();
+
+    @Override
+    protected T _deserializeEntries(JsonParser jp, DeserializationContext ctxt) throws IOException,
+            JsonProcessingException {
+        final KeyDeserializer keyDes = _keyDeserializer;
+        final JsonDeserializer<?> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _typeDeserializerForValue;
+    
+        ImmutableMap.Builder<Object, Object> builder = createBuilder();
+        for (; jp.getCurrentToken() == JsonToken.FIELD_NAME; jp.nextToken()) {
+            // Must point to field name now
+            String fieldName = jp.getCurrentName();
+            Object key = (keyDes == null) ? fieldName : keyDes.deserializeKey(fieldName, ctxt);
+            // And then the value...
+            JsonToken t = jp.nextToken();
+            // 28-Nov-2010, tatu: Should probably support "ignorable properties" in future...
+            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);
+            }
+            builder.put(key, value);
+        }
+        // No class outside of the package will be able to subclass us,
+        // and we provide the proper builder for the subclasses we implement.
+        @SuppressWarnings("unchecked")
+        T map = (T) builder.build();
+        return map;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMapDeserializer.java
new file mode 100644
index 0000000..9075756
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMapDeserializer.java
@@ -0,0 +1,135 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapType;
+
+public abstract class GuavaMapDeserializer<T>
+    extends JsonDeserializer<T>
+    implements ContextualDeserializer
+{
+    protected final MapType _mapType;
+    
+    /**
+     * Key deserializer used, if not null. If null, String from JSON
+     * content is used as is.
+     */
+    protected KeyDeserializer _keyDeserializer;
+
+    /**
+     * Value deserializer.
+     */
+    protected JsonDeserializer<?> _valueDeserializer;
+
+    /**
+     * If value instances have polymorphic type information, this
+     * is the type deserializer that can handle it
+     */
+    protected final TypeDeserializer _typeDeserializerForValue;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    protected GuavaMapDeserializer(MapType type, KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        _mapType = type;
+        _keyDeserializer = keyDeser;
+        _typeDeserializerForValue = typeDeser;
+        _valueDeserializer = deser;
+    }
+
+    /**
+     * Overridable fluent factory method used for creating contextual
+     * instances.
+     */
+    public abstract GuavaMapDeserializer<T> withResolved(KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser);
+    
+    /*
+    /**********************************************************
+    /* 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 JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        KeyDeserializer keyDeser = _keyDeserializer;
+        JsonDeserializer<?> deser = _valueDeserializer;
+        TypeDeserializer typeDeser = _typeDeserializerForValue;
+        // Do we need any contextualization?
+        if ((keyDeser != null) && (deser != null) && (typeDeser == null)) { // nope
+            return this;
+        }
+        if (keyDeser == null) {
+            keyDeser = ctxt.findKeyDeserializer(_mapType.getKeyType(), property);
+        }
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(_mapType.getContentType(), property);
+        }
+        if (typeDeser != null) {
+            typeDeser = typeDeser.forProperty(property);
+        }
+        return withResolved(keyDeser, typeDeser, deser);
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization interface
+    /**********************************************************
+     */
+
+    /**
+     * 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
+    {
+        // note: call "...FromObject" because expected output structure
+        // for value is JSON Object (regardless of contortions used for type id)
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+    
+    @Override
+    public T deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // Ok: must point to START_OBJECT or FIELD_NAME
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) { // If START_OBJECT, move to next; may also be END_OBJECT
+            t = jp.nextToken();
+        }
+        if (t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
+            throw ctxt.mappingException(_mapType.getRawClass());
+        }
+        return _deserializeEntries(jp, ctxt);
+    }
+
+    /*
+    /**********************************************************************
+    /* Abstract methods for impl classes
+    /**********************************************************************
+     */
+
+    protected abstract T _deserializeEntries(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMultisetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMultisetDeserializer.java
new file mode 100644
index 0000000..df34075
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaMultisetDeserializer.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+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.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.Multiset;
+
+abstract class GuavaMultisetDeserializer<T extends Multiset<Object>>
+    extends GuavaCollectionDeserializer<T>
+{
+    private static final long serialVersionUID = 1L;
+
+    GuavaMultisetDeserializer(CollectionType type, TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    protected abstract T createMultiset();
+
+    @Override
+    protected T _deserializeContents(JsonParser jp, DeserializationContext ctxt) throws IOException,
+            JsonProcessingException {
+        JsonDeserializer<?> valueDes = _valueDeserializer;
+        JsonToken t;
+        final TypeDeserializer typeDeser = _typeDeserializerForValue;
+        T set = createMultiset();
+    
+        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);
+            }
+            set.add(value);
+        }
+        return set;
+    }
+
+    @Override
+    protected T _deserializeFromSingleValue(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        JsonDeserializer<?> valueDes = _valueDeserializer;
+        final TypeDeserializer typeDeser = _typeDeserializerForValue;
+        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);
+        }
+        T result = createMultiset();
+        result.add(value);
+        return result;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaOptionalDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaOptionalDeserializer.java
new file mode 100644
index 0000000..5462b11
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/GuavaOptionalDeserializer.java
@@ -0,0 +1,126 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+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.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.google.common.base.Optional;
+
+public class GuavaOptionalDeserializer
+    extends StdDeserializer<Optional<?>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final JavaType _fullType;
+    
+    protected final JavaType _referenceType;
+
+    protected final JsonDeserializer<?> _valueDeserializer;
+
+    protected final TypeDeserializer _valueTypeDeserializer;
+
+    public GuavaOptionalDeserializer(JavaType fullType, JavaType refType,
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser)
+    {
+        super(fullType);
+        _fullType = fullType;
+        _referenceType = refType;
+        _valueTypeDeserializer = typeDeser;
+        _valueDeserializer = valueDeser;
+    }
+
+    @Override
+    public JavaType getValueType() { return _fullType; }
+    
+    @Override
+    public Optional<?> getNullValue() { return Optional.absent(); }
+
+    /**
+     * Overridable fluent factory method used for creating contextual
+     * instances.
+     */
+    protected GuavaOptionalDeserializer withResolved(
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser)
+    {
+        return new GuavaOptionalDeserializer(_fullType, _referenceType,
+                typeDeser, valueDeser);
+    }
+    
+    /*
+    /**********************************************************
+    /* 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 JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonDeserializer<?> deser = _valueDeserializer;
+        TypeDeserializer typeDeser = _valueTypeDeserializer;
+
+        if (deser == null) {
+            deser = ctxt.findContextualValueDeserializer(_referenceType, property);
+        } else { // otherwise directly assigned, probably not contextual yet:
+            deser = ctxt.handleSecondaryContextualization(deser, property);
+        }
+        if (typeDeser != null) {
+            typeDeser = typeDeser.forProperty(property);
+        }
+        if (deser == _valueDeserializer && typeDeser == _valueTypeDeserializer) {
+            return this;
+        }
+        return withResolved(typeDeser, deser);
+    }
+    
+    @Override
+    public Optional<?> deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
+        JsonProcessingException
+    {
+        Object refd;
+
+        if (_valueTypeDeserializer == null) {
+            refd = _valueDeserializer.deserialize(jp, ctxt);
+        } else {
+            refd = _valueDeserializer.deserializeWithType(jp, ctxt, _valueTypeDeserializer);
+        }
+        return Optional.of(refd);
+    }
+
+    /* NOTE: usually should not need this method... but for some reason, it is needed here.
+     */
+    @Override
+    public Optional<?> deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        final JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NULL) {
+            return getNullValue();
+        }
+        // 03-Nov-2013, tatu: This gets rather tricky with "natural" types
+        //   (String, Integer, Boolean), which do NOT include type information.
+        //   These might actually be handled ok except that nominal type here
+        //   is `Optional`, so special handling is not invoked; instead, need
+        //   to do a work-around here.
+        if (t != null && t.isScalarValue()) {
+            return deserialize(jp, ctxt);
+        }
+        // with type deserializer to use here? Looks like we get passed same one?
+        return Optional.of(typeDeserializer.deserializeTypedFromAny(jp, ctxt));
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HashMultisetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HashMultisetDeserializer.java
new file mode 100644
index 0000000..212c6b1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HashMultisetDeserializer.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.HashMultiset;
+
+public class HashMultisetDeserializer
+    extends GuavaMultisetDeserializer<HashMultiset<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public HashMultisetDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    public HashMultisetDeserializer withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new HashMultisetDeserializer(_containerType,
+                typeDeser, valueDeser);
+    }
+    
+    @Override
+    protected HashMultiset<Object> createMultiset() {
+        return HashMultiset.<Object> create();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HostAndPortDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HostAndPortDeserializer.java
new file mode 100644
index 0000000..09aa262
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/HostAndPortDeserializer.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+import com.google.common.net.HostAndPort;
+
+public class HostAndPortDeserializer extends StdDeserializer<HostAndPort>
+{
+    private static final long serialVersionUID = 1L;
+
+    public final static HostAndPortDeserializer std = new HostAndPortDeserializer();
+    
+    public HostAndPortDeserializer() { super(HostAndPort.class); }
+
+    @Override
+    public HostAndPort deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) { // old style
+            JsonNode root = jp.readValueAsTree();
+            String host = root.path("hostText").asText();
+            JsonNode n = root.get("port");
+            if (n == null) {
+                return HostAndPort.fromString(host);
+            }
+            return HostAndPort.fromParts(host, n.asInt());
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            return HostAndPort.fromString(jp.getText().trim());
+        }
+        // could also support arrays?
+        throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "(or JSON Object)");
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableBiMapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableBiMapDeserializer.java
new file mode 100644
index 0000000..c6a7f51
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableBiMapDeserializer.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap.Builder;
+
+public class ImmutableBiMapDeserializer extends GuavaImmutableMapDeserializer<ImmutableBiMap<Object, Object>> {
+    public ImmutableBiMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
+            JsonDeserializer<?> deser) {
+        super(type, keyDeser, typeDeser, deser);
+    }
+
+    @Override
+    protected Builder<Object, Object> createBuilder() {
+        return ImmutableBiMap.builder();
+    }
+
+    @Override
+    public GuavaMapDeserializer<ImmutableBiMap<Object, Object>> withResolved(KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
+        return new ImmutableBiMapDeserializer(_mapType, keyDeser, typeDeser, valueDeser);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableListDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableListDeserializer.java
new file mode 100644
index 0000000..51a3e5b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableListDeserializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.google.common.collect.ImmutableList;
+
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+
+public class ImmutableListDeserializer extends
+        GuavaImmutableCollectionDeserializer<ImmutableList<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public ImmutableListDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    public ImmutableListDeserializer withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new ImmutableListDeserializer(_containerType, typeDeser,
+                valueDeser);
+    }
+
+    /*
+    /**********************************************************
+    /* Deserialization
+    /**********************************************************
+     */
+
+    @Override
+    protected ImmutableList.Builder<Object> createBuilder() {
+        ImmutableList.Builder<Object> builder = ImmutableList.builder();
+        return builder;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMapDeserializer.java
new file mode 100644
index 0000000..6421b49
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMapDeserializer.java
@@ -0,0 +1,31 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.google.common.collect.ImmutableMap;
+
+public class ImmutableMapDeserializer
+ extends GuavaImmutableMapDeserializer<ImmutableMap<Object, Object>>
+{
+    public ImmutableMapDeserializer(MapType type, KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        super(type, keyDeser, typeDeser, deser);
+    }
+
+    @Override
+    public ImmutableMapDeserializer withResolved(KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
+        return new ImmutableMapDeserializer(_mapType, keyDeser,
+                typeDeser, valueDeser);
+    }
+    
+    @Override
+    protected ImmutableMap.Builder<Object, Object> createBuilder() {
+        return ImmutableMap.builder();
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMultisetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMultisetDeserializer.java
new file mode 100644
index 0000000..c488ed8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableMultisetDeserializer.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.ImmutableCollection.Builder;
+import com.google.common.collect.ImmutableMultiset;
+
+public class ImmutableMultisetDeserializer extends GuavaImmutableCollectionDeserializer<ImmutableMultiset<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public ImmutableMultisetDeserializer(CollectionType type, TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    protected Builder<Object> createBuilder() {
+        return ImmutableMultiset.builder();
+    }
+
+    @Override
+    public GuavaCollectionDeserializer<ImmutableMultiset<Object>> withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new ImmutableMultisetDeserializer(_containerType, typeDeser, valueDeser);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSetDeserializer.java
new file mode 100644
index 0000000..132200d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSetDeserializer.java
@@ -0,0 +1,30 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.ImmutableCollection.Builder;
+import com.google.common.collect.ImmutableSet;
+
+public class ImmutableSetDeserializer extends GuavaImmutableCollectionDeserializer<ImmutableSet<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public ImmutableSetDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    public ImmutableSetDeserializer withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new ImmutableSetDeserializer(_containerType,
+                typeDeser, valueDeser);
+    }
+    
+    @Override
+    protected Builder<Object> createBuilder() {
+        return ImmutableSet.builder();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedMapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedMapDeserializer.java
new file mode 100644
index 0000000..179e1de
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedMapDeserializer.java
@@ -0,0 +1,36 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.google.common.collect.ImmutableMap.Builder;
+import com.google.common.collect.ImmutableSortedMap;
+
+public class ImmutableSortedMapDeserializer extends GuavaImmutableMapDeserializer<ImmutableSortedMap<Object, Object>> {
+
+    public ImmutableSortedMapDeserializer(MapType type, KeyDeserializer keyDeser, TypeDeserializer typeDeser,
+            JsonDeserializer<?> deser) {
+        super(type, keyDeser, typeDeser, deser);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Builder<Object, Object> createBuilder() {
+        /*
+         * Not quite sure what to do with sorting/ordering; may require better
+         * support either via annotations, or via custom serialization (bean
+         * style that includes ordering aspects)
+         */
+        @SuppressWarnings("rawtypes")
+        ImmutableSortedMap.Builder<?, Object> naturalOrder = ImmutableSortedMap.<Comparable, Object>naturalOrder();
+        ImmutableSortedMap.Builder<Object, Object> builder = (ImmutableSortedMap.Builder<Object, Object>) naturalOrder;
+        return builder;
+    }
+
+    @Override
+    public GuavaMapDeserializer<ImmutableSortedMap<Object, Object>> withResolved(KeyDeserializer keyDeser,
+            TypeDeserializer typeDeser, JsonDeserializer<?> valueDeser) {
+        return new ImmutableSortedMapDeserializer(_mapType, keyDeser, typeDeser, valueDeser);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedSetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedSetDeserializer.java
new file mode 100644
index 0000000..ff46bb4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/ImmutableSortedSetDeserializer.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.ImmutableCollection.Builder;
+import com.google.common.collect.ImmutableSortedSet;
+
+public class ImmutableSortedSetDeserializer extends GuavaImmutableCollectionDeserializer<ImmutableSortedSet<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public ImmutableSortedSetDeserializer(CollectionType type,
+            TypeDeserializer typeDeser, JsonDeserializer<?> deser)
+    {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    public ImmutableSortedSetDeserializer withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new ImmutableSortedSetDeserializer(_containerType,
+                typeDeser, valueDeser);
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Builder<Object> createBuilder() {
+        /* Not quite sure what to do with sorting/ordering; may require better support either
+         * via annotations, or via custom serialization (bean style that includes ordering
+         * aspects)
+         */
+        @SuppressWarnings("rawtypes")
+        ImmutableSortedSet.Builder<?> builderComp = ImmutableSortedSet.<Comparable> naturalOrder();
+        ImmutableSortedSet.Builder<Object> builder = (ImmutableSortedSet.Builder<Object>) builderComp;
+        return builder;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/LinkedHashMultisetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/LinkedHashMultisetDeserializer.java
new file mode 100644
index 0000000..7306ca6
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/LinkedHashMultisetDeserializer.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.LinkedHashMultiset;
+
+public class LinkedHashMultisetDeserializer extends GuavaMultisetDeserializer<LinkedHashMultiset<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public LinkedHashMultisetDeserializer(CollectionType type, TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    @Override
+    protected LinkedHashMultiset<Object> createMultiset() {
+        return LinkedHashMultiset.create();
+    }
+
+    @Override
+    public GuavaCollectionDeserializer<LinkedHashMultiset<Object>> withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new LinkedHashMultisetDeserializer(_containerType, typeDeser, valueDeser);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/RangeDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/RangeDeserializer.java
new file mode 100644
index 0000000..7f8a74d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/RangeDeserializer.java
@@ -0,0 +1,181 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.datatype.guava.deser.util.RangeFactory;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BoundType;
+import com.google.common.collect.Range;
+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;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Jackson deserializer for a Guava {@link Range}.
+ *<p>
+ * TODO: I think it would make sense to reimplement this deserializer to
+ * use Delegating Deserializer, using a POJO as an intermediate form (properties
+ * could be of type {@link java.lang.Object})
+ * This would also also simplify the implementation a bit.
+ */
+public class RangeDeserializer
+    extends StdDeserializer<Range<?>>
+    implements ContextualDeserializer
+{
+    private static final long serialVersionUID = 1L;
+
+    protected final JavaType _rangeType;
+
+    protected final JsonDeserializer<Object> _endpointDeserializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public RangeDeserializer(JavaType rangeType) {
+        this(rangeType, null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public RangeDeserializer(JavaType rangeType, JsonDeserializer<?> endpointDeser)
+    {
+        super(rangeType);
+        _rangeType = rangeType;
+        _endpointDeserializer = (JsonDeserializer<Object>) endpointDeser;
+    }
+
+    @Override
+    public JavaType getValueType() { return _rangeType; }
+
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException
+    {
+        if (_endpointDeserializer == null) {
+            JavaType endpointType = _rangeType.containedType(0);
+            if (endpointType == null) { // should this ever occur?
+                endpointType = TypeFactory.unknownType();
+            }
+            JsonDeserializer<Object> deser = ctxt.findContextualValueDeserializer(endpointType, property);
+            return new RangeDeserializer(_rangeType, deser);
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Actual deserialization
+    /**********************************************************
+     */
+    
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt,
+            TypeDeserializer typeDeserializer)
+        throws IOException, JsonProcessingException
+    {
+        return typeDeserializer.deserializeTypedFromObject(jp, ctxt);
+    }
+
+    @Override
+    public Range<?> deserialize(JsonParser parser, DeserializationContext context)
+            throws IOException, JsonProcessingException
+    {
+        // NOTE: either START_OBJECT _or_ FIELD_NAME fine; latter for polymorphic cases
+        JsonToken t = parser.getCurrentToken();
+        if (t == JsonToken.START_OBJECT) {
+            t = parser.nextToken();
+        }
+
+        Comparable<?> lowerEndpoint = null;
+        Comparable<?> upperEndpoint = null;
+        BoundType lowerBoundType = null;
+        BoundType upperBoundType = null;
+
+        for (; t != JsonToken.END_OBJECT; t = parser.nextToken()) {
+            expect(parser, JsonToken.FIELD_NAME, t);
+            String fieldName = parser.getCurrentName();
+            try {
+                if (fieldName.equals("lowerEndpoint")) {
+                    Preconditions.checkState(lowerEndpoint == null, "'lowerEndpoint' field included multiple times.");
+                    parser.nextToken();
+                    lowerEndpoint = deserializeEndpoint(parser, context);
+                } else if (fieldName.equals("upperEndpoint")) {
+                    Preconditions.checkState(upperEndpoint == null, "'upperEndpoint' field included multiple times.");
+                    parser.nextToken();
+                    upperEndpoint = deserializeEndpoint(parser, context);
+                } else if (fieldName.equals("lowerBoundType")) {
+                    Preconditions.checkState(lowerBoundType == null, "'lowerBoundType' field included multiple times.");
+                    parser.nextToken();
+                    lowerBoundType = deserializeBoundType(parser);
+                } else if (fieldName.equals("upperBoundType")) {
+                    Preconditions.checkState(upperBoundType == null, "'upperBoundType' field included multiple times.");
+                    parser.nextToken();
+                    upperBoundType = deserializeBoundType(parser);
+                } else {
+                    throw context.mappingException("Unexpected Range field: " + fieldName);
+                }
+            } catch (IllegalStateException e) {
+                throw new JsonMappingException(e.getMessage());
+            }
+        }
+
+        try {
+            if ((lowerEndpoint != null) && (upperEndpoint != null)) {
+                Preconditions.checkState(lowerEndpoint.getClass() == upperEndpoint.getClass(),
+                                         "Endpoint types are not the same - 'lowerEndpoint' deserialized to [%s], and 'upperEndpoint' deserialized to [%s].",
+                                         lowerEndpoint.getClass().getName(),
+                                         upperEndpoint.getClass().getName());
+                Preconditions.checkState(lowerBoundType != null, "'lowerEndpoint' field found, but not 'lowerBoundType'");
+                Preconditions.checkState(upperBoundType != null, "'upperEndpoint' field found, but not 'upperBoundType'");
+                return RangeFactory.range(lowerEndpoint, lowerBoundType, upperEndpoint, upperBoundType);
+            }
+            if (lowerEndpoint != null) {
+                Preconditions.checkState(lowerBoundType != null, "'lowerEndpoint' field found, but not 'lowerBoundType'");
+                return RangeFactory.downTo(lowerEndpoint, lowerBoundType);
+            }
+            if (upperEndpoint != null) {
+                Preconditions.checkState(upperBoundType != null, "'upperEndpoint' field found, but not 'upperBoundType'");
+                return RangeFactory.upTo(upperEndpoint, upperBoundType);
+            }
+            return RangeFactory.all();
+        } catch (IllegalStateException e) {
+            throw new JsonMappingException(e.getMessage());
+        }
+    }
+
+    private BoundType deserializeBoundType(JsonParser parser) throws IOException
+    {
+        expect(parser, JsonToken.VALUE_STRING, parser.getCurrentToken());
+        String name = parser.getText();
+        try {
+            return BoundType.valueOf(name);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalStateException("[" + name + "] is not a valid BoundType name.");
+        }
+    }
+
+    private Comparable<?> deserializeEndpoint(JsonParser parser, DeserializationContext context) throws IOException
+    {
+        Object obj = _endpointDeserializer.deserialize(parser, context);
+        if (!(obj instanceof Comparable)) {
+            throw context.mappingException(String.format(
+                                 "Field [%s] deserialized to [%s], which does not implement Comparable.",
+                                 parser.getCurrentName(), obj.getClass().getName()));
+        }
+        return (Comparable<?>) obj;
+    }
+
+    private void expect(JsonParser jp, JsonToken expected, JsonToken actual) throws JsonMappingException
+    {
+        if (actual != expected) {
+            throw new JsonMappingException("Expecting " + expected + ", found " + actual, jp.getCurrentLocation());
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/TreeMultisetDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/TreeMultisetDeserializer.java
new file mode 100644
index 0000000..ff0ab47
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/TreeMultisetDeserializer.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.datatype.guava.deser;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import com.google.common.collect.TreeMultiset;
+
+public class TreeMultisetDeserializer extends GuavaMultisetDeserializer<TreeMultiset<Object>>
+{
+    private static final long serialVersionUID = 1L;
+
+    public TreeMultisetDeserializer(CollectionType type, TypeDeserializer typeDeser, JsonDeserializer<?> deser) {
+        super(type, typeDeser, deser);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected TreeMultiset<Object> createMultiset() {
+        @SuppressWarnings("rawtypes")
+        TreeMultiset<?> naturalOrder = TreeMultiset.<Comparable> create();
+        return (TreeMultiset<Object>) naturalOrder;
+    }
+
+    @Override
+    public GuavaCollectionDeserializer<TreeMultiset<Object>> withResolved(TypeDeserializer typeDeser,
+            JsonDeserializer<?> valueDeser) {
+        return new TreeMultisetDeserializer(_containerType, typeDeser, valueDeser);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/GuavaMultimapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/GuavaMultimapDeserializer.java
new file mode 100644
index 0000000..2e422d7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/GuavaMultimapDeserializer.java
@@ -0,0 +1,183 @@
+package com.fasterxml.jackson.datatype.guava.deser.multimap;
+
+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.KeyDeserializer;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimap;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * @author mvolkhart
+ */
+public abstract class GuavaMultimapDeserializer<T extends Multimap<Object,
+        Object>> extends JsonDeserializer<T> implements ContextualDeserializer {
+
+    private static final List<String> METHOD_NAMES = ImmutableList.of("copyOf", "create");
+    private final MapLikeType type;
+    private final KeyDeserializer keyDeserializer;
+    private final TypeDeserializer elementTypeDeserializer;
+    private final JsonDeserializer<?> elementDeserializer;
+    /**
+     * Since we have to use a method to transform from a known multi-map type into actual one, we'll
+     * resolve method just once, use it. Note that if this is set to null, we can just construct a
+     * {@link com.google.common.collect.LinkedListMultimap} instance and be done with it.
+     */
+    private final Method creatorMethod;
+
+    public GuavaMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) {
+        this(type, keyDeserializer, elementTypeDeserializer, elementDeserializer,
+                findTransformer(type.getRawClass()));
+    }
+
+    public GuavaMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer,
+            Method creatorMethod) {
+        this.type = type;
+        this.keyDeserializer = keyDeserializer;
+        this.elementTypeDeserializer = elementTypeDeserializer;
+        this.elementDeserializer = elementDeserializer;
+        this.creatorMethod = creatorMethod;
+    }
+
+    private static Method findTransformer(Class<?> rawType) {
+        // Very first thing: if it's a "standard multi-map type", can avoid copying
+        if (rawType == LinkedListMultimap.class || rawType == ListMultimap.class || rawType ==
+                Multimap.class) {
+            return null;
+        }
+
+        // First, check type itself for matching methods
+        for (String methodName : METHOD_NAMES) {
+            try {
+                Method m = rawType.getMethod(methodName, Multimap.class);
+                if (m != null) {
+                    return m;
+                }
+            } catch (NoSuchMethodException e) {
+            }
+            // pass SecurityExceptions as-is:
+            // } catch (SecurityException e) { }
+        }
+
+        // If not working, possibly super types too (should we?)
+        for (String methodName : METHOD_NAMES) {
+            try {
+                Method m = rawType.getMethod(methodName, Multimap.class);
+                if (m != null) {
+                    return m;
+                }
+            } catch (NoSuchMethodException e) {
+            }
+            // pass SecurityExceptions as-is:
+            // } catch (SecurityException e) { }
+        }
+
+        return null;
+    }
+
+    protected abstract T createMultimap();
+
+    /**
+     * We need to use this method to properly handle possible contextual variants of key and value
+     * deserializers, as well as type deserializers.
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
+            BeanProperty property) throws JsonMappingException {
+        KeyDeserializer kd = keyDeserializer;
+        if (kd == null) {
+            kd = ctxt.findKeyDeserializer(type.getKeyType(), property);
+        }
+        JsonDeserializer<?> ed = elementDeserializer;
+        if (ed == null) {
+            ed = ctxt.findContextualValueDeserializer(type.getContentType(), property);
+        }
+        // Type deserializer is slightly different; must be passed, but needs to become contextual:
+        TypeDeserializer etd = elementTypeDeserializer;
+        if (etd != null && property != null) {
+            etd = etd.forProperty(property);
+        }
+        return (_createContextual(type, kd, etd, ed, creatorMethod));
+    }
+
+    protected abstract JsonDeserializer<?> _createContextual(MapLikeType t,
+            KeyDeserializer kd, TypeDeserializer typeDeserializer,
+            JsonDeserializer<?> ed, Method method);
+
+    @Override
+    public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
+            JsonProcessingException {
+
+        T multimap = createMultimap();
+
+        expect(jp, JsonToken.START_OBJECT);
+
+        while (jp.nextToken() != JsonToken.END_OBJECT) {
+            final Object key;
+            if (keyDeserializer != null) {
+                key = keyDeserializer.deserializeKey(jp.getCurrentName(), ctxt);
+            } else {
+                key = jp.getCurrentName();
+            }
+
+            jp.nextToken();
+            expect(jp, JsonToken.START_ARRAY);
+
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                final Object value;
+                if (jp.getCurrentToken() == JsonToken.VALUE_NULL) {
+                    value = null;
+                } else if (elementTypeDeserializer != null) {
+                    value = elementDeserializer.deserializeWithType(jp, ctxt,
+                            elementTypeDeserializer);
+                } else {
+                    value = elementDeserializer.deserialize(jp, ctxt);
+                }
+                multimap.put(key, value);
+            }
+        }
+        if (creatorMethod == null) {
+            return multimap;
+        }
+        try {
+            @SuppressWarnings("unchecked") T map = (T) creatorMethod.invoke(null, multimap);
+            return map;
+        } catch (InvocationTargetException e) {
+            throw new JsonMappingException("Could not map to " + type, _peel(e));
+        } catch (IllegalArgumentException e) {
+            throw new JsonMappingException("Could not map to " + type, _peel(e));
+        } catch (IllegalAccessException e) {
+            throw new JsonMappingException("Could not map to " + type, _peel(e));
+        }
+    }
+
+    private void expect(JsonParser jp, JsonToken token) throws IOException {
+        if (jp.getCurrentToken() != token) {
+            throw new JsonMappingException("Expecting " + token + ", found " + jp.getCurrentToken(),
+                    jp.getCurrentLocation());
+        }
+    }
+
+    private Throwable _peel(Throwable t) {
+        while (t.getCause() != null) {
+            t = t.getCause();
+        }
+        return t;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/ArrayListMultimapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/ArrayListMultimapDeserializer.java
new file mode 100644
index 0000000..cdf673b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/ArrayListMultimapDeserializer.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.datatype.guava.deser.multimap.list;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer;
+import com.google.common.collect.ArrayListMultimap;
+
+import java.lang.reflect.Method;
+
+/**
+ * Provides deserialization for the Guava ArrayListMultimap class.
+ *
+ * @author mvolkhart
+ */
+public class ArrayListMultimapDeserializer extends GuavaMultimapDeserializer<ArrayListMultimap<Object,
+        Object>> {
+
+    public ArrayListMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
+    }
+
+    public ArrayListMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer,
+            Method creatorMethod) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer, creatorMethod);
+    }
+
+    @Override
+    protected ArrayListMultimap<Object, Object> createMultimap() {
+        return ArrayListMultimap.create();
+    }
+
+    @Override
+    protected JsonDeserializer<?> _createContextual(MapLikeType type,
+            KeyDeserializer keyDeserializer, TypeDeserializer typeDeserializer,
+            JsonDeserializer<?> elementDeserializer, Method method) {
+        return new ArrayListMultimapDeserializer(type, keyDeserializer, typeDeserializer,
+                elementDeserializer, method);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/LinkedListMultimapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/LinkedListMultimapDeserializer.java
new file mode 100644
index 0000000..263f6fe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/list/LinkedListMultimapDeserializer.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.datatype.guava.deser.multimap.list;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer;
+import com.google.common.collect.LinkedListMultimap;
+
+import java.lang.reflect.Method;
+
+/**
+ * Provides deserialization for the Guava LinkedListMultimap class.
+ *
+ * @author mvolkhart
+ */
+public class LinkedListMultimapDeserializer
+    extends GuavaMultimapDeserializer<LinkedListMultimap<Object,Object>>
+{
+    public LinkedListMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
+    }
+
+    public LinkedListMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer,
+            Method creatorMethod) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer, creatorMethod);
+    }
+
+    @Override
+    protected LinkedListMultimap<Object, Object> createMultimap() {
+        return LinkedListMultimap.create();
+    }
+
+    @Override
+    protected JsonDeserializer<?> _createContextual(MapLikeType type,
+            KeyDeserializer keyDeserializer, TypeDeserializer typeDeserializer,
+            JsonDeserializer<?> elementDeserializer, Method method) {
+        return new LinkedListMultimapDeserializer(type, keyDeserializer, typeDeserializer,
+                elementDeserializer, method);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/HashMultimapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/HashMultimapDeserializer.java
new file mode 100644
index 0000000..7569321
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/HashMultimapDeserializer.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.datatype.guava.deser.multimap.set;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer;
+import com.google.common.collect.HashMultimap;
+
+import java.lang.reflect.Method;
+
+/**
+ * Provides deserialization for the Guava HashMultimap class.
+ *
+ * @author mvolkhart
+ */
+public class HashMultimapDeserializer extends GuavaMultimapDeserializer<HashMultimap<Object,
+        Object>> {
+
+    public HashMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
+    }
+
+    public HashMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer,
+            Method creatorMethod) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer, creatorMethod);
+    }
+
+    @Override
+    protected HashMultimap<Object, Object> createMultimap() {
+        return HashMultimap.create();
+    }
+
+    @Override
+    protected JsonDeserializer<?> _createContextual(MapLikeType type,
+            KeyDeserializer keyDeserializer, TypeDeserializer typeDeserializer,
+            JsonDeserializer<?> elementDeserializer, Method method) {
+        return new HashMultimapDeserializer(type, keyDeserializer, typeDeserializer,
+                elementDeserializer, method);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/LinkedHashMultimapDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/LinkedHashMultimapDeserializer.java
new file mode 100644
index 0000000..fc442ac
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/multimap/set/LinkedHashMultimapDeserializer.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.datatype.guava.deser.multimap.set;
+
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+import com.fasterxml.jackson.datatype.guava.deser.multimap.GuavaMultimapDeserializer;
+import com.google.common.collect.LinkedHashMultimap;
+
+import java.lang.reflect.Method;
+
+/**
+ * Provides deserialization for the Guava LinkedHashMultimap class.
+ *
+ * @author mvolkhart
+ */
+public class LinkedHashMultimapDeserializer extends
+        GuavaMultimapDeserializer<LinkedHashMultimap<Object, Object>> {
+
+    public LinkedHashMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer);
+    }
+
+    public LinkedHashMultimapDeserializer(MapLikeType type, KeyDeserializer keyDeserializer,
+            TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer,
+            Method creatorMethod) {
+        super(type, keyDeserializer, elementTypeDeserializer, elementDeserializer, creatorMethod);
+    }
+
+    @Override
+    protected LinkedHashMultimap<Object, Object> createMultimap() {
+        return LinkedHashMultimap.create();
+    }
+
+    @Override
+    protected JsonDeserializer<?> _createContextual(MapLikeType type,
+            KeyDeserializer keyDeserializer, TypeDeserializer typeDeserializer,
+            JsonDeserializer<?> elementDeserializer, Method method) {
+        return new LinkedHashMultimapDeserializer(type, keyDeserializer, typeDeserializer,
+                elementDeserializer, method);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/deser/util/RangeFactory.java b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/util/RangeFactory.java
new file mode 100644
index 0000000..0d7c54d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/deser/util/RangeFactory.java
@@ -0,0 +1,181 @@
+
+package com.fasterxml.jackson.datatype.guava.deser.util;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Range;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Callable;
+
+/**
+ * A factory for creating Guava {@link Range}s that is compatible with Guava 10 and later.
+ *
+ * If Guava 10, 11, 12, or 13 is being used, the factory methods in the com.google.common.collect.Ranges class (a beta
+ * class that was removed in Guava 14) are used to reflectively instantiate Ranges. If Guava 14 or later is being used,
+ * the factory methods in the Range class itself (added in Guava 14) are used to instantiate Ranges.
+ */
+public class RangeFactory
+{
+    private static final String LEGACY_RANGES_CLASS_NAME = "com.google.common.collect.Ranges";
+
+    private static final String LEGACY_RANGE_METHOD_NAME = "range";
+    private static final String LEGACY_DOWN_TO_METHOD_NAME = "downTo";
+    private static final String LEGACY_UP_TO_METHOD_NAME = "upTo";
+    private static final String LEGACY_ALL_METHOD_NAME = "all";
+
+    private static Method legacyRangeMethod;
+    private static Method legacyDownToMethod;
+    private static Method legacyUpToMethod;
+    private static Method legacyAllMethod;
+
+    static
+    {
+        initLegacyRangeFactoryMethods();
+    }
+
+    private static void initLegacyRangeFactoryMethods()
+    {
+        try {
+            Class<?> rangesClass = Class.forName(LEGACY_RANGES_CLASS_NAME);
+            legacyRangeMethod = findMethod(rangesClass, LEGACY_RANGE_METHOD_NAME, Comparable.class, BoundType.class,
+                                           Comparable.class, BoundType.class);
+            legacyDownToMethod = findMethod(rangesClass, LEGACY_DOWN_TO_METHOD_NAME, Comparable.class, BoundType.class);
+            legacyUpToMethod = findMethod(rangesClass, LEGACY_UP_TO_METHOD_NAME, Comparable.class, BoundType.class);
+            legacyAllMethod = findMethod(rangesClass, LEGACY_ALL_METHOD_NAME);
+        } catch (ClassNotFoundException e) {
+            // ignore
+        }
+    }
+
+    // returns null if the method is not found
+    private static Method findMethod(Class<?> clazz, String methodName, Class<?> ... paramTypes)
+    {
+        try {
+            return clazz.getMethod(methodName, paramTypes);
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    public static <C extends Comparable<?>> Range<C> open(C lowerEndpoint, C upperEndpoint)
+    {
+        return range(lowerEndpoint, BoundType.OPEN, upperEndpoint, BoundType.OPEN);
+    }
+
+    public static <C extends Comparable<?>> Range<C> openClosed(C lowerEndpoint, C upperEndpoint)
+    {
+        return range(lowerEndpoint, BoundType.OPEN, upperEndpoint, BoundType.CLOSED);
+    }
+
+    public static <C extends Comparable<?>> Range<C> closedOpen(C lowerEndpoint, C upperEndpoint)
+    {
+        return range(lowerEndpoint, BoundType.CLOSED, upperEndpoint, BoundType.OPEN);
+    }
+
+    public static <C extends Comparable<?>> Range<C> closed(C lowerEndpoint, C upperEndpoint)
+    {
+        return range(lowerEndpoint, BoundType.CLOSED, upperEndpoint, BoundType.CLOSED);
+    }
+
+    public static <C extends Comparable<?>> Range<C> range(final C lowerEndpoint, final BoundType lowerBoundType,
+                           final C upperEndpoint, final BoundType upperBoundType)
+    {
+        return createRange(new Callable<Range<C>>() {
+            @Override
+            public Range<C> call() throws Exception {
+                return Range.range(lowerEndpoint, lowerBoundType, upperEndpoint, upperBoundType);
+            }
+        }, legacyRangeMethod, lowerEndpoint, lowerBoundType, upperEndpoint, upperBoundType);
+    }
+
+    public static <C extends Comparable<?>> Range<C> greaterThan(C lowerEndpoint)
+    {
+        return downTo(lowerEndpoint, BoundType.OPEN);
+    }
+
+    public static <C extends Comparable<?>> Range<C> atLeast(C lowerEndpoint)
+    {
+        return downTo(lowerEndpoint, BoundType.CLOSED);
+    }
+
+    public static <C extends Comparable<?>> Range<C> downTo(final C lowerEndpoint, final BoundType lowerBoundType)
+    {
+        return createRange(new Callable<Range<C>>() {
+            @Override
+            public Range<C> call() throws Exception {
+                return Range.downTo(lowerEndpoint, lowerBoundType);
+            }
+        }, legacyDownToMethod, lowerEndpoint, lowerBoundType);
+    }
+
+    public static <C extends Comparable<?>> Range<C> lessThan(C upperEndpoint)
+    {
+        return upTo(upperEndpoint, BoundType.OPEN);
+    }
+
+    public static <C extends Comparable<?>> Range<C> atMost(C upperEndpoint)
+    {
+        return upTo(upperEndpoint, BoundType.CLOSED);
+    }
+
+    public static <C extends Comparable<?>> Range<C> upTo(final C upperEndpoint, final BoundType upperBoundType)
+    {
+        return createRange(new Callable<Range<C>>() {
+            @Override
+            public Range<C> call() throws Exception {
+                return Range.upTo(upperEndpoint, upperBoundType);
+            }
+        }, legacyUpToMethod, upperEndpoint, upperBoundType);
+    }
+
+    public static <C extends Comparable<?>> Range<C> all()
+    {
+        return createRange(new Callable<Range<C>>() {
+            @Override
+            public Range<C> call() throws Exception {
+                return Range.all();
+            }
+        }, legacyAllMethod);
+    }
+
+    public static <C extends Comparable<?>> Range<C> singleton(final C value)
+    {
+        return createRange(new Callable<Range<C>>() {
+            @Override
+            public Range<C> call() throws Exception {
+                return Range.singleton(value);
+            }
+        }, legacyRangeMethod, value, BoundType.CLOSED, value, BoundType.CLOSED);
+    }
+
+    private static <C extends Comparable<?>> Range<C> createRange(Callable<Range<C>> rangeCallable, Method legacyRangeFactoryMethod, Object ... params)
+    {
+        try {
+            return rangeCallable.call();
+        } catch (NoSuchMethodError noSuchMethodError) {
+            if (legacyRangeFactoryMethod != null) {
+                return invokeLegacyRangeFactoryMethod(legacyRangeFactoryMethod, params);
+            } else {
+                throw noSuchMethodError;
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <C extends Comparable<?>> Range<C> invokeLegacyRangeFactoryMethod(Method method, Object... params)
+    {
+        try {
+            //noinspection unchecked
+            return (Range<C>) method.invoke(null, params);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke legacy Range factory method [" + method.getName() +
+                                               "] with params " + Lists.newArrayList(params) + ".", e);
+        }
+    }
+
+    // prevent instantiation
+    private RangeFactory() { }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaBeanSerializerModifier.java b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaBeanSerializerModifier.java
new file mode 100644
index 0000000..1083ff5
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaBeanSerializerModifier.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.datatype.guava.ser;
+
+import com.fasterxml.jackson.databind.BeanDescription;
+import com.fasterxml.jackson.databind.SerializationConfig;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
+import com.google.common.base.Optional;
+
+import java.util.List;
+
+public class GuavaBeanSerializerModifier extends BeanSerializerModifier {
+
+    @Override
+    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
+            BeanDescription beanDesc,
+            List<BeanPropertyWriter> beanProperties)
+    {
+        for (int i = 0; i < beanProperties.size(); ++i) {
+            final BeanPropertyWriter writer = beanProperties.get(i);
+            if (Optional.class.isAssignableFrom(writer.getPropertyType())) {
+                beanProperties.set(i, new GuavaOptionalBeanPropertyWriter(writer));
+            }
+        }
+        return beanProperties;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalBeanPropertyWriter.java b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalBeanPropertyWriter.java
new file mode 100644
index 0000000..396a274
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalBeanPropertyWriter.java
@@ -0,0 +1,26 @@
+package com.fasterxml.jackson.datatype.guava.ser;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
+import com.google.common.base.Optional;
+
+public class GuavaOptionalBeanPropertyWriter extends BeanPropertyWriter {
+
+    protected GuavaOptionalBeanPropertyWriter(BeanPropertyWriter base) {
+        super(base);
+    }
+
+    @Override
+    public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception
+    {
+        if (_nullSerializer == null) {
+            Object value = get(bean);
+            if (value == null || Optional.absent().equals(value)) {
+                return;
+            }
+        }
+        super.serializeAsField(bean, jgen, prov);
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalSerializer.java
new file mode 100644
index 0000000..5fa2846
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/GuavaOptionalSerializer.java
@@ -0,0 +1,55 @@
+package com.fasterxml.jackson.datatype.guava.ser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import com.google.common.base.Optional;
+
+public final class GuavaOptionalSerializer extends StdSerializer<Optional<?>>
+{
+    public GuavaOptionalSerializer(JavaType type) {
+        super(type);
+    }
+
+    // implemented since 2.3
+    @Override
+    public boolean isEmpty(Optional<?> value) {
+        return (value == null) || !value.isPresent();
+    }
+
+    @Override
+    public void serialize(Optional<?> value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonGenerationException {
+        if(value.isPresent()){
+            provider.defaultSerializeValue(value.get(), jgen);
+        } else{
+            provider.defaultSerializeNull(jgen);
+        }
+    }
+    
+    @Override
+    public void serializeWithType(Optional<?> value,
+                                  JsonGenerator jgen,
+                                  SerializerProvider provider,
+                                  TypeSerializer typeSer) throws IOException, JsonProcessingException {
+        serialize(value, jgen, provider);
+    }
+
+    @Override
+    public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
+        JavaType typeParameter = typeHint.containedType(0);
+        if (typeParameter != null) {
+            visitor.getProvider().findValueSerializer(typeParameter, null).acceptJsonFormatVisitor(visitor, typeParameter);
+            return;
+        }
+        super.acceptJsonFormatVisitor(visitor, typeHint);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java
new file mode 100644
index 0000000..eef48cd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java
@@ -0,0 +1,177 @@
+package com.fasterxml.jackson.datatype.guava.ser;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map.Entry;
+
+import com.fasterxml.jackson.core.*;
+
+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.ContextualSerializer;
+import com.fasterxml.jackson.databind.type.MapLikeType;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+public class MultimapSerializer
+    extends ContainerSerializer<Multimap<?, ?>>
+    implements ContextualSerializer
+{
+    private final MapLikeType _type;
+    private final BeanProperty _property;
+    private final JsonSerializer<Object> _keySerializer;
+    private final TypeSerializer _valueTypeSerializer;
+    private final JsonSerializer<Object> _valueSerializer;
+
+    public MultimapSerializer(SerializationConfig config,
+            MapLikeType type,
+            BeanDescription beanDesc,
+            JsonSerializer<Object> keySerializer,
+            TypeSerializer valueTypeSerializer,
+            JsonSerializer<Object> valueSerializer)
+    {
+        super(type.getRawClass(), false);
+        _type = type;
+        _property = null;
+        _keySerializer = keySerializer;
+        _valueTypeSerializer = valueTypeSerializer;
+        _valueSerializer = valueSerializer;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected MultimapSerializer(MultimapSerializer src, BeanProperty property,
+                JsonSerializer<?> keySerializer,
+                TypeSerializer valueTypeSerializer, JsonSerializer<?> valueSerializer)
+    {
+        super(src);
+        _type = src._type;
+        _property = property;
+        _keySerializer = (JsonSerializer<Object>) keySerializer;
+        _valueTypeSerializer = valueTypeSerializer;
+        _valueSerializer = (JsonSerializer<Object>) valueSerializer;
+    }
+
+    protected MultimapSerializer withResolved(BeanProperty property,
+            JsonSerializer<?> keySer,
+            TypeSerializer vts, JsonSerializer<?> valueSer)
+    {
+        return new MultimapSerializer(this, property, keySer, vts, valueSer);
+    }
+
+    @Override
+    protected ContainerSerializer<?> _withValueTypeSerializer(TypeSerializer typeSer) {
+        return new MultimapSerializer(this, _property, _keySerializer,
+                typeSer, _valueSerializer);
+    }
+    
+    /*
+    /**********************************************************
+    /* Post-processing (contextualization)
+    /**********************************************************
+     */
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider provider,
+            BeanProperty property) throws JsonMappingException
+    {
+        JsonSerializer<?> valueSer = _valueSerializer;
+        if (valueSer == null) { // if type is final, can actually resolve:
+            JavaType valueType = _type.getContentType();
+            if (valueType.isFinal()) {
+                valueSer = provider.findValueSerializer(valueType, property);
+            }
+        } else if (valueSer instanceof ContextualSerializer) {
+            valueSer = ((ContextualSerializer) valueSer).createContextual(provider, property);
+        }
+        JsonSerializer<?> keySer = _keySerializer;
+        if (keySer == null) {
+            keySer = provider.findKeySerializer(_type.getKeyType(), property);
+        } else if (keySer instanceof ContextualSerializer) {
+            keySer = ((ContextualSerializer) keySer).createContextual(provider, property);
+        }
+        // finally, TypeSerializers may need contextualization as well
+        TypeSerializer typeSer = _valueTypeSerializer;
+        if (typeSer != null) {
+            typeSer = typeSer.forProperty(property);
+        }
+        return withResolved(property, keySer, typeSer, valueSer);
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors for ContainerSerializer
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonSerializer<?> getContentSerializer() {
+        return _valueSerializer;
+    }
+
+    @Override
+    public JavaType getContentType() {
+        return _type.getContentType();
+    }
+
+    @Override
+    public boolean hasSingleElement(Multimap<?,?> map) {
+        return map.size() == 1;
+    }
+
+    @Override
+    public boolean isEmpty(Multimap<?,?> map) {
+        return map.isEmpty();
+    }
+    
+    /*
+    /**********************************************************
+    /* Post-processing (contextualization)
+    /**********************************************************
+     */
+    
+    @Override
+    public void serialize(Multimap<?, ?> value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+    {
+        jgen.writeStartObject();
+        if (!value.isEmpty()) {
+            serializeFields(value, jgen, provider);
+        }        
+        jgen.writeEndObject();
+    }
+
+    @Override
+    public void serializeWithType(Multimap<?,?> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonGenerationException
+    {
+        typeSer.writeTypePrefixForObject(value, jgen);
+        serializeFields(value, jgen, provider);
+        typeSer.writeTypeSuffixForObject(value, jgen);
+    }
+
+    private final void serializeFields(Multimap<?, ?> value, JsonGenerator jgen, SerializerProvider provider)
+            throws IOException, JsonProcessingException
+    {
+        for (Entry<?, ? extends Collection<?>> e : value.asMap().entrySet()) {
+            if (_keySerializer != null) {
+                _keySerializer.serialize(e.getKey(), jgen, provider);
+            } else {
+                provider.findKeySerializer(provider.constructType(String.class), _property)
+                    .serialize(e.getKey(), jgen, provider);
+            }
+            if (_valueSerializer != null) {
+                // note: value is a List, but generic type is for contents... so:
+                jgen.writeStartArray();
+                for (Object vv : e.getValue()) {
+                    _valueSerializer.serialize(vv, jgen, provider);
+                }
+                jgen.writeEndArray();
+            } else {
+                provider.defaultSerializeValue(Lists.newArrayList(e.getValue()), jgen);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/guava/ser/RangeSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/RangeSerializer.java
new file mode 100644
index 0000000..097ea3f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/guava/ser/RangeSerializer.java
@@ -0,0 +1,112 @@
+package com.fasterxml.jackson.datatype.guava.ser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import com.google.common.collect.Range;
+
+/**
+ * Jackson serializer for a Guava {@link Range}.
+ */
+public class RangeSerializer extends StdSerializer<Range<?>>
+    implements ContextualSerializer
+{
+    protected final JavaType _rangeType;
+    
+    protected final JsonSerializer<Object> _endpointSerializer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public RangeSerializer(JavaType type) { this(type, null); }
+    
+    @SuppressWarnings("unchecked")
+    public RangeSerializer(JavaType type, JsonSerializer<?> endpointSer)
+    {
+        super(type);
+        _rangeType = type;
+        _endpointSerializer = (JsonSerializer<Object>) endpointSer;
+    }
+    
+    // TODO: can this be implemented with better semantics? Base class only
+    // checks for null
+    @Override
+    public boolean isEmpty(Range<?> value) {
+        return super.isEmpty(value);
+    }
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property) throws JsonMappingException
+    {
+        if (_endpointSerializer == null) {
+            JavaType endpointType = _rangeType.containedType(0);
+            // let's not consider "untyped" (java.lang.Object) to be meaningful here...
+            if (endpointType != null && !endpointType.hasRawClass(Object.class)) {
+                JsonSerializer<?> ser = prov.findValueSerializer(endpointType, property);
+                return new RangeSerializer(_rangeType, ser);
+            }
+        }
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Serialization methods
+    /**********************************************************
+     */
+
+    @Override
+    public void serialize(Range<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        jgen.writeStartObject();
+        _writeContents(value, jgen, provider);
+        jgen.writeEndObject();
+
+    }
+
+    @Override
+    public void serializeWithType(Range<?> value, JsonGenerator jgen, SerializerProvider provider,
+            TypeSerializer typeSer)
+        throws IOException, JsonProcessingException
+    {
+        // Will be serialized as a JSON Object, so:
+        typeSer.writeTypePrefixForObject(value, jgen);
+        _writeContents(value, jgen, provider);
+        typeSer.writeTypeSuffixForObject(value, jgen);
+    }
+
+    private void _writeContents(Range<?> value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException
+    {
+        if (value.hasLowerBound()) {
+            if (_endpointSerializer != null) {
+                jgen.writeFieldName("lowerEndpoint");
+                _endpointSerializer.serialize(value.lowerEndpoint(), jgen, provider);
+            } else {
+                provider.defaultSerializeField("lowerEndpoint", value.lowerEndpoint(), jgen);
+            }
+            provider.defaultSerializeField("lowerBoundType", value.lowerBoundType(), jgen);
+        }
+        if (value.hasUpperBound()) {
+            if (_endpointSerializer != null) {
+                jgen.writeFieldName("upperEndpoint");
+                _endpointSerializer.serialize(value.upperEndpoint(), jgen, provider);
+            } else {
+                provider.defaultSerializeField("upperEndpoint", value.upperEndpoint(), jgen);
+            }
+            provider.defaultSerializeField("upperBoundType", value.upperBoundType(), jgen);
+        }
+    }
+}
diff --git a/src/main/resources/META-INF/LICENSE b/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..f5f45d2
--- /dev/null
+++ b/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,8 @@
+This copy of Jackson JSON processor streaming parser/generator 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/services/com.fasterxml.jackson.databind.Module b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
new file mode 100644
index 0000000..047e1f8
--- /dev/null
+++ b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
@@ -0,0 +1 @@
+com.fasterxml.jackson.datatype.guava.GuavaModule
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/FluentIterableTest.java b/src/test/java/com/fasterxml/jackson/datatype/guava/FluentIterableTest.java
new file mode 100644
index 0000000..493a424
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/FluentIterableTest.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.util.Iterator;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Unit tests to verify serialization of {@link FluentIterable}s.
+ */
+public class FluentIterableTest extends ModuleTestBase
+{
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    FluentIterable<Integer> createFluentIterable() {
+        return new FluentIterable<Integer>() {
+            private final Iterable<Integer> _iterable = Sets.newHashSet(1, 2, 3);
+            @Override
+            public Iterator<Integer> iterator() {
+                return _iterable.iterator();
+            }
+        };
+    }
+
+    /**
+     * This test is present so that we know if either Jackson's handling of FluentIterable
+     * or Guava's implementation of FluentIterable changes.
+     * @throws Exception
+     */
+    public void testSerializationWithoutModule() throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        Iterable<Integer> fluentIterable = createFluentIterable();
+        String json = mapper.writeValueAsString(fluentIterable);
+        assertEquals("{\"empty\":false}", json);
+    }
+
+    public void testSerialization() throws Exception {
+        Iterable<Integer> fluentIterable = createFluentIterable();
+        String json = MAPPER.writeValueAsString(fluentIterable);
+        assertEquals("[1,2,3]", json);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/HostAndPortTest.java b/src/test/java/com/fasterxml/jackson/datatype/guava/HostAndPortTest.java
new file mode 100644
index 0000000..d562363
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/HostAndPortTest.java
@@ -0,0 +1,41 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.net.HostAndPort;
+
+public class HostAndPortTest extends ModuleTestBase
+{
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    public void testSerialization() throws Exception
+    {
+        HostAndPort input = HostAndPort.fromParts("localhost", 80);
+        String json = MAPPER.writeValueAsString(input);
+        assertEquals("\"localhost:80\"", json);
+    }
+
+    public void testDeserialization() throws Exception
+    {
+        // Actually, let's support both old style and new style
+
+        // old:
+        HostAndPort result = MAPPER.readValue(aposToQuotes("{'hostText':'localhost','port':9090}"),
+                HostAndPort.class);
+        assertEquals("localhost", result.getHostText());
+        assertEquals(9090, result.getPort());
+
+        // and new:
+        result = MAPPER.readValue(quote("localhost:7070"), HostAndPort.class);
+        assertEquals("localhost", result.getHostText());
+        assertEquals(7070, result.getPort());
+
+        // and ... error
+        try {
+            MAPPER.readValue("false", HostAndPort.class);
+            fail("Should not deserialize from boolean");
+        } catch (JsonProcessingException e) {
+            verifyException(e, "Unexpected token");
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/IterablesTest.java b/src/test/java/com/fasterxml/jackson/datatype/guava/IterablesTest.java
new file mode 100644
index 0000000..d51ce87
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/IterablesTest.java
@@ -0,0 +1,16 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Iterables;
+
+public class IterablesTest extends ModuleTestBase
+{
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    public void testIterablesSerialization() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(Iterables.limit(Iterables.cycle(1,2,3), 3));
+        assertNotNull(json);
+        assertEquals("[1,2,3]", json);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/ModuleTestBase.java b/src/test/java/com/fasterxml/jackson/datatype/guava/ModuleTestBase.java
new file mode 100644
index 0000000..b9179c1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/ModuleTestBase.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
+
+public abstract class ModuleTestBase extends junit.framework.TestCase
+{
+    protected ModuleTestBase() { }
+    
+    protected ObjectMapper mapperWithModule()
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        mapper.registerModule(new GuavaModule());
+        return mapper;
+    }
+
+    protected String aposToQuotes(String json) {
+        return json.replace("'", "\"");
+    }
+
+    public String quote(String str) {
+        return '"'+str+'"';
+    }
+
+    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+"\"");
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestImmutables.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestImmutables.java
new file mode 100644
index 0000000..857ca55
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestImmutables.java
@@ -0,0 +1,240 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.util.Iterator;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.*;
+
+/**
+ * Unit tests for verifying that various immutable types
+ * (like {@link ImmutableList}, {@link ImmutableMap} and {@link ImmutableSet})
+ * work as expected.
+ * 
+ * @author tsaloranta
+ */
+public class TestImmutables extends ModuleTestBase
+{
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    static class Holder {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+        public Object value;
+
+        public Holder() { }
+        public Holder(Object v) {
+            value = v;
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Unit tests for verifying handling in absence of module registration
+    /**********************************************************************
+     */
+    
+    /**
+     * Immutable types can actually be serialized as regular collections, without
+     * problems.
+     */
+    public void testWithoutSerializers() throws Exception
+    {
+        ImmutableList<Integer> list = ImmutableList.<Integer>builder()
+            .add(1).add(2).add(3).build();
+        assertEquals("[1,2,3]", MAPPER.writeValueAsString(list));
+
+        ImmutableSet<String> set = ImmutableSet.<String>builder()
+            .add("abc").add("def").build();
+        assertEquals("[\"abc\",\"def\"]", MAPPER.writeValueAsString(set));
+
+        ImmutableMap<String,Integer> map = ImmutableMap.<String,Integer>builder()
+            .put("a", 1).put("b", 2).build();
+        assertEquals("{\"a\":1,\"b\":2}", MAPPER.writeValueAsString(map));
+    }
+
+    /**
+     * Deserialization will fail, however.
+     */
+    public void testWithoutDeserializers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            mapper.readValue("[1,2,3]",
+                    new TypeReference<ImmutableList<Integer>>() { });
+            fail("Expected failure for missing deserializer");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find a deserializer");
+        }
+
+        try {
+            mapper.readValue("[1,2,3]", new TypeReference<ImmutableSet<Integer>>() { });
+            fail("Expected failure for missing deserializer");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find a deserializer");
+        }
+
+        try {
+            mapper.readValue("[1,2,3]", new TypeReference<ImmutableSortedSet<Integer>>() { });
+            fail("Expected failure for missing deserializer");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find a deserializer");
+        }
+        
+        try {
+            mapper.readValue("{\"a\":true,\"b\":false}", new TypeReference<ImmutableMap<Integer,Boolean>>() { });
+            fail("Expected failure for missing deserializer");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find a deserializer");
+        }
+    }
+        
+    /*
+    /**********************************************************************
+    /* Unit tests for actual registered module
+    /**********************************************************************
+     */
+
+    public void testImmutableList() throws Exception
+    {
+        ImmutableList<Integer> list = MAPPER.readValue("[1,2,3]", new TypeReference<ImmutableList<Integer>>() { });
+        assertEquals(3, list.size());
+        assertEquals(Integer.valueOf(1), list.get(0));
+        assertEquals(Integer.valueOf(2), list.get(1));
+        assertEquals(Integer.valueOf(3), list.get(2));
+    }
+
+    public void testImmutableSet() throws Exception
+    {
+        ImmutableSet<Integer> set = MAPPER.readValue("[3,7,8]",
+                new TypeReference<ImmutableSet<Integer>>() { });
+        assertEquals(3, set.size());
+        Iterator<Integer> it = set.iterator();
+        assertEquals(Integer.valueOf(3), it.next());
+        assertEquals(Integer.valueOf(7), it.next());
+        assertEquals(Integer.valueOf(8), it.next());
+
+        set = MAPPER.readValue("[  ]",
+                new TypeReference<ImmutableSet<Integer>>() { });
+        assertEquals(0, set.size());
+    }
+
+    public void testImmutableSetFromSingle() throws Exception
+    {
+        ObjectMapper mapper = mapperWithModule()
+            .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+        ImmutableSet<String> set = mapper.readValue("\"abc\"",
+                new TypeReference<ImmutableSet<String>>() { });
+        assertEquals(1, set.size());
+        assertTrue(set.contains("abc"));
+    }
+    
+    public void testTypedImmutableset() throws Exception
+    {
+        ImmutableSet<Integer> set;
+        Holder h;
+        String json;
+        Holder result;
+
+        // First, with one entry
+        set = new ImmutableSet.Builder<Integer>()
+                .add(1).build();
+        h = new Holder(set);
+        json = MAPPER.writeValueAsString(h);
+
+        // so far so good. and back?
+        result = MAPPER.readValue(json, Holder.class);
+        assertNotNull(result.value);
+        if (!(result.value instanceof ImmutableSet<?>)) {
+            fail("Expected ImmutableSet, got "+result.value.getClass());
+        }
+        assertEquals(1, ((ImmutableSet<?>) result.value).size());
+        // and then an empty version:
+        set = new ImmutableSet.Builder<Integer>().build();
+        h = new Holder(set);
+        json = MAPPER.writeValueAsString(h);
+        result = MAPPER.readValue(json, Holder.class);
+        assertNotNull(result.value);
+        if (!(result.value instanceof ImmutableSet<?>)) {
+            fail("Expected ImmutableSet, got "+result.value.getClass());
+        }
+        assertEquals(0, ((ImmutableSet<?>) result.value).size());
+    }
+    
+    public void testImmutableSortedSet() throws Exception
+    {
+        ImmutableSortedSet<Integer> set = MAPPER.readValue("[5,1,2]", new TypeReference<ImmutableSortedSet<Integer>>() { });
+        assertEquals(3, set.size());
+        Iterator<Integer> it = set.iterator();
+        assertEquals(Integer.valueOf(1), it.next());
+        assertEquals(Integer.valueOf(2), it.next());
+        assertEquals(Integer.valueOf(5), it.next());
+    }
+    
+    public void testImmutableMap() throws Exception
+    {
+        final JavaType type = MAPPER.getTypeFactory().constructType(new TypeReference<ImmutableMap<Integer,Boolean>>() { });
+        ImmutableMap<Integer,Boolean> map = MAPPER.readValue("{\"12\":true,\"4\":false}", type);
+        assertEquals(2, map.size());
+        assertEquals(Boolean.TRUE, map.get(Integer.valueOf(12)));
+        assertEquals(Boolean.FALSE, map.get(Integer.valueOf(4)));
+
+        map = MAPPER.readValue("{}", type);
+        assertNotNull(map);
+        assertEquals(0, map.size());
+    }
+
+    public void testTypedImmutableMap() throws Exception
+    {
+        ImmutableMap<String,Integer> map;
+        Holder h;
+        String json;
+        Holder result;
+
+        // First, with one entry
+        map = new ImmutableMap.Builder<String,Integer>()
+                .put("a", 1).build();
+        h = new Holder(map);
+        json = MAPPER.writeValueAsString(h);
+
+        // so far so good. and back?
+        result = MAPPER.readValue(json, Holder.class);
+        assertNotNull(result.value);
+        if (!(result.value instanceof ImmutableMap<?,?>)) {
+            fail("Expected ImmutableMap, got "+result.value.getClass());
+        }
+        assertEquals(1, ((ImmutableMap<?,?>) result.value).size());
+        // and then an empty version:
+        map = new ImmutableMap.Builder<String,Integer>().build();
+        h = new Holder(map);
+        json = MAPPER.writeValueAsString(h);
+        result = MAPPER.readValue(json, Holder.class);
+        assertNotNull(result.value);
+        if (!(result.value instanceof ImmutableMap<?,?>)) {
+            fail("Expected ImmutableMap, got "+result.value.getClass());
+        }
+        assertEquals(0, ((ImmutableMap<?,?>) result.value).size());
+    }
+    
+    public void testImmutableSortedMap() throws Exception
+    {
+        ImmutableSortedMap<Integer,Boolean> map = MAPPER.readValue("{\"12\":true,\"4\":false}", new TypeReference<ImmutableSortedMap<Integer,Boolean>>() { });
+        assertEquals(2, map.size());
+        assertEquals(Boolean.TRUE, map.get(Integer.valueOf(12)));
+        assertEquals(Boolean.FALSE, map.get(Integer.valueOf(4)));
+    }
+    
+    public void testImmutableBiMap() throws Exception
+    {
+        ImmutableBiMap<Integer,Boolean> map = MAPPER.readValue("{\"12\":true,\"4\":false}", new TypeReference<ImmutableBiMap<Integer,Boolean>>() { });
+        assertEquals(2, map.size());
+        assertEquals(Boolean.TRUE, map.get(12));
+        assertEquals(Boolean.FALSE, map.get(4));
+        assertEquals(map.get(12), Boolean.TRUE);
+        assertEquals(map.get(4), Boolean.FALSE);
+    }
+    
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultimaps.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultimaps.java
new file mode 100644
index 0000000..b6b9bc1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultimaps.java
@@ -0,0 +1,205 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.google.common.collect.*;
+
+import java.io.IOException;
+import java.util.Map;
+
+import static com.google.common.collect.TreeMultimap.create;
+
+/**
+ * Unit tests to verify handling of various {@link Multimap}s.
+ *
+ * @author steven at nesscomputing.com
+ */
+public class TestMultimaps extends ModuleTestBase
+{
+    // Test for issue #13 on github, provided by stevenschlansker
+    public static enum MyEnum {
+        YAY,
+        BOO
+    }
+
+    // [Issue#41]
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    static class MultiMapWrapper {
+        @JsonProperty
+        Multimap<String, String> map = ArrayListMultimap.create();
+    }
+
+    private static final String StringStringMultimap =
+            "{\"first\":[\"abc\",\"abc\",\"foo\"]," + "\"second\":[\"bar\"]}";
+
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    public void testMultimap() throws Exception
+    {
+        _testMultimap(TreeMultimap.create(), true,
+                "{\"false\":[false],\"maybe\":[false,true],\"true\":[true]}");
+        _testMultimap(LinkedListMultimap.create(), false,
+                "{\"true\":[true],\"false\":[false],\"maybe\":[true,false]}");
+        _testMultimap(LinkedHashMultimap.create(), false, null);
+    }
+    
+    private void _testMultimap(Multimap<?,?> map0, boolean fullyOrdered, String EXPECTED) throws Exception
+    {
+        @SuppressWarnings("unchecked")
+        Multimap<String,Boolean> map = (Multimap<String,Boolean>) map0;
+        map.put("true", Boolean.TRUE);
+        map.put("false", Boolean.FALSE);
+        map.put("maybe", Boolean.TRUE);
+        map.put("maybe", Boolean.FALSE);
+
+        // Test that typed writes work
+        if (EXPECTED != null) {
+            String json =  MAPPER.writerWithType(new TypeReference<Multimap<String, Boolean>>() {}).writeValueAsString(map);
+            assertEquals(EXPECTED, json);
+        }
+
+        // And untyped too
+        String serializedForm = MAPPER.writeValueAsString(map);
+
+        if (EXPECTED != null) {
+            assertEquals(EXPECTED, serializedForm);
+        }
+
+        // these seem to be order-sensitive as well, so only use for ordered-maps
+        if (fullyOrdered) {
+            assertEquals(map, MAPPER.<Multimap<String, Boolean>>readValue(serializedForm, new TypeReference<TreeMultimap<String, Boolean>>() {}));
+            assertEquals(map, create(MAPPER.<Multimap<String, Boolean>>readValue(serializedForm, new TypeReference<Multimap<String, Boolean>>() {})));
+            assertEquals(map, create(MAPPER.<Multimap<String, Boolean>>readValue(serializedForm, new TypeReference<HashMultimap<String, Boolean>>() {})));
+            assertEquals(map, create(MAPPER.<Multimap<String, Boolean>>readValue(serializedForm, new TypeReference<ImmutableMultimap<String, Boolean>>() {})));
+        }
+    }
+
+    public void testMultimapIssue3() throws Exception
+    {
+        Multimap<String, String> m1 = TreeMultimap.create();
+        m1.put("foo", "bar");
+        m1.put("foo", "baz");
+        m1.put("qux", "quux");
+        ObjectMapper o = MAPPER;
+        
+        String t1 = o.writerWithType(new TypeReference<TreeMultimap<String, String>>(){}).writeValueAsString(m1);
+        Map<?,?> javaMap = o.readValue(t1, Map.class);
+        assertEquals(2, javaMap.size());
+        
+        String t2 = o.writerWithType(new TypeReference<Multimap<String, String>>(){}).writeValueAsString(m1);
+        javaMap = o.readValue(t2, Map.class);
+        assertEquals(2, javaMap.size());
+        
+        TreeMultimap<String, String> m2 = TreeMultimap.create();
+        m2.put("foo", "bar");
+        m2.put("foo", "baz");
+        m2.put("qux", "quux");
+        
+        String t3 = o.writerWithType(new TypeReference<TreeMultimap<String, String>>(){}).writeValueAsString(m2);
+        javaMap = o.readValue(t3, Map.class);
+        assertEquals(2, javaMap.size());
+   
+        String t4 = o.writerWithType(new TypeReference<Multimap<String, String>>(){}).writeValueAsString(m2);
+        javaMap = o.readValue(t4, Map.class);
+        assertEquals(2, javaMap.size());
+    }
+
+    public void testEnumKey() throws Exception
+    {
+        final TypeReference<TreeMultimap<MyEnum, Integer>> type = new TypeReference<TreeMultimap<MyEnum, Integer>>() {};
+        final Multimap<MyEnum, Integer> map = TreeMultimap.create();
+
+        map.put(MyEnum.YAY, 5);
+        map.put(MyEnum.BOO, 2);
+
+        final String serializedForm = MAPPER.writerWithType(type).writeValueAsString(map);
+
+        assertEquals(serializedForm, MAPPER.writeValueAsString(map));
+        assertEquals(map, MAPPER.readValue(serializedForm, type));
+    }
+
+    // [Issue#41]
+    public void testEmptyMapExclusion() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(new MultiMapWrapper());
+        assertEquals("{}", json);
+    }
+    
+    /*
+    /**********************************************************************
+    /* Unit tests for set-based multimaps
+    /**********************************************************************
+     */
+    public void testTreeMultimap() {
+
+    }
+
+    public void testForwardingSortedSetMultimap() {
+
+    }
+
+    public void testImmutableSetMultimap() {
+        // TODO look at others
+    }
+
+    public void testHashMultimap() throws IOException {
+        SetMultimap<String, String> map =
+                setBasedHelper(new TypeReference<HashMultimap<String, String>>() {
+                });
+        assertTrue(map instanceof HashMultimap);
+    }
+
+    public void testLinkedHashMultimap() throws IOException {
+        SetMultimap<String, String> map =
+                setBasedHelper(new TypeReference<LinkedHashMultimap<String, String>>() {
+                });
+        assertTrue(map instanceof LinkedHashMultimap);
+    }
+
+    public void testForwardingSetMultimap() {
+
+    }
+
+    private SetMultimap<String, String> setBasedHelper(TypeReference<?> type)
+            throws IOException
+    {
+        SetMultimap<String, String> map = MAPPER.readValue(StringStringMultimap, type);
+        assertEquals(3, map.size());
+        assertTrue(map.containsEntry("first", "abc"));
+        assertTrue(map.containsEntry("first", "foo"));
+        assertTrue(map.containsEntry("second", "bar"));
+        return map;
+    }
+
+    /*
+    /**********************************************************************
+    /* Unit tests for list-based multimaps
+    /**********************************************************************
+     */
+
+    public void testArrayListMultimap() throws IOException {
+        ListMultimap<String, String> map =
+                listBasedHelper(new TypeReference<ArrayListMultimap<String, String>>() {
+                });
+        assertTrue(map instanceof ArrayListMultimap);
+    }
+
+    public void testLinkedListMultimap() throws IOException {
+        ListMultimap<String, String> map =
+                listBasedHelper(new TypeReference<LinkedListMultimap<String, String>>() {
+                });
+        assertTrue(map instanceof LinkedListMultimap);
+    }
+
+    private ListMultimap<String, String> listBasedHelper(TypeReference<?> type) throws IOException {
+        ListMultimap<String, String> map = MAPPER.readValue(StringStringMultimap, type);
+        assertEquals(4, map.size());
+        assertTrue(map.remove("first", "abc"));
+        assertTrue(map.containsEntry("first", "abc"));
+        assertTrue(map.containsEntry("first", "foo"));
+        assertTrue(map.containsEntry("second", "bar"));
+        return map;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultisets.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultisets.java
new file mode 100644
index 0000000..07c23c5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestMultisets.java
@@ -0,0 +1,110 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.*;
+
+import com.google.common.collect.*;
+
+/**
+ * Unit tests to verify handling of various {@link Multiset}s.
+ * 
+ * @author tsaloranta
+ */
+public class TestMultisets extends ModuleTestBase
+{
+
+    /*
+    /**********************************************************************
+    /* Unit tests for verifying handling in absence of module registration
+    /**********************************************************************
+     */
+    
+    /**
+     * Multi-sets can actually be serialized as regular collections, without
+     * problems.
+     */
+    public void testWithoutSerializers() throws Exception
+    {
+        
+        ObjectMapper mapper = new ObjectMapper();
+        Multiset<String> set = LinkedHashMultiset.create();
+        // hash-based multi-sets actually keeps 'same' instances together, otherwise insertion-ordered:
+        set.add("abc");
+        set.add("foo");
+        set.add("abc");
+        String json = mapper.writeValueAsString(set);
+        assertEquals("[\"abc\",\"abc\",\"foo\"]", json);
+    }
+
+    public void testWithoutDeserializers() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        try {
+            /*Multiset<String> set =*/ mapper.readValue("[\"abc\",\"abc\",\"foo\"]",
+                    new TypeReference<Multiset<String>>() { });
+            fail("Should have failed");
+        } catch (JsonMappingException e) {
+            verifyException(e, "can not find a deserializer");
+        }
+    }
+    
+    /*
+    /**********************************************************************
+    /* Unit tests for actual registered module
+    /**********************************************************************
+     */
+
+    final ObjectMapper MAPPER = mapperWithModule();
+    
+    public void testDefault() throws Exception
+    {
+        Multiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<Multiset<String>>() { });
+        assertEquals(3, set.size());
+        assertEquals(1, set.count("foo"));
+        assertEquals(2, set.count("abc"));
+        assertEquals(0, set.count("bar"));
+    }
+    
+    public void testLinkedHashMultiset() throws Exception {
+        LinkedHashMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<LinkedHashMultiset<String>>() { });
+        assertEquals(3, set.size());
+        assertEquals(1, set.count("foo"));
+        assertEquals(2, set.count("abc"));
+        assertEquals(0, set.count("bar"));
+    }
+    
+    public void testHashMultiset() throws Exception {
+        HashMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<HashMultiset<String>>() { });
+        assertEquals(3, set.size());
+        assertEquals(1, set.count("foo"));
+        assertEquals(2, set.count("abc"));
+        assertEquals(0, set.count("bar"));
+    }
+    
+    public void testTreeMultiset() throws Exception {
+        TreeMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<TreeMultiset<String>>() { });
+        assertEquals(3, set.size());
+        assertEquals(1, set.count("foo"));
+        assertEquals(2, set.count("abc"));
+        assertEquals(0, set.count("bar"));
+    }
+    
+    public void testImmutableMultiset() throws Exception {
+        ImmutableMultiset<String> set = MAPPER.readValue("[\"abc\",\"abc\",\"foo\"]", new TypeReference<ImmutableMultiset<String>>() { });
+        assertEquals(3, set.size());
+        assertEquals(1, set.count("foo"));
+        assertEquals(2, set.count("abc"));
+        assertEquals(0, set.count("bar"));
+    }
+
+    public void testFromSingle() throws Exception
+    {
+        ObjectMapper mapper = mapperWithModule()
+            .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+        Multiset<String> set = mapper.readValue("\"abc\"",
+                new TypeReference<Multiset<String>>() { });
+        assertEquals(1, set.size());
+        assertTrue(set.contains("abc"));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptional.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptional.java
new file mode 100644
index 0000000..fab74d0
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptional.java
@@ -0,0 +1,176 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import com.google.common.base.Optional;
+
+public class TestOptional extends ModuleTestBase
+{
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    @JsonAutoDetect(fieldVisibility=Visibility.ANY)
+    public static final class OptionalData{
+        private Optional<String> myString;
+    }
+    
+    @JsonAutoDetect(fieldVisibility=Visibility.ANY)
+    public static final class OptionalGenericData<T>{
+        private Optional<T> myData;
+    }
+
+    @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class)
+    public static class Unit
+    {
+//        @JsonIdentityReference(alwaysAsId=true)
+        public Optional<Unit> baseUnit;
+        
+        public Unit() { }
+        public Unit(Optional<Unit> u) { baseUnit = u; }
+        
+        public void link(Unit u) {
+            baseUnit = Optional.of(u);
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    public void testDeserAbsent() throws Exception {
+        Optional<?> value = MAPPER.readValue("null", new TypeReference<Optional<String>>() {});
+        assertFalse(value.isPresent());
+    }
+    
+    public void testDeserSimpleString() throws Exception{
+        Optional<?> value = MAPPER.readValue("\"simpleString\"", new TypeReference<Optional<String>>() {});
+        assertTrue(value.isPresent());
+        assertEquals("simpleString", value.get());
+    }
+    
+    public void testDeserInsideObject() throws Exception {
+        OptionalData data = MAPPER.readValue("{\"myString\":\"simpleString\"}", OptionalData.class);
+        assertTrue(data.myString.isPresent());
+        assertEquals("simpleString", data.myString.get());
+    }
+    
+    public void testDeserComplexObject() throws Exception {
+        TypeReference<Optional<OptionalData>> type = new TypeReference<Optional<OptionalData>>() {};
+        Optional<OptionalData> data = MAPPER.readValue("{\"myString\":\"simpleString\"}", type);
+        assertTrue(data.isPresent());
+        assertTrue(data.get().myString.isPresent());
+        assertEquals("simpleString", data.get().myString.get());
+    }
+    
+    public void testDeserGeneric() throws Exception {
+        TypeReference<Optional<OptionalGenericData<String>>> type = new TypeReference<Optional<OptionalGenericData<String>>>() {};
+        Optional<OptionalGenericData<String>> data = MAPPER.readValue("{\"myData\":\"simpleString\"}", type);
+        assertTrue(data.isPresent());
+        assertTrue(data.get().myData.isPresent());
+        assertEquals("simpleString", data.get().myData.get());
+    }
+    
+    public void testSerAbsent() throws Exception {
+        String value = MAPPER.writeValueAsString(Optional.absent());
+        assertEquals("null", value);
+    }
+    
+    public void testSerSimpleString() throws Exception {
+        String value = MAPPER.writeValueAsString(Optional.of("simpleString"));
+        assertEquals("\"simpleString\"", value);
+    }
+    
+    public void testSerInsideObject() throws Exception {
+        OptionalData data = new OptionalData();
+        data.myString = Optional.of("simpleString");
+        String value = MAPPER.writeValueAsString(data);
+        assertEquals("{\"myString\":\"simpleString\"}", value);
+    }
+    
+    public void testSerComplexObject() throws Exception {
+        OptionalData data = new OptionalData();
+        data.myString = Optional.of("simpleString");
+        String value = MAPPER.writeValueAsString(Optional.of(data));
+        assertEquals("{\"myString\":\"simpleString\"}", value);
+    }
+    
+    public void testSerGeneric() throws Exception {
+        OptionalGenericData<String> data = new OptionalGenericData<String>();
+        data.myData = Optional.of("simpleString");
+        String value = MAPPER.writeValueAsString(Optional.of(data));
+        assertEquals("{\"myData\":\"simpleString\"}", value);
+    }
+
+    public void testSerNonNull() throws Exception {
+        OptionalData data = new OptionalData();
+        data.myString = Optional.absent();
+        String value = mapperWithModule().setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
+        assertEquals("{}", value);
+    }
+    
+    public void testSerOptNull() throws Exception {
+        OptionalData data = new OptionalData();
+        data.myString = null;
+        String value = mapperWithModule().setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
+        assertEquals("{}", value);
+    }
+    
+    public void testWithTypingEnabled() throws Exception
+    {
+		final ObjectMapper objectMapper = mapperWithModule();
+		// ENABLE TYPING
+		objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
+
+		final OptionalData myData = new OptionalData();
+		myData.myString = Optional.fromNullable("abc");
+		
+		final String json = objectMapper.writeValueAsString(myData);
+		
+		final OptionalData deserializedMyData = objectMapper.readValue(json, OptionalData.class);
+		assertEquals(myData.myString, deserializedMyData.myString);
+    }
+
+    // [Issue#17]
+    public void testObjectId() throws Exception
+    {
+        final Unit input = new Unit();
+        input.link(input);
+        String json = MAPPER.writeValueAsString(input);
+        Unit result = MAPPER.readValue(json,  Unit.class);
+        assertNotNull(result);
+        assertNotNull(result.baseUnit);
+        assertTrue(result.baseUnit.isPresent());
+        Unit base = result.baseUnit.get();
+        assertSame(result, base);
+    }
+
+    // [Issue#37]
+    public void testOptionalCollection() throws Exception {
+        ObjectMapper mapper = new ObjectMapper().registerModule(new GuavaModule());
+
+        TypeReference<List<Optional<String>>> typeReference =
+            new TypeReference<List<Optional<String>>>() {};
+
+        List<Optional<String>> list = new ArrayList<Optional<String>>();
+        list.add(Optional.of("2014-1-22"));
+        list.add(Optional.<String>absent());
+        list.add(Optional.of("2014-1-23"));
+
+        String str = mapper.writeValueAsString(list);
+        assertEquals("[\"2014-1-22\",null,\"2014-1-23\"]", str);
+
+        List<Optional<String>> result = mapper.readValue(str, typeReference);
+        assertEquals(list.size(), result.size());
+        for (int i = 0; i < list.size(); ++i) {
+            assertEquals("Entry #"+i, list.get(i), result.get(i));
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptionalWithPolymorphic.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptionalWithPolymorphic.java
new file mode 100644
index 0000000..782bbc7
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestOptionalWithPolymorphic.java
@@ -0,0 +1,106 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.databind.*;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+
+public class TestOptionalWithPolymorphic extends ModuleTestBase
+{
+    static class ContainerA {
+        @JsonProperty private Optional<String> name = Optional.absent();
+        @JsonProperty private Optional<Strategy> strategy = Optional.absent();
+    }
+
+    static class ContainerB {
+        @JsonProperty private Optional<String> name = Optional.absent();
+        @JsonProperty private Strategy strategy = null;
+    }
+         
+    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
+    @JsonSubTypes({
+    @JsonSubTypes.Type(name = "Foo", value = Foo.class),
+    @JsonSubTypes.Type(name = "Bar", value = Bar.class),
+    @JsonSubTypes.Type(name = "Baz", value = Baz.class)
+    })
+    interface Strategy {
+    }
+     
+    static class Foo implements Strategy {
+        @JsonProperty private final int foo;
+        @JsonCreator Foo(@JsonProperty("foo") int foo) {
+            this.foo = foo;
+        }
+    }
+         
+    static class Bar implements Strategy {
+        @JsonProperty private final boolean bar;
+        @JsonCreator Bar(@JsonProperty("bar") boolean bar) {
+            this.bar = bar;
+        }
+    }
+         
+    static class Baz implements Strategy {
+        @JsonProperty private final String baz;
+        @JsonCreator Baz(@JsonProperty("baz") String baz) {
+            this.baz = baz;
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Test methods
+    /**********************************************************************
+     */
+
+    final ObjectMapper MAPPER = mapperWithModule();
+    
+    public void testOptionalMapsFoo() throws Exception {
+
+        ImmutableMap<String, Object> foo = ImmutableMap.<String, Object>builder()
+            .put("name", "foo strategy")
+            .put("strategy", ImmutableMap.builder()
+            .put("type", "Foo")
+            .put("foo", 42)
+            .build())
+            .build();
+        _test(MAPPER, foo);
+    }
+
+    public void testOptionalMapsBar() throws Exception {
+
+        ImmutableMap<String, Object> bar = ImmutableMap.<String, Object>builder()
+            .put("name", "bar strategy")
+            .put("strategy", ImmutableMap.builder()
+            .put("type", "Bar")
+            .put("bar", true)
+            .build())
+            .build();
+        _test(MAPPER, bar);
+    }
+
+    public void testOptionalMapsBaz() throws Exception {
+        ImmutableMap<String, Object> baz = ImmutableMap.<String, Object>builder()
+            .put("name", "baz strategy")
+            .put("strategy", ImmutableMap.builder()
+            .put("type", "Baz")
+            .put("baz", "hello world!")
+            .build())
+            .build();
+        _test(MAPPER, baz);
+    }
+         
+    private void _test(ObjectMapper m, Map<String, ?> map) throws Exception
+    {
+        String json = m.writeValueAsString(map);
+
+        ContainerA objA = m.readValue(json, ContainerA.class);
+        assertNotNull(objA);
+
+        ContainerB objB = m.readValue(json, ContainerB.class);
+        assertNotNull(objB);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestRange.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestRange.java
new file mode 100644
index 0000000..3a1a741
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestRange.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.guava.deser.util.RangeFactory;
+import com.google.common.base.Objects;
+import com.google.common.collect.Range;
+
+import java.io.IOException;
+
+/**
+ * Unit tests to verify serialization of Guava {@link Range}s.
+ */
+public class TestRange extends ModuleTestBase {
+
+    private final ObjectMapper MAPPER = mapperWithModule();
+
+    protected static class Untyped
+    {
+        @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+        public Object range;
+
+        public Untyped() { }
+        public Untyped(Range<?> r) { range = r; }
+    }
+    
+    /**
+     * This test is present so that we know if either Jackson's handling of Range
+     * or Guava's implementation of Range changes.
+     * @throws Exception
+     */
+    public void testSerializationWithoutModule() throws Exception
+    {
+        ObjectMapper mapper = new ObjectMapper();
+        Range<Integer> range = RangeFactory.closed(1, 10);
+        String json = mapper.writeValueAsString(range);
+        assertEquals("{\"empty\":false}", json);
+    }
+
+    public void testSerialization() throws Exception
+    {
+        testSerialization(MAPPER, RangeFactory.open(1, 10));
+        testSerialization(MAPPER, RangeFactory.openClosed(1, 10));
+        testSerialization(MAPPER, RangeFactory.closedOpen(1, 10));
+        testSerialization(MAPPER, RangeFactory.closed(1, 10));
+        testSerialization(MAPPER, RangeFactory.atLeast(1));
+        testSerialization(MAPPER, RangeFactory.greaterThan(1));
+        testSerialization(MAPPER, RangeFactory.atMost(10));
+        testSerialization(MAPPER, RangeFactory.lessThan(10));
+        testSerialization(MAPPER, RangeFactory.all());
+        testSerialization(MAPPER, RangeFactory.singleton(1));
+    }
+
+    public void testDeserialization() throws Exception
+    {
+        String json = MAPPER.writeValueAsString(RangeFactory.open(1, 10));
+        @SuppressWarnings("unchecked")
+        Range<Integer> r = (Range<Integer>) MAPPER.readValue(json, Range.class);
+        assertNotNull(r);
+        assertEquals(Integer.valueOf(1), r.lowerEndpoint());
+        assertEquals(Integer.valueOf(10), r.upperEndpoint());
+    }
+    
+    private void testSerialization(ObjectMapper objectMapper, Range<?> range) throws IOException
+    {
+        String json = objectMapper.writeValueAsString(range);
+        Range<?> rangeClone = objectMapper.readValue(json, Range.class);
+        assert Objects.equal(rangeClone, range);
+    }
+
+    public void testUntyped() throws Exception
+    {
+        String json = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(new Untyped(RangeFactory.open(1, 10)));
+        Untyped out = MAPPER.readValue(json, Untyped.class);
+        assertNotNull(out);
+        assertEquals(Range.class, out.range.getClass());
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/guava/TestVersions.java b/src/test/java/com/fasterxml/jackson/datatype/guava/TestVersions.java
new file mode 100644
index 0000000..a43a4d5
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/guava/TestVersions.java
@@ -0,0 +1,38 @@
+package com.fasterxml.jackson.datatype.guava;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.Versioned;
+import com.fasterxml.jackson.core.util.VersionUtil;
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
+import com.fasterxml.jackson.datatype.guava.PackageVersion;
+
+public class TestVersions extends ModuleTestBase
+{
+    public void testMapperVersions() throws IOException
+    {
+        GuavaModule module = new GuavaModule();
+        assertVersion(module);
+    }
+
+    public void testPackageVersion()
+    {
+        assertEquals(PackageVersion.VERSION,
+                VersionUtil.versionFor(GuavaModule.class));
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void assertVersion(Versioned vers)
+    {
+        final Version v = vers.version();
+        assertFalse("Should find version information (got "+v+")", v.isUknownVersion());
+        assertEquals(PackageVersion.VERSION, v);
+    }
+}
+

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



More information about the pkg-java-commits mailing list