[jackson-dataformat-yaml] 01/04: Imported Upstream version 2.2.3

Wolodja Wentland babilen-guest at alioth.debian.org
Fri Sep 27 12:19:15 UTC 2013


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

babilen-guest pushed a commit to annotated tag debian/2.2.3-1
in repository jackson-dataformat-yaml.

commit c33011ba22580b8e20d02d0279e8f02f1e4a4150
Author: Wolodja Wentland <babilen at gmail.com>
Date:   Thu Sep 26 11:48:27 2013 +0100

    Imported Upstream version 2.2.3
---
 .gitignore                                         |   21 +
 README.md                                          |   57 ++
 contributor-agreement.pdf                          |  Bin 0 -> 45328 bytes
 pom.xml                                            |  130 ++++
 profile-perf.sh                                    |    6 +
 release-notes/CREDITS                              |   12 +
 release-notes/VERSION                              |   66 ++
 .../jackson/dataformat/yaml/PackageVersion.java.in |   20 +
 .../jackson/dataformat/yaml/UTF8Reader.java        |  521 ++++++++++++++
 .../jackson/dataformat/yaml/UTF8Writer.java        |  412 +++++++++++
 .../jackson/dataformat/yaml/YAMLFactory.java       |  681 ++++++++++++++++++
 .../jackson/dataformat/yaml/YAMLGenerator.java     |  545 ++++++++++++++
 .../jackson/dataformat/yaml/YAMLParser.java        |  743 ++++++++++++++++++++
 src/main/resources/META-INF/LICENSE                |    8 +
 src/main/resources/META-INF/NOTICE                 |   20 +
 .../com.fasterxml.jackson.core.JsonFactory         |    1 +
 .../jackson/dataformat/yaml/EventsTest.java        |   52 ++
 .../dataformat/yaml/FormatDetectionTest.java       |   52 ++
 .../jackson/dataformat/yaml/ModuleTestBase.java    |  159 +++++
 .../dataformat/yaml/SimpleDatabindTest.java        |   81 +++
 .../dataformat/yaml/SimpleGenerationTest.java      |   76 ++
 .../jackson/dataformat/yaml/SimpleParseTest.java   |  281 ++++++++
 .../jackson/dataformat/yaml/TestVersions.java      |   30 +
 src/test/java/perf/DeserPerf.java                  |  142 ++++
 src/test/java/perf/MediaItem.java                  |  396 +++++++++++
 src/test/java/perf/SerPerf.java                    |  146 ++++
 26 files changed, 4658 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84914ec
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+# use glob syntax.
+syntax: glob
+*.class
+*~
+*.bak
+*.off
+*.old
+.DS_Store
+
+# building
+target
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# IDEA
+*.iml
+*.ipr
+*.iws
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..477dd76
--- /dev/null
+++ b/README.md
@@ -0,0 +1,57 @@
+# Overview
+
+This project contains [Jackson](http://http://wiki.fasterxml.com/JacksonHome) extension component for reading and writing [YAML](http://en.wikipedia.org/wiki/YAML) encoded data.
+[SnakeYAML](http://code.google.com/p/snakeyaml/) library is used for low-level YAML parsing.
+This project adds necessary abstractions on top to make things work with other Jackson functionality.
+
+Project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt).
+
+# Status
+
+Project is in its prototype phase, so:
+
+* Basic parsing seems to work, as per basic unit tests
+* Basic generation: not configurable, produces visually ok block format
+* Even format auto-detection works! (can create `ObjectMapper` with multiple `JsonFactory` instances, give an `InputStream`, and it'll figure out what format content is in!)
+
+Missing are:
+
+* Not much configurability: might make sense to esp. allow configuration of generation details
+* Support for YAML tags (which theoretically could help with typing), aliases and anchors (which would be good for Object Id, refs): ideally these would be supported. And it is possible in principle, no fundamental problems.
+
+## Maven dependency
+
+To use this extension on Maven-based projects, use following dependency:
+
+```xml
+<dependency>
+  <groupId>com.fasterxml.jackson.dataformat</groupId>
+  <artifactId>jackson-dataformat-yaml</artifactId>
+  <version>2.1.3</version>
+</dependency>
+```
+
+# Usage
+
+## Simple usage
+
+Usage is as with basic `JsonFactory`; most commonly you will just construct a standard `ObjectMapper` with `com.fasterxml.jackson.dataformat.yaml.YAMLFactory`, like so:
+
+```java
+ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+User user = mapper.readValue(yamlSource, User.class);
+```
+
+but you can also just use underlying `YAMLFactory` and parser it produces, for event-based processing:
+
+```java
+YAMLFactory factory = new YAMLFactory();
+JsonParser parser = factory.createJsonParser(yamlString); // don't be fooled by method name...
+while (parser.nextToken() != null) {
+  // do something!
+}
+```
+
+# Documentation
+
+* [Documentation](jackson-dataformat-yaml/wiki/Documentation) IS TO BE WRITTEN
diff --git a/contributor-agreement.pdf b/contributor-agreement.pdf
new file mode 100644
index 0000000..fe36036
Binary files /dev/null and b/contributor-agreement.pdf differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b85531d
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion> 
+
+  <parent>
+    <groupId>com.fasterxml</groupId>
+    <artifactId>oss-parent</artifactId>
+    <version>11</version>
+  </parent>
+
+  <groupId>com.fasterxml.jackson.dataformat</groupId>
+  <artifactId>jackson-dataformat-yaml</artifactId>
+  <version>2.2.3</version>
+
+  <name>Jackson-dataformat-YAML</name>
+  <description>Support for reading and writing YAML-encoded data via Jackson
+abstractions.
+  </description>
+  <url>http://wiki.fasterxml.com/JacksonExtensionYAML</url>
+
+  <scm>
+    <connection>scm:git:git at github.com:FasterXML/jackson-dataformat-yaml.git</connection>
+    <developerConnection>scm:git:git at github.com:FasterXML/jackson-dataformat-yaml.git</developerConnection>
+    <url>http://github.com/FasterXML/jackson-dataformat-yaml</url>    
+    <tag>jackson-dataformat-yaml-2.2.3</tag>
+  </scm>
+
+  <properties>
+    <jackson.core.version>2.2.3</jackson.core.version>
+    <jackson.annotations.version>2.2.3</jackson.annotations.version>
+    <packageVersion.dir>com/fasterxml/jackson/dataformat/yaml</packageVersion.dir>
+    <packageVersion.package>${project.groupId}.yaml</packageVersion.package>
+
+    <osgi.export>${project.groupId}.yaml;version=${project.version}</osgi.export>
+    <osgi.import>
+org.yaml.snakeyaml,
+org.yaml.snakeyaml.emitter,
+org.yaml.snakeyaml.error,
+org.yaml.snakeyaml.events,
+org.yaml.snakeyaml.parser,
+org.yaml.snakeyaml.reader,
+com.fasterxml.jackson.core,
+com.fasterxml.jackson.core.base,
+com.fasterxml.jackson.core.format,
+com.fasterxml.jackson.core.io,
+com.fasterxml.jackson.core.json,
+com.fasterxml.jackson.core.type,
+com.fasterxml.jackson.core.util,
+</osgi.import>
+  </properties>
+
+  <dependencies>
+    <!-- Extends Jackson core; uses SnakeYAML for parsing, generation -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>${jackson.core.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.yaml</groupId>
+      <artifactId>snakeyaml</artifactId>
+      <version>1.10</version>
+    </dependency>
+
+     <!-- and for testing, JUnit (or TestNG?) is needed; as well as databinder, annotatons -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.10</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+      <version>${jackson.annotations.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>${jackson.core.version}</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <!-- Inherited from oss-base. Generate PackageVersion.java.-->
+        <groupId>com.google.code.maven-replacer-plugin</groupId>
+        <artifactId>replacer</artifactId>
+        <executions>
+          <execution>
+            <id>process-packageVersion</id>
+            <phase>generate-sources</phase>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <!--  We will shade SnakeYAML, to simplify deployment, avoid version conflicts -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>1.4</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <artifactSet>
+                <includes>
+                  <include>org.yaml:snakeyaml</include>
+                </includes>
+              </artifactSet>
+              <relocations>
+                <relocation>
+                  <pattern>org.yaml.snakeyaml</pattern>
+                  <shadedPattern>com.fasterxml.jackson.dataformat.yaml.snakeyaml</shadedPattern>
+                </relocation>
+              </relocations>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/profile-perf.sh b/profile-perf.sh
new file mode 100755
index 0000000..d66b6ec
--- /dev/null
+++ b/profile-perf.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+java -Xmx64m -server \
+  -cp target/classes:target/test-classes:lib/\* \
+   -Xrunhprof:cpu=samples,depth=10,verbose=n,interval=2 \
+$*
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
new file mode 100644
index 0000000..f250251
--- /dev/null
+++ b/release-notes/CREDITS
@@ -0,0 +1,12 @@
+Here are people who have contributed to development Jackson JSON process 
+databind core component, version 2.x
+(version numbers in brackets indicate release in which the problem was fixed)
+
+(note: for older credits, check out release notes for 1.x versions)
+
+Tatu Saloranta, tatu.saloranta at iki.fi: author
+
+Shawn Smith:
+
+- Contibuted [Issue-14]: Fix parsing of longs near MIN/MAX_INT, parsing of BigIntegers.
+ (2.1.4)
diff --git a/release-notes/VERSION b/release-notes/VERSION
new file mode 100644
index 0000000..b2c745c
--- /dev/null
+++ b/release-notes/VERSION
@@ -0,0 +1,66 @@
+Project: jackson-dataformat-yaml
+Version: 2.2.3 (22-Aug-2013)
+
+No functional changes.
+
+------------------------------------------------------------------------
+=== History: ===
+------------------------------------------------------------------------
+
+2.2.2 (27-May-2013)
+
+No functional changes.
+
+2.2.1 (03-May-2013)
+
+- Fixed problem with YamlFactory.copy()
+
+2.2.0 (22-Apr-2013)
+
+New minor version; no functional changes.
+
+2.1.3 (19-Jan-2013)
+
+- [Issue-10]: Avoid parsing scalars in non-plain flow styles
+ (contributed by nkvoll at github)
+- [Issue-12]: Problems with 'YAMLFactory.createJsonGenerator(File)'
+
+2.1.2 (04-Dec-2012)
+
+No functional changes.
+
+2.1.1 (12-Nov-2012)
+
+No functional changes.
+
+2.1.0 (08-Oct-2012)
+
+Another minor update on Jackson YAML format module
+
+Improvements:
+
+- [Issue-5]: Expose Aliases as JsonToken.STRING_VALUE tokens
+- [Issue-6]: Add a method in YAMLParser to detect Anchors: `getCurrentAnchor()`
+- [Issue-7]: Possible parsing problems with unquoted values with colons
+
+2.0.5 (27-Jul-2012)
+
+- [Issue-4]: Regexp for detecting floating point numbers too permissive with dots
+ (reported by Dan Sheehan)
+ 
+2.0.4: skipped release (no changes)
+
+2.0.3: skipped release (no changes)
+
+2.0.2 (14-May-2012)
+
+Improvements:
+
+- 10-20% performance improvements based on profiling (most overhead
+  in SnakeYaml, so limited possibilities)
+
+2.0.0 (03-May-2012)
+
+Fixes:
+
+* [Issue-2] UUIDs not properly deserialized.
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/PackageVersion.java.in b/src/main/java/com/fasterxml/jackson/dataformat/yaml/PackageVersion.java.in
new file mode 100644
index 0000000..7860aa1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/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/dataformat/yaml/UTF8Reader.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/UTF8Reader.java
new file mode 100644
index 0000000..e924270
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/UTF8Reader.java
@@ -0,0 +1,521 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.lang.ref.SoftReference;
+
+/**
+ * Optimized Reader that reads UTF-8 encoded content from an input stream.
+ * In addition to doing (hopefully) optimal conversion, it can also take
+ * array of "pre-read" (leftover) bytes; this is necessary when preliminary
+ * stream/reader is trying to figure out underlying character encoding.
+ */
+public final class UTF8Reader
+    extends Reader
+{
+    private final static int DEFAULT_BUFFER_SIZE = 8000;
+    
+    /**
+     * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftRerefence}
+     * to a byte array used for holding content to decode
+     */
+    final protected static ThreadLocal<SoftReference<byte[][]>> _bufferRecycler
+        = new ThreadLocal<SoftReference<byte[][]>>();
+
+    protected final byte[][] _bufferHolder;
+    
+    private InputStream _inputSource;
+
+    private final boolean _autoClose;
+    
+    protected byte[] _inputBuffer;
+
+    /**
+     * Pointer to the next available byte (if any), iff less than
+     * <code>mByteBufferEnd</code>
+     */
+    protected int _inputPtr;
+
+    /**
+     * Pointed to the end marker, that is, position one after the last
+     * valid available byte.
+     */
+    protected int _inputEnd;
+
+    /**
+     * Decoded first character of a surrogate pair, if one needs to be buffered
+     */
+    protected int _surrogate = -1;
+    
+    /**
+     * Total read character count; used for error reporting purposes
+     */
+    int _charCount = 0;
+
+    /**
+     * Total read byte count; used for error reporting purposes
+     */
+    int _byteCount = 0;
+
+    /*
+    /**********************************************************************
+    /* Life-cycle
+    /**********************************************************************
+     */
+    
+    public UTF8Reader(InputStream in, boolean autoClose)
+    {
+        super(in);
+        _inputSource = in;
+        _bufferHolder = _findBufferHolder();
+        byte[] buffer = _bufferHolder[0];
+        if (buffer == null) {
+            _bufferHolder[0] = buffer = new byte[DEFAULT_BUFFER_SIZE];
+        }
+        _inputBuffer = buffer;
+        _inputPtr = 0;
+        _inputEnd = 0;
+        _autoClose = autoClose;
+    }
+
+    public UTF8Reader(byte[] buf, int ptr, int len, boolean autoClose)
+    {
+        super(bogusStream());
+        _inputSource = null;
+        _inputBuffer = buf;
+        _inputPtr = ptr;
+        _inputEnd = ptr+len;
+        _autoClose = autoClose; 
+        _bufferHolder = null;
+    }
+
+    private static InputStream bogusStream() {
+        return new InputStream() {
+            @Override
+            public int read() throws IOException {
+                return -1;
+            }
+        };
+    }
+    
+    private static byte[][] _findBufferHolder()
+    {
+        byte[][] bufs = null;
+        SoftReference<byte[][]> ref = _bufferRecycler.get();
+        if (ref != null) {
+            bufs = ref.get();
+        }
+        if (bufs == null) {
+            bufs = new byte[1][];
+            _bufferRecycler.set(new SoftReference<byte[][]>(bufs));
+        }
+        return bufs;
+    }
+
+
+    /*
+    /**********************************************************************
+    /* Reader API
+    /**********************************************************************
+     */
+
+    @Override
+    public void close()
+        throws IOException
+    {
+        InputStream in = _inputSource;
+
+        if (in != null) {
+            _inputSource = null;
+            if (_autoClose) {
+                in.close();
+            }
+        }
+        freeBuffers();
+    }
+
+    private char[] _tmpBuffer = null;
+
+    /**
+     * Although this method is implemented by the base class, AND it should
+     * never be called by Woodstox code, let's still implement it bit more
+     * efficiently just in case
+     */
+    @Override
+    public int read()
+        throws IOException
+    {
+        if (_tmpBuffer == null) {
+            _tmpBuffer = new char[1];
+        }
+        if (read(_tmpBuffer, 0, 1) < 1) {
+            return -1;
+        }
+        return _tmpBuffer[0];
+    }
+
+    @Override
+    public int read(char[] cbuf) throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+    
+    @Override
+    public int read(char[] cbuf, int start, int len)
+        throws IOException
+    {
+        // Already EOF?
+        if (_inputBuffer == null) {
+            return -1;
+        }
+        len += start;
+        int outPtr = start;
+
+        // Ok, first; do we have a surrogate from last round?
+        if (_surrogate >= 0) {
+            cbuf[outPtr++] = (char) _surrogate;
+            _surrogate = -1;
+            // No need to load more, already got one char
+        } else {
+            /* To prevent unnecessary blocking (esp. with network streams),
+             * we'll only require decoding of a single char
+             */
+            int left = (_inputEnd - _inputPtr);
+
+            /* So; only need to load more if we can't provide at least
+             * one more character. We need not do thorough check here,
+             * but let's check the common cases here: either completely
+             * empty buffer (left == 0), or one with less than max. byte
+             * count for a single char, and starting of a multi-byte
+             * encoding (this leaves possibility of a 2/3-byte char
+             * that is still fully accessible... but that can be checked
+             * by the load method)
+             */
+
+            if (left < 4) {
+                // Need to load more?
+                if (left < 1 || _inputBuffer[_inputPtr] < 0) {
+                    if (!loadMore(left)) { // (legal) EOF?
+                        return -1;
+                    }
+                }
+            }
+        }
+        final byte[] buf = _inputBuffer;
+        int inPtr = _inputPtr;
+        final int inBufLen = _inputEnd;
+
+        main_loop:
+        while (outPtr < len) {
+            // At this point we have at least one byte available
+            int c = (int) buf[inPtr++];
+
+            // Let's first do the quickie loop for common case; 7-bit ASCII
+            if (c >= 0) { // ASCII? can probably loop, then
+                cbuf[outPtr++] = (char) c; // ok since MSB is never on
+
+                // Ok, how many such chars could we safely process without overruns?
+                // (will combine 2 in-loop comparisons into just one)
+                int outMax = (len - outPtr); // max output
+                int inMax = (inBufLen - inPtr); // max input
+                int inEnd = inPtr + ((inMax < outMax) ? inMax : outMax);
+
+                ascii_loop:
+                while (true) {
+                    if (inPtr >= inEnd) {
+                        break main_loop;
+                    }
+                    c = buf[inPtr++];
+                    if (c < 0) { // or multi-byte
+                        break ascii_loop;
+                    }
+                    cbuf[outPtr++] = (char) c;
+                }
+            }
+
+            int needed;
+
+            // Ok; if we end here, we got multi-byte combination
+            if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+                c = (c & 0x1F);
+                needed = 1;
+            } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+                c = (c & 0x0F);
+                needed = 2;
+            } else if ((c & 0xF8) == 0xF0) {
+                // 4 bytes; double-char BS, with surrogates and all...
+                c = (c & 0x0F);
+                needed = 3;
+            } else {
+                reportInvalidInitial(c & 0xFF, outPtr-start);
+                // never gets here...
+                needed = 1;
+            }
+            /* Do we have enough bytes? If not, let's just push back the
+             * byte and leave, since we have already gotten at least one
+             * char decoded. This way we will only block (with read from
+             * input stream) when absolutely necessary.
+             */
+            if ((inBufLen - inPtr) < needed) {
+                --inPtr;
+                break main_loop;
+            }
+
+            int d = (int) buf[inPtr++];
+            if ((d & 0xC0) != 0x080) {
+                reportInvalidOther(d & 0xFF, outPtr-start);
+            }
+            c = (c << 6) | (d & 0x3F);
+
+            if (needed > 1) { // needed == 1 means 2 bytes total
+                d = buf[inPtr++]; // 3rd byte
+                if ((d & 0xC0) != 0x080) {
+                    reportInvalidOther(d & 0xFF, outPtr-start);
+                }
+                c = (c << 6) | (d & 0x3F);
+                if (needed > 2) { // 4 bytes? (need surrogates)
+                    d = buf[inPtr++];
+                    if ((d & 0xC0) != 0x080) {
+                        reportInvalidOther(d & 0xFF, outPtr-start);
+                    }
+                    c = (c << 6) | (d & 0x3F);
+                    /* Ugh. Need to mess with surrogates. Ok; let's inline them
+                     * there, then, if there's room: if only room for one,
+                     * need to save the surrogate for the rainy day...
+                     */
+                    c -= 0x10000; // to normalize it starting with 0x0
+                    cbuf[outPtr++] = (char) (0xD800 + (c >> 10));
+                    // hmmh. can this ever be 0? (not legal, at least?)
+                    c = (0xDC00 | (c & 0x03FF));
+
+                    // Room for second part?
+                    if (outPtr >= len) { // nope
+                        _surrogate = c;
+                        break main_loop;
+                    }
+                    // sure, let's fall back to normal processing:
+                }
+                // Otherwise, should we check that 3-byte chars are
+                // legal ones (should not expand to surrogates?
+                // For now, let's not...
+                /*
+                else {
+                    if (c >= 0xD800 && c < 0xE000) {
+                        reportInvalid(c, outPtr-start, "(a surrogate character) ");
+                    }
+                }
+                */
+            }
+            cbuf[outPtr++] = (char) c;
+            if (inPtr >= inBufLen) {
+                break main_loop;
+            }
+        }
+
+        _inputPtr = inPtr;
+        len = outPtr - start;
+        _charCount += len;
+        return len;
+    }
+    
+    /*
+    /**********************************************************************
+    /* Internal/package methods:
+    /**********************************************************************
+     */
+
+    protected final InputStream getStream() { return _inputSource; }
+
+    /**
+     * Method for reading as many bytes from the underlying stream as possible
+     * (that fit in the buffer), to the beginning of the buffer.
+     */
+    protected final int readBytes()
+        throws IOException
+    {
+        _inputPtr = 0;
+        _inputEnd = 0;
+        if (_inputSource != null) {
+            int count = _inputSource.read(_inputBuffer, 0, _inputBuffer.length);
+            if (count > 0) {
+                _inputEnd = count;
+            }
+            return count;
+        }
+        return -1;
+    }
+
+    /**
+     * Method for reading as many bytes from the underlying stream as possible
+     * (that fit in the buffer considering offset), to the specified offset.
+     *
+     * @return Number of bytes read, if any; -1 to indicate none available
+     *  (that is, end of input)
+     */
+    protected final int readBytesAt(int offset)
+        throws IOException
+    {
+        // shouldn't modify mBytePtr, assumed to be 'offset'
+        if (_inputSource != null) {
+            int count = _inputSource.read(_inputBuffer, offset, _inputBuffer.length - offset);
+            if (count > 0) {
+                _inputEnd += count;
+            }
+            return count;
+        }
+        return -1;
+    }
+
+    /**
+     * This method should be called along with (or instead of) normal
+     * close. After calling this method, no further reads should be tried.
+     * Method will try to recycle read buffers (if any).
+     */
+    public final void freeBuffers()
+    {
+        if (_bufferHolder != null) {
+            byte[] buf = _inputBuffer;
+            if (buf != null) {
+                _inputBuffer = null;
+                _bufferHolder[0] = buf;
+            }
+        }
+    }
+
+    /*
+    /**********************************************************************
+    /* Internal methods
+    /**********************************************************************
+     */
+
+    private void reportInvalidInitial(int mask, int offset)
+        throws IOException
+    {
+        // input (byte) ptr has been advanced by one, by now:
+        int bytePos = _byteCount + _inputPtr - 1;
+        int charPos = _charCount + offset + 1;
+
+        throw new CharConversionException("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask)
+                +" (at char #"+charPos+", byte #"+bytePos+")");
+    }
+
+    private void reportInvalidOther(int mask, int offset)
+        throws IOException
+    {
+        int bytePos = _byteCount + _inputPtr - 1;
+        int charPos = _charCount + offset;
+
+        throw new CharConversionException("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask)
+                +" (at char #"+charPos+", byte #"+bytePos+")");
+    }
+
+    private void reportUnexpectedEOF(int gotBytes, int needed)
+        throws IOException
+    {
+        int bytePos = _byteCount + gotBytes;
+        int charPos = _charCount;
+
+        throw new CharConversionException("Unexpected EOF in the middle of a multi-byte char: got "
+                +gotBytes+", needed "+needed +", at char #"+charPos+", byte #"+bytePos+")");
+    }
+
+    /*
+    private void reportInvalid(int value, int offset, String msg) throws IOException
+    { 
+        int bytePos = _byteCount + _inputPtr - 1;
+        int charPos = _charCount + offset;
+
+        throw new CharConversionException("Invalid UTF-8 character 0x"+Integer.toHexString(value)+msg
+                +" at char #"+charPos+", byte #"+bytePos+")");
+    }
+    */
+
+    /**
+     * @param available Number of "unused" bytes in the input buffer
+     *
+     * @return True, if enough bytes were read to allow decoding of at least
+     *   one full character; false if EOF was encountered instead.
+     */
+    private boolean loadMore(int available)
+        throws IOException
+    {
+        _byteCount += (_inputEnd - available);
+
+        // Bytes that need to be moved to the beginning of buffer?
+        if (available > 0) {
+            if (_inputPtr > 0) {
+                // sanity check: can only move if we "own" buffers
+                if (_bufferHolder == null) {
+                    throw new IllegalStateException("Internal error: need to move partially decoded character; buffer not modifiable");
+                }
+
+                for (int i = 0; i < available; ++i) {
+                    _inputBuffer[i] = _inputBuffer[_inputPtr+i];
+                }
+                _inputPtr = 0;
+		_inputEnd = available;
+            }
+        } else {
+            /* Ok; here we can actually reasonably expect an EOF,
+             * so let's do a separate read right away:
+             */
+            int count = readBytes();
+            if (count < 1) {
+                freeBuffers(); // to help GC?
+                if (count < 0) { // -1
+                    return false;
+                }
+                // 0 count is no good; let's err out
+                reportStrangeStream();
+            }
+        }
+
+        /* We now have at least one byte... and that allows us to
+         * calculate exactly how many bytes we need!
+         */
+        @SuppressWarnings("cast")
+        int c = (int) _inputBuffer[_inputPtr];
+        if (c >= 0) { // single byte (ascii) char... cool, can return
+            return true;
+        }
+
+        // Ok, a multi-byte char, let's check how many bytes we'll need:
+        int needed;
+        if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+            needed = 2;
+        } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+            needed = 3;
+        } else if ((c & 0xF8) == 0xF0) {
+            // 4 bytes; double-char BS, with surrogates and all...
+            needed = 4;
+        } else {
+            reportInvalidInitial(c & 0xFF, 0);
+            // never gets here... but compiler whines without this:
+            needed = 1;
+        }
+
+        /* And then we'll just need to load up to that many bytes;
+         * if an EOF is hit, that'll be an error. But we need not do
+         * actual decoding here, just load enough bytes.
+         */
+        while ((_inputPtr + needed) > _inputEnd) {
+            int count = readBytesAt(_inputEnd);
+            if (count < 1) {
+                if (count < 0) { // -1, EOF... no good!
+                    freeBuffers();
+                    reportUnexpectedEOF(_inputEnd, needed);
+                }
+                // 0 count is no good; let's err out
+                reportStrangeStream();
+            }
+        }
+        return true;
+    }
+
+    protected void reportBounds(char[] cbuf, int start, int len) throws IOException {
+        throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]");
+    }
+
+    protected void reportStrangeStream() throws IOException {
+        throw new IOException("Strange I/O stream, returned 0 bytes on read");
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/UTF8Writer.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/UTF8Writer.java
new file mode 100644
index 0000000..8193177
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/UTF8Writer.java
@@ -0,0 +1,412 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.lang.ref.SoftReference;
+
+public final class UTF8Writer
+    extends Writer
+{
+    final static int SURR1_FIRST = 0xD800;
+    final static int SURR1_LAST = 0xDBFF;
+    final static int SURR2_FIRST = 0xDC00;
+    final static int SURR2_LAST = 0xDFFF;
+
+    private final static int DEFAULT_BUFFER_SIZE = 8000;
+    
+    /**
+     * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftRerefence}
+     * to a byte array used for holding content to decode
+     */
+    final protected static ThreadLocal<SoftReference<byte[][]>> _bufferRecycler
+        = new ThreadLocal<SoftReference<byte[][]>>();
+
+    protected final byte[][] _bufferHolder;
+    
+    private OutputStream _out;
+
+    private byte[] _outBuffer;
+
+    private final int _outBufferEnd;
+
+    private int _outPtr;
+
+    /**
+     * When outputting chars from BMP, surrogate pairs need to be coalesced.
+     * To do this, both pairs must be known first; and since it is possible
+     * pairs may be split, we need temporary storage for the first half
+     */
+    int _surrogate = 0;
+
+    public UTF8Writer(OutputStream out)
+    {
+        _out = out;
+        _bufferHolder = _findBufferHolder();
+        byte[] buffer = _bufferHolder[0];
+        if (buffer == null) {
+            _bufferHolder[0] = buffer = new byte[DEFAULT_BUFFER_SIZE];
+        }
+        _outBuffer = buffer;
+        /* Max. expansion for a single char (in unmodified UTF-8) is
+         * 4 bytes (or 3 depending on how you view it -- 4 when recombining
+         * surrogate pairs)
+         */
+        _outBufferEnd = _outBuffer.length - 4;
+        _outPtr = 0;
+    }
+
+    private static byte[][] _findBufferHolder()
+    {
+        byte[][] bufs = null;
+        SoftReference<byte[][]> ref = _bufferRecycler.get();
+        if (ref != null) {
+            bufs = ref.get();
+        }
+        if (bufs == null) {
+            bufs = new byte[1][];
+            _bufferRecycler.set(new SoftReference<byte[][]>(bufs));
+        }
+        return bufs;
+    }
+    
+    @Override
+    public Writer append(char c)
+        throws IOException
+    {
+        write(c);
+        return this;
+    }
+
+    @Override
+    public void close()
+        throws IOException
+    {
+        if (_out != null) {
+            if (_outPtr > 0) {
+                _out.write(_outBuffer, 0, _outPtr);
+                _outPtr = 0;
+            }
+            OutputStream out = _out;
+            _out = null;
+
+            byte[] buf = _outBuffer;
+            if (buf != null) {
+                _outBuffer = null;
+                _bufferHolder[0] = buf;
+            }
+            out.close();
+
+            /* Let's 'flush' orphan surrogate, no matter what; but only
+             * after cleanly closing everything else.
+             */
+            int code = _surrogate;
+            _surrogate = 0;
+            if (code > 0) {
+                throwIllegal(code);
+            }
+        }
+    }
+
+    @Override
+    public void flush()
+        throws IOException
+    {
+        if (_out != null) {
+            if (_outPtr > 0) {
+                _out.write(_outBuffer, 0, _outPtr);
+                _outPtr = 0;
+            }
+            _out.flush();
+        }
+    }
+
+    @Override
+    public void write(char[] cbuf)
+        throws IOException
+    {
+        write(cbuf, 0, cbuf.length);
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len)
+        throws IOException
+    {
+        if (len < 2) {
+            if (len == 1) {
+                write(cbuf[off]);
+            }
+            return;
+        }
+
+        // First: do we have a leftover surrogate to deal with?
+        if (_surrogate > 0) {
+            char second = cbuf[off++];
+            --len;
+            write(convertSurrogate(second));
+            // will have at least one more char
+        }
+
+        int outPtr = _outPtr;
+        byte[] outBuf = _outBuffer;
+        int outBufLast = _outBufferEnd; // has 4 'spare' bytes
+
+        // All right; can just loop it nice and easy now:
+        len += off; // len will now be the end of input buffer
+
+        output_loop:
+        for (; off < len; ) {
+            /* First, let's ensure we can output at least 4 bytes
+             * (longest UTF-8 encoded codepoint):
+             */
+            if (outPtr >= outBufLast) {
+                _out.write(outBuf, 0, outPtr);
+                outPtr = 0;
+            }
+
+            int c = cbuf[off++];
+            // And then see if we have an Ascii char:
+            if (c < 0x80) { // If so, can do a tight inner loop:
+                outBuf[outPtr++] = (byte)c;
+                // Let's calc how many ascii chars we can copy at most:
+                int maxInCount = (len - off);
+                int maxOutCount = (outBufLast - outPtr);
+
+                if (maxInCount > maxOutCount) {
+                    maxInCount = maxOutCount;
+                }
+                maxInCount += off;
+                ascii_loop:
+                while (true) {
+                    if (off >= maxInCount) { // done with max. ascii seq
+                        continue output_loop;
+                    }
+                    c = cbuf[off++];
+                    if (c >= 0x80) {
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (byte) c;
+                }
+            }
+
+            // Nope, multi-byte:
+            if (c < 0x800) { // 2-byte
+                outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (c < SURR1_FIRST || c > SURR2_LAST) {
+                    outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+                    outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                    outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+                    continue;
+                }
+                // Yup, a surrogate:
+                if (c > SURR1_LAST) { // must be from first range
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                _surrogate = c;
+                // and if so, followed by another from next range
+                if (off >= len) { // unless we hit the end?
+                    break;
+                }
+                c = convertSurrogate(cbuf[off++]);
+                if (c > 0x10FFFF) { // illegal in JSON as well as in XML
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            }
+        }
+        _outPtr = outPtr;
+    }
+    
+    @Override
+    public void write(int c) throws IOException
+    {
+        // First; do we have a left over surrogate?
+        if (_surrogate > 0) {
+            c = convertSurrogate(c);
+            // If not, do we start with a surrogate?
+        } else if (c >= SURR1_FIRST && c <= SURR2_LAST) {
+            // Illegal to get second part without first:
+            if (c > SURR1_LAST) {
+                throwIllegal(c);
+            }
+            // First part just needs to be held for now
+            _surrogate = c;
+            return;
+        }
+
+        if (_outPtr >= _outBufferEnd) { // let's require enough room, first
+            _out.write(_outBuffer, 0, _outPtr);
+            _outPtr = 0;
+        }
+
+        if (c < 0x80) { // ascii
+            _outBuffer[_outPtr++] = (byte) c;
+        } else {
+            int ptr = _outPtr;
+            if (c < 0x800) { // 2-byte
+                _outBuffer[ptr++] = (byte) (0xc0 | (c >> 6));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            } else if (c <= 0xFFFF) { // 3 bytes
+                _outBuffer[ptr++] = (byte) (0xe0 | (c >> 12));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 4 bytes
+                if (c > 0x10FFFF) { // illegal
+                    throwIllegal(c);
+                }
+                _outBuffer[ptr++] = (byte) (0xf0 | (c >> 18));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            }
+            _outPtr = ptr;
+        }
+    }
+
+    @Override
+    public void write(String str) throws IOException
+    {
+        write(str, 0, str.length());
+    }
+
+    @Override
+    public void write(String str, int off, int len)  throws IOException
+    {
+        if (len < 2) {
+            if (len == 1) {
+                write(str.charAt(off));
+            }
+            return;
+        }
+
+        // First: do we have a leftover surrogate to deal with?
+        if (_surrogate > 0) {
+            char second = str.charAt(off++);
+            --len;
+            write(convertSurrogate(second));
+            // will have at least one more char (case of 1 char was checked earlier on)
+        }
+
+        int outPtr = _outPtr;
+        byte[] outBuf = _outBuffer;
+        int outBufLast = _outBufferEnd; // has 4 'spare' bytes
+
+        // All right; can just loop it nice and easy now:
+        len += off; // len will now be the end of input buffer
+
+        output_loop:
+        for (; off < len; ) {
+            /* First, let's ensure we can output at least 4 bytes
+             * (longest UTF-8 encoded codepoint):
+             */
+            if (outPtr >= outBufLast) {
+                _out.write(outBuf, 0, outPtr);
+                outPtr = 0;
+            }
+
+            int c = str.charAt(off++);
+            // And then see if we have an Ascii char:
+            if (c < 0x80) { // If so, can do a tight inner loop:
+                outBuf[outPtr++] = (byte)c;
+                // Let's calc how many ascii chars we can copy at most:
+                int maxInCount = (len - off);
+                int maxOutCount = (outBufLast - outPtr);
+
+                if (maxInCount > maxOutCount) {
+                    maxInCount = maxOutCount;
+                }
+                maxInCount += off;
+                ascii_loop:
+                while (true) {
+                    if (off >= maxInCount) { // done with max. ascii seq
+                        continue output_loop;
+                    }
+                    c = str.charAt(off++);
+                    if (c >= 0x80) {
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (byte) c;
+                }
+            }
+
+            // Nope, multi-byte:
+            if (c < 0x800) { // 2-byte
+                outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (c < SURR1_FIRST || c > SURR2_LAST) {
+                    outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+                    outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                    outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+                    continue;
+                }
+                // Yup, a surrogate:
+                if (c > SURR1_LAST) { // must be from first range
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                _surrogate = c;
+                // and if so, followed by another from next range
+                if (off >= len) { // unless we hit the end?
+                    break;
+                }
+                c = convertSurrogate(str.charAt(off++));
+                if (c > 0x10FFFF) { // illegal, as per RFC 4627
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            }
+        }
+        _outPtr = outPtr;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to calculate UTF codepoint, from a surrogate pair.
+     */
+    private int convertSurrogate(int secondPart)
+        throws IOException
+    {
+        int firstPart = _surrogate;
+        _surrogate = 0;
+
+        // Ok, then, is the second part valid?
+        if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
+            throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
+        }
+        return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
+    }
+
+    private void throwIllegal(int code)
+        throws IOException
+    {
+        if (code > 0x10FFFF) { // over max?
+            throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627");
+        }
+        if (code >= SURR1_FIRST) {
+            if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
+                throw new IOException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
+            }
+            throw new IOException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
+        }
+
+        // should we ever get this?
+        throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java
new file mode 100644
index 0000000..73d2d25
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java
@@ -0,0 +1,681 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.net.URL;
+import java.nio.charset.Charset;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.format.InputAccessor;
+import com.fasterxml.jackson.core.format.MatchStrength;
+import com.fasterxml.jackson.core.io.IOContext;
+
+public class YAMLFactory extends JsonFactory
+{
+	private static final long serialVersionUID = 1171663157274350349L;
+
+	/**
+     * Name used to identify YAML format.
+     * (and returned by {@link #getFormatName()}
+     */
+    public final static String FORMAT_NAME_YAML = "YAML";
+
+    /**
+     * Bitfield (set of flags) of all parser features that are enabled
+     * by default.
+     */
+    protected final static int DEFAULT_YAML_PARSER_FEATURE_FLAGS = YAMLParser.Feature.collectDefaults();
+
+    /**
+     * Bitfield (set of flags) of all generator features that are enabled
+     * by default.
+     */    
+    protected final static int DEFAULT_YAML_GENERATOR_FEATURE_FLAGS = YAMLGenerator.Feature.collectDefaults();
+
+    private final static byte UTF8_BOM_1 = (byte) 0xEF;
+    private final static byte UTF8_BOM_2 = (byte) 0xBB;
+    private final static byte UTF8_BOM_3 = (byte) 0xBF;
+
+    /*
+    /**********************************************************************
+    /* Configuration
+    /**********************************************************************
+     */
+
+    protected int _yamlParserFeatures = DEFAULT_YAML_PARSER_FEATURE_FLAGS;
+
+    protected int _yamlGeneratorFeatures = DEFAULT_YAML_GENERATOR_FEATURE_FLAGS;
+    
+    /*
+    /**********************************************************************
+    /* Factory construction, configuration
+    /**********************************************************************
+     */
+
+    protected transient DumperOptions _outputOptions;
+
+    protected Integer[] _version;
+    
+    /**
+     * Default constructor used to create factory instances.
+     * Creation of a factory instance is a light-weight operation,
+     * but it is still a good idea to reuse limited number of
+     * factory instances (and quite often just a single instance):
+     * factories are used as context for storing some reused
+     * processing objects (such as symbol tables parsers use)
+     * and this reuse only works within context of a single
+     * factory instance.
+     */
+    public YAMLFactory() { this(null); }
+
+    public YAMLFactory(ObjectCodec oc)
+    {
+        super(oc);
+        _yamlParserFeatures = DEFAULT_YAML_PARSER_FEATURE_FLAGS;
+        _yamlGeneratorFeatures = DEFAULT_YAML_GENERATOR_FEATURE_FLAGS;
+        _outputOptions = _defaultOptions();
+        DumperOptions.Version version = _outputOptions.getVersion();
+        _version = (version == null) ? null : version.getArray();
+    }
+
+    /**
+     * @since 2.2.1
+     */
+    public YAMLFactory(YAMLFactory src, ObjectCodec oc)
+    {
+        super(src, oc);
+        _outputOptions = src._outputOptions;
+        _version = src._version;
+        _yamlParserFeatures = src._yamlParserFeatures;
+        _yamlGeneratorFeatures = src._yamlGeneratorFeatures;
+    }
+    
+    private static DumperOptions _defaultOptions()
+    {
+        DumperOptions opt = new DumperOptions();
+        // would we want canonical?
+        opt.setCanonical(false);
+        // if not, MUST specify flow styles
+        opt.setDefaultFlowStyle(FlowStyle.BLOCK);
+        return opt;
+        
+    }
+
+    @Override
+    public YAMLFactory copy()
+    {
+        _checkInvalidCopy(YAMLFactory.class);
+        return new YAMLFactory(this, null);
+    }
+
+    /*
+    /**********************************************************
+    /* Serializable overrides
+    /**********************************************************
+     */
+
+    /**
+     * Method that we need to override to actually make restoration go
+     * through constructors etc.
+     * Also: must be overridden by sub-classes as well.
+     */
+    @Override
+    protected Object readResolve() {
+        // this is transient, need to explicitly recreate:
+        _outputOptions = _defaultOptions();
+        return new YAMLFactory(this, _objectCodec);
+    }
+
+    /*                                                                                       
+    /**********************************************************                              
+    /* Versioned                                                                             
+    /**********************************************************                              
+     */
+
+    @Override
+    public Version version() {
+        return PackageVersion.VERSION;
+    }
+    
+    /*
+    /**********************************************************
+    /* Format detection functionality (since 1.8)
+    /**********************************************************
+     */
+    
+    @Override
+    public String getFormatName() {
+        return FORMAT_NAME_YAML;
+    }
+    
+    /**
+     * Sub-classes need to override this method (as of 1.8)
+     */
+    @Override
+    public MatchStrength hasFormat(InputAccessor acc) throws IOException
+    {
+        /* Actually quite possible to do, thanks to (optional) "---"
+         * indicator we may be getting...
+         */
+        if (!acc.hasMoreBytes()) {
+            return MatchStrength.INCONCLUSIVE;
+        }
+        byte b = acc.nextByte();
+        // Very first thing, a UTF-8 BOM?
+        if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (acc.nextByte() != UTF8_BOM_2) {
+                return MatchStrength.NO_MATCH;
+            }
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (acc.nextByte() != UTF8_BOM_3) {
+                return MatchStrength.NO_MATCH;
+            }
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            b = acc.nextByte();
+        }
+        // as far as I know, leading space is NOT allowed before "---" marker?
+        if (b == '-' && (acc.hasMoreBytes() && acc.nextByte() == '-')
+                && (acc.hasMoreBytes() && acc.nextByte() == '-')) {
+            return MatchStrength.FULL_MATCH;
+        }
+        return MatchStrength.INCONCLUSIVE;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, parser settings
+    /**********************************************************
+     */
+
+    /**
+     * Method for enabling or disabling specified parser feature
+     * (check {@link YAMLParser.Feature} for list of features)
+     */
+    public final YAMLFactory configure(YAMLParser.Feature f, boolean state)
+    {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /**
+     * Method for enabling specified parser feature
+     * (check {@link YAMLParser.Feature} for list of features)
+     */
+    public YAMLFactory enable(YAMLParser.Feature f) {
+        _yamlParserFeatures |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified parser features
+     * (check {@link YAMLParser.Feature} for list of features)
+     */
+    public YAMLFactory disable(YAMLParser.Feature f) {
+        _yamlParserFeatures &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Checked whether specified parser feature is enabled.
+     */
+    public final boolean isEnabled(YAMLParser.Feature f) {
+        return (_yamlParserFeatures & f.getMask()) != 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration, generator settings
+    /**********************************************************
+     */
+
+    /**
+     * Method for enabling or disabling specified generator feature
+     * (check {@link YAMLGenerator.Feature} for list of features)
+     */
+    public final YAMLFactory configure(YAMLGenerator.Feature f, boolean state) {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+
+    /**
+     * Method for enabling specified generator features
+     * (check {@link YAMLGenerator.Feature} for list of features)
+     */
+    public YAMLFactory enable(YAMLGenerator.Feature f) {
+        _yamlGeneratorFeatures |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified generator feature
+     * (check {@link YAMLGenerator.Feature} for list of features)
+     */
+    public YAMLFactory disable(YAMLGenerator.Feature f) {
+        _yamlGeneratorFeatures &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Check whether specified generator feature is enabled.
+     */
+    public final boolean isEnabled(YAMLGenerator.Feature f) {
+        return (_yamlGeneratorFeatures & f.getMask()) != 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden parser factory methods (for 2.1)
+    /**********************************************************
+     */
+
+    @Override
+    public YAMLParser createParser(String content)
+        throws IOException, JsonParseException
+    {
+        Reader r = new StringReader(content);
+        IOContext ctxt = _createContext(r, true); // true->own, can close
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+        return _createParser(r, ctxt);
+    }
+
+    @SuppressWarnings("resource")
+    @Override
+    public YAMLParser createParser(File f)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(f, true);
+        InputStream in = new FileInputStream(f);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+    
+    @Override
+    public YAMLParser createParser(URL url)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(url, true);
+        InputStream in = _optimizedStreamFromURL(url);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+
+    @Override
+    public YAMLParser createParser(InputStream in)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(in, false);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+
+    @Override
+    public JsonParser createParser(Reader r)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(r, false);
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+        return _createParser(r, ctxt);
+    }
+
+    @Override
+    public YAMLParser createParser(byte[] data)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length);
+            if (in != null) {
+                return _createParser(in, ctxt);
+            }
+        }
+        return _createParser(data, 0, data.length, ctxt);
+    }
+    
+    @Override
+    public YAMLParser createParser(byte[] data, int offset, int len)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, offset, len);
+            if (in != null) {
+                return _createParser(in, ctxt);
+            }
+        }
+        return _createParser(data, offset, len, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden parser factory methods (2.0 and prior)
+    /**********************************************************
+     */
+
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(String content)
+        throws IOException, JsonParseException
+    {
+        Reader r = new StringReader(content);
+        IOContext ctxt = _createContext(r, true); // true->own, can close
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+        return _createParser(r, ctxt);
+    }
+
+    @SuppressWarnings("resource")
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(File f)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(f, true);
+        InputStream in = new FileInputStream(f);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+    
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(URL url)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(url, true);
+        InputStream in = _optimizedStreamFromURL(url);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(InputStream in)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(in, false);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createParser(in, ctxt);
+    }
+
+    @Deprecated
+    @Override
+    public JsonParser createJsonParser(Reader r)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(r, false);
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+        return _createParser(r, ctxt);
+    }
+
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(byte[] data)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length);
+            if (in != null) {
+                return _createParser(in, ctxt);
+            }
+        }
+        return _createParser(data, 0, data.length, ctxt);
+    }
+    
+    @Deprecated
+    @Override
+    public YAMLParser createJsonParser(byte[] data, int offset, int len)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, offset, len);
+            if (in != null) {
+                return _createParser(in, ctxt);
+            }
+        }
+        return _createParser(data, offset, len, ctxt);
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden generator factory methods (2.1)
+    /**********************************************************
+     */
+
+    @Override
+    public YAMLGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException
+    {
+        // false -> we won't manage the stream unless explicitly directed to
+        IOContext ctxt = _createContext(out, false);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            out = _outputDecorator.decorate(ctxt, out);
+        }
+        return _createGenerator(_createWriter(out, JsonEncoding.UTF8, ctxt), ctxt);
+    }
+
+    @Override
+    public YAMLGenerator createGenerator(OutputStream out) throws IOException
+    {
+        // false -> we won't manage the stream unless explicitly directed to
+        IOContext ctxt = _createContext(out, false);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            out = _outputDecorator.decorate(ctxt, out);
+        }
+        return _createGenerator(_createWriter(out, JsonEncoding.UTF8, ctxt), ctxt);
+    }
+    
+    @Override
+    public YAMLGenerator createGenerator(Writer out) throws IOException
+    {
+        IOContext ctxt = _createContext(out, false);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            out = _outputDecorator.decorate(ctxt, out);
+        }
+        return _createGenerator(out, ctxt);
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden generator factory methods (2.0 and before)
+    /**********************************************************
+     */
+
+    @Deprecated
+    @Override
+    public YAMLGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException {
+        return createGenerator(out, enc);
+    }
+
+    @Deprecated
+    @Override
+    public YAMLGenerator createJsonGenerator(OutputStream out) throws IOException {
+        return createGenerator(out);
+    }
+
+    @Deprecated
+    @Override
+    public YAMLGenerator createJsonGenerator(Writer out) throws IOException {
+        return createGenerator(out);
+    }
+    
+    /*
+    /******************************************************
+    /* Overridden internal factory methods
+    /******************************************************
+     */
+
+    //protected IOContext _createContext(Object srcRef, boolean resourceManaged)
+
+    @Override
+    protected YAMLParser _createParser(InputStream in, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        Reader r = _createReader(in, null, ctxt);
+        return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
+                _objectCodec, r);
+    }
+
+    @Override
+    protected YAMLParser _createParser(Reader r, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
+                _objectCodec, r);
+    }
+
+    @Override
+    protected YAMLParser _createParser(byte[] data, int offset, int len, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        Reader r = _createReader(data, offset, len, null, ctxt);
+        return new YAMLParser(ctxt, _getBufferRecycler(), _parserFeatures, _yamlParserFeatures,
+                _objectCodec, r);
+    }
+
+    @Override
+    protected YAMLParser _createJsonParser(InputStream in, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return _createParser(in, ctxt);
+    }
+
+    @Override
+    protected JsonParser _createJsonParser(Reader r, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return _createParser(r, ctxt);
+    }
+
+    @Override
+    protected YAMLParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return _createParser(data, offset, len, ctxt);
+    }
+
+    @Override
+    protected YAMLGenerator _createGenerator(Writer out, IOContext ctxt)
+        throws IOException
+    {
+        int feats = _yamlGeneratorFeatures;
+        YAMLGenerator gen = new YAMLGenerator(ctxt, _generatorFeatures, feats,
+                _objectCodec, out, _outputOptions, _version);
+        // any other initializations? No?
+        return gen;
+    }
+
+    @Override
+    protected YAMLGenerator _createJsonGenerator(Writer out, IOContext ctxt)
+        throws IOException
+    {
+        return _createGenerator(out, ctxt);
+    }
+
+    @Override
+    protected YAMLGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
+    	return _createGenerator(new UTF8Writer(out), ctxt);
+    }
+
+    @Override
+    @Deprecated
+    protected YAMLGenerator _createUTF8JsonGenerator(OutputStream out, IOContext ctxt) throws IOException {
+        return _createUTF8Generator(out, ctxt);
+    }
+    
+    @Override
+    protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException
+    {
+        if (enc == JsonEncoding.UTF8) {
+            return new UTF8Writer(out);
+        }
+        return new OutputStreamWriter(out, enc.getJavaName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    protected final Charset UTF8 = Charset.forName("UTF-8");
+
+    protected Reader _createReader(InputStream in, JsonEncoding enc, IOContext ctxt) throws IOException
+    {
+        if (enc == null) {
+            enc = JsonEncoding.UTF8;
+        }
+        // default to UTF-8 if encoding missing
+        if (enc == JsonEncoding.UTF8) {
+            boolean autoClose = ctxt.isResourceManaged() || this.isEnabled(JsonParser.Feature.AUTO_CLOSE_SOURCE);
+            return new UTF8Reader(in, autoClose);
+//          return new InputStreamReader(in, UTF8);
+        }
+        return new InputStreamReader(in, enc.getJavaName());
+    }
+
+    protected Reader _createReader(byte[] data, int offset, int len,
+            JsonEncoding enc, IOContext ctxt) throws IOException
+    {
+        if (enc == null) {
+            enc = JsonEncoding.UTF8;
+        }
+        // default to UTF-8 if encoding missing
+        if (enc == null || enc == JsonEncoding.UTF8) {
+            return new UTF8Reader(data, offset, len, true);
+        }
+        ByteArrayInputStream in = new ByteArrayInputStream(data, offset, len);
+        return new InputStreamReader(in, enc.getJavaName());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java
new file mode 100644
index 0000000..5687331
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java
@@ -0,0 +1,545 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.events.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.GeneratorBase;
+import com.fasterxml.jackson.core.json.JsonWriteContext;
+import com.fasterxml.jackson.core.io.IOContext;
+
+public class YAMLGenerator extends GeneratorBase
+{
+    /**
+     * Enumeration that defines all togglable features for YAML generators
+     */
+    public enum Feature {
+        BOGUS(false) // placeholder
+        ;
+
+        protected final boolean _defaultState;
+        protected final int _mask;
+        
+        /**
+         * Method that calculates bit set (flags) of all features that
+         * are enabled by default.
+         */
+        public static int collectDefaults()
+        {
+            int flags = 0;
+            for (Feature f : values()) {
+                if (f.enabledByDefault()) {
+                    flags |= f.getMask();
+                }
+            }
+            return flags;
+        }
+        
+        private Feature(boolean defaultState) {
+            _defaultState = defaultState;
+            _mask = (1 << ordinal());
+        }
+        
+        public boolean enabledByDefault() { return _defaultState; }
+        public int getMask() { return _mask; }
+    };
+
+    protected final static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
+    protected final static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    final protected IOContext _ioContext;
+
+    /**
+     * Bit flag composed of bits that indicate which
+     * {@link YAMLGenerator.Feature}s
+     * are enabled.
+     */
+    protected int _yamlFeatures;
+
+    protected Writer _writer;
+
+    protected DumperOptions _outputOptions;
+
+    // for field names, leave out quotes
+    private final static Character STYLE_NAME = null;
+    
+    // numbers, booleans, should use implicit
+    private final static Character STYLE_SCALAR = null;
+    // Strings quoted for fun
+    private final static Character STYLE_STRING = Character.valueOf('"');
+        
+    // Which flow style to use for Base64? Maybe basic quoted?
+    private final static Character STYLE_BASE64 = Character.valueOf('"');
+    
+    /*
+    /**********************************************************
+    /* Output state
+    /**********************************************************
+     */
+
+    protected Emitter _emitter;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+    
+    public YAMLGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures,
+            ObjectCodec codec, Writer out,
+            DumperOptions outputOptions, Integer[] version
+            ) throws IOException
+    {
+        super(jsonFeatures, codec);
+        _ioContext = ctxt;
+        _yamlFeatures = yamlFeatures;
+        _writer = out;
+        _emitter = new Emitter(_writer, outputOptions);
+        _outputOptions = outputOptions;
+        // should we start output now, or try to defer?
+        _emitter.emit(new StreamStartEvent(null, null));
+        _emitter.emit(new DocumentStartEvent(null, null, /*explicit start*/ false,
+                version, /*tags*/ Collections.<String,String>emptyMap()));
+    }
+
+    /*                                                                                       
+    /**********************************************************                              
+    /* Versioned                                                                             
+    /**********************************************************                              
+     */
+
+    @Override
+    public Version version() {
+        return PackageVersion.VERSION;
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden methods, configuration
+    /**********************************************************
+     */
+
+    /**
+     * Not sure what to do here; could reset indentation to some value maybe?
+     */
+    @Override
+    public YAMLGenerator useDefaultPrettyPrinter()
+    {
+        return this;
+    }
+
+    /**
+     * Not sure what to do here; will always indent, but uses
+     * YAML-specific settings etc.
+     */
+    @Override
+    public YAMLGenerator setPrettyPrinter(PrettyPrinter pp) {
+        return this;
+    }
+
+    @Override
+    public Object getOutputTarget() {
+        return _writer;
+    }
+
+    @Override
+    public boolean canUseSchema(FormatSchema schema) {
+        return false;
+    }
+    
+    //@Override public void setSchema(FormatSchema schema)
+
+    /*
+    /**********************************************************************
+    /* Overridden methods; writing field names
+    /**********************************************************************
+     */
+    
+    /* And then methods overridden to make final, streamline some
+     * aspects...
+     */
+
+    @Override
+    public final void writeFieldName(String name) throws IOException, JsonGenerationException
+    {
+        if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(name);
+    }
+
+    @Override
+    public final void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        // Object is a value, need to verify it's allowed
+        if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(name.getValue());
+    }
+
+    @Override
+    public final void writeStringField(String fieldName, String value)
+        throws IOException, JsonGenerationException
+    {
+        if (_writeContext.writeFieldName(fieldName) == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(fieldName);
+        writeString(value);
+    }
+
+    private final void _writeFieldName(String name)
+        throws IOException, JsonGenerationException
+    {
+        _writeScalar(name, "string", STYLE_NAME);
+    }
+    
+    /*
+    /**********************************************************
+    /* Extended API, configuration
+    /**********************************************************
+     */
+
+    public YAMLGenerator enable(Feature f) {
+        _yamlFeatures |= f.getMask();
+        return this;
+    }
+
+    public YAMLGenerator disable(Feature f) {
+        _yamlFeatures &= ~f.getMask();
+        return this;
+    }
+
+    public final boolean isEnabled(Feature f) {
+        return (_yamlFeatures & f.getMask()) != 0;
+    }
+
+    public YAMLGenerator configure(Feature f, boolean state) {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API: low-level I/O
+    /**********************************************************
+     */
+
+    @Override
+    public final void flush() throws IOException
+    {
+        _writer.flush();
+    }
+    
+    @Override
+    public void close() throws IOException
+    {
+        _emitter.emit(new DocumentEndEvent(null, null, false));
+        _emitter.emit(new StreamEndEvent(null, null));
+        super.close();
+        _writer.close();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API: structural output
+    /**********************************************************
+     */
+    
+    @Override
+    public final void writeStartArray() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an array");
+        _writeContext = _writeContext.createChildArrayContext();
+        Boolean style = _outputOptions.getDefaultFlowStyle().getStyleBoolean();
+        // note: can NOT be implicit, to avoid having to specify tag
+        _emitter.emit(new SequenceStartEvent(/*anchor*/null, /*tag*/null,
+                /*implicit*/ true,  null, null, style));
+    }
+    
+    @Override
+    public final void writeEndArray() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inArray()) {
+            _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
+        }
+        _writeContext = _writeContext.getParent();
+        _emitter.emit(new SequenceEndEvent(null, null));
+    }
+
+    @Override
+    public final void writeStartObject() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an object");
+        _writeContext = _writeContext.createChildObjectContext();
+        Boolean style = _outputOptions.getDefaultFlowStyle().getStyleBoolean();
+        // note: can NOT be implicit, to avoid having to specify tag
+        _emitter.emit(new MappingStartEvent(/* anchor */null, null, //TAG_OBJECT,
+                /*implicit*/true, null, null, style));
+    }
+
+    @Override
+    public final void writeEndObject() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inObject()) {
+            _reportError("Current context not an object but "+_writeContext.getTypeDesc());
+        }
+        _writeContext = _writeContext.getParent();
+        _emitter.emit(new MappingEndEvent(null, null));
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, textual
+    /**********************************************************
+     */
+
+    @Override
+    public void writeString(String text) throws IOException,JsonGenerationException
+    {
+        if (text == null) {
+            writeNull();
+            return;
+        }
+        _verifyValueWrite("write String value");
+        _writeScalar(text, "string", STYLE_STRING);
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException
+    {
+        writeString(new String(text, offset, len));
+    }
+
+    @Override
+    public final void writeString(SerializableString sstr)
+        throws IOException, JsonGenerationException
+    {
+        writeString(sstr.toString());
+    }
+
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public final void writeUTF8String(byte[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        writeString(new String(text, offset, len, "UTF-8"));
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, unprocessed ("raw")
+    /**********************************************************
+     */
+
+    @Override
+    public void writeRaw(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(char c) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, base64-encoded binary
+    /**********************************************************
+     */
+    
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException, JsonGenerationException
+    {
+        if (data == null) {
+            writeNull();
+            return;
+        }
+        _verifyValueWrite("write Binary value");
+        // ok, better just Base64 encode as a String...
+        if (offset > 0 || (offset+len) != data.length) {
+            data = Arrays.copyOfRange(data, offset, offset+len);
+        }
+        String encoded = b64variant.encode(data);
+        _writeScalar(encoded, "byte[]", STYLE_BASE64);
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, primitive
+    /**********************************************************
+     */
+
+    @Override
+    public void writeBoolean(boolean state) throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write boolean value");
+        _writeScalar(state ? "true" : "false", "bool", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNull() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write null value");
+        // no real type for this, is there?
+        _writeScalar("null", "object", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNumber(int i) throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        _writeScalar(String.valueOf(i), "int", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNumber(long l) throws IOException, JsonGenerationException
+    {
+        // First: maybe 32 bits is enough?
+        if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) {
+            writeNumber((int) l);
+            return;
+        }
+        _verifyValueWrite("write number");
+        _writeScalar(String.valueOf(l), "long", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNumber(BigInteger v) throws IOException, JsonGenerationException
+    {
+        if (v == null) {
+            writeNull();
+            return;
+        }
+        _verifyValueWrite("write number");
+        _writeScalar(String.valueOf(v.toString()), "java.math.BigInteger", STYLE_SCALAR);
+    }
+    
+    @Override
+    public void writeNumber(double d) throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        _writeScalar(String.valueOf(d), "double", STYLE_SCALAR);
+    }    
+
+    @Override
+    public void writeNumber(float f) throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        _writeScalar(String.valueOf(f), "float", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNumber(BigDecimal dec) throws IOException, JsonGenerationException
+    {
+        if (dec == null) {
+            writeNull();
+            return;
+        }
+        _verifyValueWrite("write number");
+        _writeScalar(dec.toString(), "java.math.BigDecimal", STYLE_SCALAR);
+    }
+
+    @Override
+    public void writeNumber(String encodedValue) throws IOException,JsonGenerationException, UnsupportedOperationException
+    {
+        if (encodedValue == null) {
+            writeNull();
+            return;
+        }
+        _verifyValueWrite("write number");
+        _writeScalar(encodedValue, "number", STYLE_SCALAR);
+    }
+
+    /*
+    /**********************************************************
+    /* Implementations for methods from base class
+    /**********************************************************
+     */
+    
+    @Override
+    protected final void _verifyValueWrite(String typeMsg)
+        throws IOException, JsonGenerationException
+    {
+        int status = _writeContext.writeValue();
+        if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
+            _reportError("Can not "+typeMsg+", expecting field name");
+        }
+    }
+
+    @Override
+    protected void _releaseBuffers() {
+        // nothing special to do...
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    // Implicit means that (type) tags won't be shown, right?
+    private final static ImplicitTuple DEFAULT_IMPLICIT = new ImplicitTuple(true, true);
+
+    protected void _writeScalar(String value, String type, Character style) throws IOException
+    {
+        _emitter.emit(_scalarEvent(value, type, style));
+    }
+    
+    protected ScalarEvent _scalarEvent(String value, String tag, Character style)
+    {
+        // 'type' can be used as 'tag'... but should we?
+        return new ScalarEvent(null, null, DEFAULT_IMPLICIT, value,
+                null, null, style);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java
new file mode 100644
index 0000000..756cdea
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLParser.java
@@ -0,0 +1,743 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.events.*;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserBase;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.util.BufferRecycler;
+import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+
+/**
+ * {@link JsonParser} implementation used to expose YAML documents
+ * in form that allows other Jackson functionality to deal
+ * with it.
+ */
+public class YAMLParser
+    extends ParserBase
+{
+    /**
+     * Enumeration that defines all togglable features for YAML parsers.
+     */
+    public enum Feature {
+        
+        BOGUS(false)
+        ;
+
+        final boolean _defaultState;
+        final int _mask;
+        
+        /**
+         * Method that calculates bit set (flags) of all features that
+         * are enabled by default.
+         */
+        public static int collectDefaults()
+        {
+            int flags = 0;
+            for (Feature f : values()) {
+                if (f.enabledByDefault()) {
+                    flags |= f.getMask();
+                }
+            }
+            return flags;
+        }
+        
+        private Feature(boolean defaultState) {
+            _defaultState = defaultState;
+            _mask = (1 << ordinal());
+        }
+        
+        public boolean enabledByDefault() { return _defaultState; }
+        public int getMask() { return _mask; }
+    }
+
+    // note: does NOT include '0', handled separately
+//    private final static Pattern PATTERN_INT = Pattern.compile("-?[1-9][0-9]*");
+
+    /**
+     * We will use pattern that is bit stricter than YAML definition,
+     * but we will still allow things like extra '_' in there.
+     */
+    private final static Pattern PATTERN_FLOAT = Pattern.compile(
+            "[-+]?([0-9][0-9_]*)?\\.[0-9]*([eE][-+][0-9]+)?");
+    
+    /*
+    /**********************************************************************
+    /* Configuration
+    /**********************************************************************
+     */
+    
+    /**
+     * Codec used for data binding when (if) requested.
+     */
+    protected ObjectCodec _objectCodec;
+
+    protected int _yamlFeatures;
+
+    /*
+    /**********************************************************************
+    /* Input sources
+    /**********************************************************************
+     */
+
+    /**
+     * Need to keep track of underlying {@link Reader} to be able to
+     * auto-close it (if required to)
+     */
+    protected Reader _reader;
+    
+    protected ParserImpl _yamlParser;
+    /*
+    /**********************************************************************
+    /* State
+    /**********************************************************************
+     */
+
+    /**
+     * Keep track of the last event read, to get access to Location info
+     */
+    protected Event _lastEvent;
+
+    /**
+     * We need to keep track of text values.
+     */
+    protected String _textValue;
+
+    /**
+     * Let's also have a local copy of the current field name
+     */
+    protected String _currentFieldName;
+
+    /**
+     * Flag that is set when current token was derived from an Alias
+     * (reference to another value's anchor)
+     * 
+     * @since 2.1
+     */
+    protected boolean _currentIsAlias;
+
+    /**
+     * Anchor for the value that parser currently points to: in case of
+     * structured types, value whose first token current token is.
+     */
+    protected String _currentAnchor;
+    
+    /*
+    /**********************************************************************
+    /* Life-cycle
+    /**********************************************************************
+     */
+    
+    public YAMLParser(IOContext ctxt, BufferRecycler br,
+            int parserFeatures, int csvFeatures,
+            ObjectCodec codec, Reader reader)
+    {
+        super(ctxt, parserFeatures);    
+        _objectCodec = codec;
+        _yamlFeatures = csvFeatures;
+        _reader = reader;
+        _yamlParser = new ParserImpl(new StreamReader(reader));
+    }
+
+
+    @Override
+    public ObjectCodec getCodec() {
+        return _objectCodec;
+    }
+
+    @Override
+    public void setCodec(ObjectCodec c) {
+        _objectCodec = c;
+    }
+
+    /*                                                                                       
+    /**********************************************************                              
+    /* Extended YAML-specific API
+    /**********************************************************                              
+     */
+
+    /**
+     * Method that can be used to check whether current token was
+     * created from YAML Alias token (reference to an anchor).
+     * 
+     * @since 2.1
+     */
+    public boolean isCurrentAlias() {
+        return _currentIsAlias;
+    }
+
+    /**
+     * Method that can be used to check if the current token has an
+     * associated anchor (id to reference via Alias)
+     * 
+     * @since 2.1
+     */
+    public String getCurrentAnchor() {
+        return _currentAnchor;
+    }
+    
+    /*                                                                                       
+    /**********************************************************                              
+    /* Versioned                                                                             
+    /**********************************************************                              
+     */
+
+    @Override
+    public Version version() {
+        return PackageVersion.VERSION;
+    }
+
+    /*
+    /**********************************************************                              
+    /* ParserBase method impls
+    /**********************************************************                              
+     */
+
+    
+    @Override
+    protected boolean loadMore() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void _finishString() throws IOException, JsonParseException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void _closeInput() throws IOException {
+        _reader.close();
+    }
+    
+    /*
+    /**********************************************************                              
+    /* Overridden methods
+    /**********************************************************                              
+     */
+    
+    /*
+    /***************************************************
+    /* Public API, configuration
+    /***************************************************
+     */
+
+    /**
+     * Method for enabling specified CSV feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser enable(YAMLParser.Feature f)
+    {
+        _yamlFeatures |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified  CSV feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser disable(YAMLParser.Feature f)
+    {
+        _yamlFeatures &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for enabling or disabling specified CSV feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser configure(YAMLParser.Feature f, boolean state)
+    {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /**
+     * Method for checking whether specified CSV {@link Feature}
+     * is enabled.
+     */
+    public boolean isEnabled(YAMLParser.Feature f) {
+        return (_yamlFeatures & f.getMask()) != 0;
+    }
+
+//    @Override public CsvSchema getSchema() 
+    
+    /*
+    /**********************************************************
+    /* Location info
+    /**********************************************************
+     */
+
+    @Override
+    public JsonLocation getTokenLocation()
+    {
+        if (_lastEvent == null) {
+            return JsonLocation.NA;
+        }
+        return _locationFor(_lastEvent.getStartMark());
+    }
+
+    @Override
+    public JsonLocation getCurrentLocation() {
+        // can assume we are at the end of token now...
+        if (_lastEvent == null) {
+            return JsonLocation.NA;
+        }
+        return _locationFor(_lastEvent.getEndMark());
+    }
+    
+    protected JsonLocation _locationFor(Mark m)
+    {
+        if (m == null) {
+            return new JsonLocation(_ioContext.getSourceReference(),
+                    -1, -1, -1);
+        }
+        return new JsonLocation(_ioContext.getSourceReference(),
+                -1,
+                m.getLine() + 1, // from 0- to 1-based
+                m.getColumn() + 1); // ditto
+    }
+
+    // Note: SHOULD override 'getTokenLineNr', 'getTokenColumnNr', but those are final in 2.0
+    
+    /*
+    /**********************************************************
+    /* Parsing
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonToken nextToken() throws IOException, JsonParseException
+    {
+        _currentIsAlias = false;
+        _binaryValue = null;
+        _currentAnchor = null;
+        if (_closed) {
+            return null;
+        }
+        
+        while (true) {
+            Event evt = _yamlParser.getEvent();
+            // is null ok? Assume it is, for now, consider to be same as end-of-doc
+            if (evt == null) {
+                return (_currToken = null);
+            }
+            
+            _lastEvent = evt;
+            
+            /* One complication: field names are only inferred from the
+             * fact that we are in Object context...
+             */
+            if (_parsingContext.inObject() && _currToken != JsonToken.FIELD_NAME) {
+                if (!evt.is(Event.ID.Scalar)) {
+                    // end is fine
+                    if (evt.is(Event.ID.MappingEnd)) {
+                        if (!_parsingContext.inObject()) { // sanity check is optional, but let's do it for now
+                            _reportMismatchedEndMarker('}', ']');
+                        }
+                        _parsingContext = _parsingContext.getParent();
+                        return (_currToken = JsonToken.END_OBJECT);
+                    }
+                    _reportError("Expected a field name (Scalar value in YAML), got this instead: "+evt);
+                }
+                ScalarEvent scalar = (ScalarEvent) evt;
+                String name = scalar.getValue();
+                _currentFieldName = name;
+                _parsingContext.setCurrentName(name);
+                _currentAnchor = scalar.getAnchor();
+                return (_currToken = JsonToken.FIELD_NAME);
+            }
+            // Ugh. Why not expose id, to be able to Switch?
+
+            // scalar values are probably the commonest:
+            if (evt.is(Event.ID.Scalar)) {
+                return (_currToken = _decodeScalar((ScalarEvent) evt));
+            }
+
+            // followed by maps, then arrays
+            if (evt.is(Event.ID.MappingStart)) {
+                Mark m = evt.getStartMark();
+                _currentAnchor = ((NodeEvent)evt).getAnchor();
+                _parsingContext = _parsingContext.createChildObjectContext(m.getLine(), m.getColumn());
+                return (_currToken = JsonToken.START_OBJECT);
+            }
+            if (evt.is(Event.ID.MappingEnd)) { // actually error; can not have map-end here
+                _reportError("Not expecting END_OBJECT but a value");
+            }
+            if (evt.is(Event.ID.SequenceStart)) {
+                Mark m = evt.getStartMark();
+                _currentAnchor = ((NodeEvent)evt).getAnchor();
+                _parsingContext = _parsingContext.createChildArrayContext(m.getLine(), m.getColumn());
+                return (_currToken = JsonToken.START_ARRAY);
+            }
+            if (evt.is(Event.ID.SequenceEnd)) {
+                if (!_parsingContext.inArray()) { // sanity check is optional, but let's do it for now
+                    _reportMismatchedEndMarker(']', '}');
+                }
+                _parsingContext = _parsingContext.getParent();
+                return (_currToken = JsonToken.END_ARRAY);
+            }
+
+            // after this, less common tokens:
+            
+            if (evt.is(Event.ID.DocumentEnd)) {
+                // logical end of doc; fine. Two choices; either skip, or
+                // return null as marker. Do latter, for now. But do NOT close.
+                return (_currToken = null);
+            }
+            if (evt.is(Event.ID.DocumentStart)) {
+                // does this matter? Shouldn't, should it?
+                continue;
+            }
+            if (evt.is(Event.ID.Alias)) {
+                AliasEvent alias = (AliasEvent) evt;
+                _currentIsAlias = true;
+                _textValue = alias.getAnchor();
+                // for now, nothing to do: in future, maybe try to expose as ObjectIds?
+                return (_currToken = JsonToken.VALUE_STRING);
+            }
+            if (evt.is(Event.ID.StreamEnd)) { // end-of-input; force closure
+                close();
+                return (_currToken = null);
+            }
+            if (evt.is(Event.ID.StreamStart)) { // useless, skip
+                continue;
+            }
+        }
+    }
+    
+    protected JsonToken _decodeScalar(ScalarEvent scalar)
+    {
+        String value = scalar.getValue();
+        _textValue = value;
+        // we may get an explicit tag, if so, use for corroborating...
+        String typeTag = scalar.getTag();
+        final int len = value.length();
+
+        if (typeTag == null) { // no, implicit
+            // We only try to parse the string value if it is in the plain flow style.
+            // The API for ScalarEvent.getStyle() might be read as a null being returned
+            // in the plain flow style, but debugging shows the null-byte character, so
+            // we support both.
+            Character style = scalar.getStyle();
+
+            if ((style == null || style == '\u0000') && len > 0) {
+                char c = value.charAt(0);
+                switch (c) {
+                case 'n':
+                    if ("null".equals(value)) {
+                        return JsonToken.VALUE_NULL;
+                    }
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                case '+':
+                case '-':
+                case '.':
+                    JsonToken t = _decodeNumberScalar(value, len);
+                    if (t != null) {
+                        return t;
+                    }
+                }
+                Boolean B = _matchYAMLBoolean(value, len);
+                if (B != null) {
+                    return B.booleanValue() ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
+                }
+            }
+        } else { // yes, got type tag
+            // canonical values by YAML are actually 'y' and 'n'; but plenty more unofficial:
+            if ("bool".equals(typeTag)) { // must be "true" or "false"
+                Boolean B = _matchYAMLBoolean(value, len);
+                if (B != null) {
+                    return B.booleanValue() ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE;
+                }
+            } else if ("int".equals(typeTag)) {
+                return JsonToken.VALUE_NUMBER_INT;
+            } else if ("float".equals(typeTag)) {
+                return JsonToken.VALUE_NUMBER_FLOAT;
+            } else if ("null".equals(typeTag)) {
+                return JsonToken.VALUE_NULL;
+            }
+        }
+        
+        // any way to figure out actual type? No?
+        return (_currToken = JsonToken.VALUE_STRING);
+    }
+
+    protected Boolean _matchYAMLBoolean(String value, int len)
+    {
+        switch (len) {
+        case 1:
+            switch (value.charAt(0)) {
+            case 'y': case 'Y': return Boolean.TRUE;
+            case 'n': case 'N': return Boolean.FALSE;
+            }
+            break;
+        case 2:
+            if ("no".equalsIgnoreCase(value)) return Boolean.FALSE;
+            if ("on".equalsIgnoreCase(value)) return Boolean.TRUE;
+            break;
+        case 3:
+            if ("yes".equalsIgnoreCase(value)) return Boolean.TRUE;
+            if ("off".equalsIgnoreCase(value)) return Boolean.FALSE;
+            break;
+        case 4:
+            if ("true".equalsIgnoreCase(value)) return Boolean.TRUE;
+            break;
+        case 5:
+            if ("false".equalsIgnoreCase(value)) return Boolean.FALSE;
+            break;
+        }
+        return null;
+    }
+
+    protected JsonToken _decodeNumberScalar(String value, final int len)
+    {
+        if ("0".equals(value)) { // special case for regexp (can't take minus etc)
+            _numberNegative = false;
+            _numberInt = 0;
+            _numTypesValid = NR_INT;
+            return JsonToken.VALUE_NUMBER_INT;
+        }
+        /* 05-May-2012, tatu: Turns out this is a hot spot; so let's write it
+         *   out and avoid regexp overhead...
+         */
+        //if (PATTERN_INT.matcher(value).matches()) {
+        int i;
+        if (value.charAt(0) == '-') {
+            _numberNegative = true;
+            i = 1;
+            if (len == 1) {
+                return null;
+            }
+        } else {
+            _numberNegative = false;
+            i = 0;
+        }
+        while (true) {
+            int c = value.charAt(i);
+            if (c > '9' || c < '0') {
+                break;
+            }
+            if (++i == len) {
+                _numTypesValid = 0;
+                return JsonToken.VALUE_NUMBER_INT;
+            }
+        }
+        if (PATTERN_FLOAT.matcher(value).matches()) {
+            _numTypesValid = 0;
+            return JsonToken.VALUE_NUMBER_FLOAT;
+        }
+        return null;
+    }   
+
+    /*
+    /**********************************************************
+    /* String value handling
+    /**********************************************************
+     */
+
+    // For now we do not store char[] representation...
+    @Override
+    public boolean hasTextCharacters() {
+        return false;
+    }
+    
+    @Override
+    public String getText() throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.VALUE_STRING) {
+            return _textValue;
+        }
+        if (_currToken == JsonToken.FIELD_NAME) {
+            return _currentFieldName;
+        }
+        if (_currToken != null) {
+            if (_currToken.isScalarValue()) {
+                return _textValue;
+            }
+            return _currToken.asString();
+        }
+        return null;
+    }
+
+    @Override
+    public String getCurrentName() throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.FIELD_NAME) {
+            return _currentFieldName;
+        }
+        return super.getCurrentName();
+    }
+    
+    @Override
+    public char[] getTextCharacters() throws IOException, JsonParseException {
+        String text = getText();
+        return (text == null) ? null : text.toCharArray();
+    }
+
+    @Override
+    public int getTextLength() throws IOException, JsonParseException {
+        String text = getText();
+        return (text == null) ? 0 : text.length();
+    }
+
+    @Override
+    public int getTextOffset() throws IOException, JsonParseException {
+        return 0;
+    }
+    
+    /*
+    /**********************************************************************
+    /* Binary (base64)
+    /**********************************************************************
+     */
+
+    @Override
+    public Object getEmbeddedObject() throws IOException, JsonParseException {
+        return null;
+    }
+    
+    @Override
+    public byte[] getBinaryValue(Base64Variant variant) throws IOException, JsonParseException
+    {
+        if (_binaryValue == null) {
+            if (_currToken != JsonToken.VALUE_STRING) {
+                _reportError("Current token ("+_currToken+") not VALUE_STRING, can not access as binary");
+            }
+            ByteArrayBuilder builder = _getByteArrayBuilder();
+            _decodeBase64(getText(), builder, variant);
+            _binaryValue = builder.toByteArray();
+        }
+        return _binaryValue;
+    }
+
+    /*
+    /**********************************************************************
+    /* Number accessors
+    /**********************************************************************
+     */
+    
+    @Override
+    protected void _parseNumericValue(int expType)
+        throws IOException, JsonParseException
+    {
+        // Int or float?
+        if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+            int len = _textValue.length();
+            if (_numberNegative) {
+                len--;
+            }
+            if (len <= 9) { // definitely fits in int
+                _numberInt = Integer.parseInt(_textValue);
+                _numTypesValid = NR_INT;
+                return;
+            }
+            if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls
+                long l = Long.parseLong(_textValue);
+                // [JACKSON-230] Could still fit in int, need to check
+                if (len == 10) {
+                    if (_numberNegative) {
+                        if (l >= Integer.MIN_VALUE) {
+                            _numberInt = (int) l;
+                            _numTypesValid = NR_INT;
+                            return;
+                        }
+                    } else {
+                        if (l <= Integer.MAX_VALUE) {
+                            _numberInt = (int) l;
+                            _numTypesValid = NR_INT;
+                            return;
+                        }
+                    }
+                }
+                _numberLong = l;
+                _numTypesValid = NR_LONG;
+                return;
+            }
+            // !!! TODO: implement proper bounds checks; now we'll just use BigInteger for convenience
+            try {
+                BigInteger n = new BigInteger(_textValue);
+                // Could still fit in a long, need to check
+                if (len == 19 && n.bitLength() <= 63) {
+                    _numberLong = n.longValue();
+                    _numTypesValid = NR_LONG;
+                    return;
+                }
+                _numberBigInt = n;
+                _numTypesValid = NR_BIGINT;
+                return;
+            } catch (NumberFormatException nex) {
+                // Can this ever occur? Due to overflow, maybe?
+                _wrapError("Malformed numeric value '"+_textValue+"'", nex);
+            }
+        }
+        if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
+            // related to [Issue-4]: strip out optional underscores, if any:
+            String str = _cleanYamlDouble(_textValue);
+            try {
+                if (expType == NR_BIGDECIMAL) {
+                    _numberBigDecimal = new BigDecimal(str);
+                    _numTypesValid = NR_BIGDECIMAL;
+                } else {
+                    // Otherwise double has to do
+                    _numberDouble = Double.parseDouble(str);
+                    _numTypesValid = NR_DOUBLE;
+                }
+            } catch (NumberFormatException nex) {
+                // Can this ever occur? Due to overflow, maybe?
+                _wrapError("Malformed numeric value '"+str+"'", nex);
+            }
+            return;
+        }
+        _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
+    }
+
+    /*
+    /**********************************************************************
+    /* Internal methods
+    /**********************************************************************
+     */
+    
+    /**
+     * Helper method used to clean up YAML floating-point value so it can be parsed
+     * using standard JDK classes.
+     * Currently this just means stripping out optional underscores.
+     */
+    private String _cleanYamlDouble(String str)
+    {
+        final int len = str.length();
+        int ix = str.indexOf('_');
+        if (ix < 0 || len == 0) {
+            return str;
+        }
+        StringBuilder sb = new StringBuilder(len);
+        // first: do we have a leading plus sign to skip?
+        int i = (str.charAt(0) == '+') ? 1 : 0;
+        for (; i < len; ++i) {
+            char c = str.charAt(i);
+            if (c != '_') {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/src/main/resources/META-INF/LICENSE b/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..8d5775d
--- /dev/null
+++ b/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,8 @@
+This copy of Jackson JSON processor YAML module is licensed under the
+Apache (Software) License, version 2.0 ("the License").
+See the License for details about distribution rights, and the
+specific rights regarding derivate works.
+
+You may obtain a copy of the License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
diff --git a/src/main/resources/META-INF/NOTICE b/src/main/resources/META-INF/NOTICE
new file mode 100644
index 0000000..5ab1e56
--- /dev/null
+++ b/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,20 @@
+# Jackson JSON processor
+
+Jackson is a high-performance, Free/Open Source JSON processing library.
+It was originally written by Tatu Saloranta (tatu.saloranta at iki.fi), and has
+been in development since 2007.
+It is currently developed by a community of developers, as well as supported
+commercially by FasterXML.com.
+
+## Licensing
+
+Jackson core and extension components may be licensed under different licenses.
+To find the details that apply to this artifact see the accompanying LICENSE file.
+For more information, including possible other licensing options, contact
+FasterXML.com (http://fasterxml.com).
+
+## Credits
+
+A list of contributors may be found from CREDITS file, which is included
+in some artifacts (usually source distributions); but is always available
+from the source code management (SCM) system project uses.
diff --git a/src/main/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory b/src/main/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory
new file mode 100644
index 0000000..1686b09
--- /dev/null
+++ b/src/main/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory
@@ -0,0 +1 @@
+com.fasterxml.jackson.dataformat.yaml.YAMLFactory
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/EventsTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/EventsTest.java
new file mode 100644
index 0000000..41bb56d
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/EventsTest.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Tests that test low-level handling of events from YAML source
+ */
+public class EventsTest extends ModuleTestBase
+{
+    public void testBasic() throws Exception
+    {
+        final String YAML =
+ "string: 'text'\n"
++"bool: true\n"
++"bool2: false\n"
++"null: null\n"
++"i: 123\n"
++"d: 1.25\n"
+;
+        YAMLFactory f = new YAMLFactory();
+        JsonParser p = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, p.nextToken());
+
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        assertEquals("text", p.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+        assertEquals("true", p.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+        assertEquals("false", p.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_NULL, p.nextToken());
+        assertEquals("null", p.getText());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
+        assertEquals("123", p.getText());
+        assertEquals(123, p.getIntValue());
+        assertToken(JsonToken.FIELD_NAME, p.nextToken());
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertEquals("1.25", p.getText());
+        assertEquals(1.25, p.getDoubleValue());
+        assertEquals(1, p.getIntValue());
+
+        assertToken(JsonToken.END_OBJECT, p.nextToken());
+        assertNull(p.nextToken());
+        assertNull(p.nextToken());
+        assertNull(p.nextToken());
+        p.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/FormatDetectionTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/FormatDetectionTest.java
new file mode 100644
index 0000000..62c435c
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/FormatDetectionTest.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.format.DataFormatDetector;
+import com.fasterxml.jackson.core.format.DataFormatMatcher;
+import com.fasterxml.jackson.core.format.MatchStrength;
+
+public class FormatDetectionTest extends ModuleTestBase
+{
+   /**
+    * One nifty thing YAML has is the "---" start-doc indicator, which
+    * makes it possible to auto-detect format...
+    */
+   public void testFormatDetection() throws Exception
+   {
+       YAMLFactory yamlF = new YAMLFactory();
+       JsonFactory jsonF = new JsonFactory();
+       DataFormatDetector det = new DataFormatDetector(new JsonFactory[] { yamlF, jsonF });
+       // let's accept about any match; but only if no "solid match" found
+       det = det.withMinimalMatch(MatchStrength.WEAK_MATCH).withOptimalMatch(MatchStrength.SOLID_MATCH);
+
+       // First, give a JSON document...
+       DataFormatMatcher match = det.findFormat("{ \"name\" : \"Bob\" }".getBytes("UTF-8"));
+       assertNotNull(match);
+       assertEquals(jsonF.getFormatName(), match.getMatchedFormatName());
+       // and verify we can parse it
+       JsonParser p = match.createParserWithMatch();
+       assertToken(JsonToken.START_OBJECT, p.nextToken());
+       assertToken(JsonToken.FIELD_NAME, p.nextToken());
+       assertEquals("name", p.getCurrentName());
+       assertToken(JsonToken.VALUE_STRING, p.nextToken());
+       assertEquals("Bob", p.getText());
+       assertToken(JsonToken.END_OBJECT, p.nextToken());
+       p.close();
+
+       // then YAML
+       match = det.findFormat("---\nname: Bob\n".getBytes("UTF-8"));
+       assertNotNull(match);
+       assertEquals(yamlF.getFormatName(), match.getMatchedFormatName());
+       // and parsing
+       p = match.createParserWithMatch();
+       assertToken(JsonToken.START_OBJECT, p.nextToken());
+       assertToken(JsonToken.FIELD_NAME, p.nextToken());
+       assertEquals("name", p.getCurrentName());
+       assertToken(JsonToken.VALUE_STRING, p.nextToken());
+       assertEquals("Bob", p.getText());
+       assertToken(JsonToken.END_OBJECT, p.nextToken());
+       p.close();
+   }
+}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/ModuleTestBase.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/ModuleTestBase.java
new file mode 100644
index 0000000..a74e620
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/ModuleTestBase.java
@@ -0,0 +1,159 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public abstract class ModuleTestBase extends junit.framework.TestCase
+{
+    /**
+     * Slightly modified sample class from Jackson tutorial ("JacksonInFiveMinutes")
+     */
+    protected static class FiveMinuteUser {
+        public enum Gender { MALE, FEMALE };
+
+        private Gender _gender;
+
+        public String firstName, lastName;
+
+        private boolean _isVerified;
+        private byte[] _userImage;
+
+        public FiveMinuteUser() { }
+
+        public FiveMinuteUser(String first, String last, boolean verified, Gender g, byte[] data)
+        {
+            firstName = first;
+            lastName = last;
+            _isVerified = verified;
+            _gender = g;
+            _userImage = data;
+        }
+        
+        public boolean isVerified() { return _isVerified; }
+        public Gender getGender() { return _gender; }
+        public byte[] getUserImage() { return _userImage; }
+
+        public void setVerified(boolean b) { _isVerified = b; }
+        public void setGender(Gender g) { _gender = g; }
+        public void setUserImage(byte[] b) { _userImage = b; }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            if (o == this) return true;
+            if (o == null || o.getClass() != getClass()) return false;
+            FiveMinuteUser other = (FiveMinuteUser) o;
+            if (_isVerified != other._isVerified) return false;
+            if (_gender != other._gender) return false; 
+            if (!firstName.equals(other.firstName)) return false;
+            if (!lastName.equals(other.lastName)) return false;
+            byte[] otherImage = other._userImage;
+            if (otherImage.length != _userImage.length) return false;
+            for (int i = 0, len = _userImage.length; i < len; ++i) {
+                if (_userImage[i] != otherImage[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+    
+    protected ModuleTestBase() { }
+
+    /*
+    /**********************************************************************
+    /* Helper methods, setup
+    /**********************************************************************
+     */
+    
+    protected ObjectMapper mapperForYAML()
+    {
+        return new ObjectMapper(new YAMLFactory());
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods; low-level
+    /**********************************************************
+     */
+
+    public String quote(String str) {
+        return '"'+str+'"';
+    }
+
+    protected void assertToken(JsonToken expToken, JsonToken actToken)
+    {
+        if (actToken != expToken) {
+            fail("Expected token "+expToken+", current token "+actToken);
+        }
+    }
+    
+    protected void assertToken(JsonToken expToken, JsonParser jp)
+    {
+        assertToken(expToken, jp.getCurrentToken());
+    }
+
+    protected void assertType(Object ob, Class<?> expType)
+    {
+        if (ob == null) {
+            fail("Expected an object of type "+expType.getName()+", got null");
+        }
+        Class<?> cls = ob.getClass();
+        if (!expType.isAssignableFrom(cls)) {
+            fail("Expected type "+expType.getName()+", got "+cls.getName());
+        }
+    }
+
+    /**
+     * Method that gets textual contents of the current token using
+     * available methods, and ensures results are consistent, before
+     * returning them
+     */
+    protected String getAndVerifyText(JsonParser jp)
+        throws IOException, JsonParseException
+    {
+        // Ok, let's verify other accessors
+        int actLen = jp.getTextLength();
+        char[] ch = jp.getTextCharacters();
+        String str2 = new String(ch, jp.getTextOffset(), actLen);
+        String str = jp.getText();
+
+        if (str.length() !=  actLen) {
+            fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen);
+        }
+        assertEquals("String access via getText(), getTextXxx() must be the same", str, str2);
+
+        return str;
+    }
+
+    protected void verifyFieldName(JsonParser jp, String expName)
+        throws IOException
+    {
+        assertEquals(expName, jp.getText());
+        assertEquals(expName, jp.getCurrentName());
+    }
+    
+    protected void verifyIntValue(JsonParser jp, long expValue)
+        throws IOException
+    {
+        // First, via textual
+        assertEquals(String.valueOf(expValue), jp.getText());
+    }
+    
+    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/dataformat/yaml/SimpleDatabindTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleDatabindTest.java
new file mode 100644
index 0000000..937acf9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleDatabindTest.java
@@ -0,0 +1,81 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.ByteArrayInputStream;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.Assert;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Unit tests for checking functioning of the databinding
+ * on top of YAML layer.
+ */
+public class SimpleDatabindTest extends ModuleTestBase
+{
+    public void testBasicUntyped() throws Exception
+    {
+        final String YAML =
+ "template: Hello, %s!\n"
++"database:\n"
++"  driverClass: org.h2.Driver\n"
++"  user: scott\n"
++"  password: tiger\n"
++"  extra: [1,2]"
+;
+        ObjectMapper mapper = mapperForYAML();
+        Map<?,?> result = mapper.readValue(YAML, Map.class);
+        // sanity check first:
+        assertEquals(2, result.size());
+        // then literal comparison; easiest to just write as JSON...
+        ObjectMapper jsonMapper = new ObjectMapper();
+        String json = jsonMapper.writeValueAsString(result);
+        String EXP = "{\"template\":\"Hello, %s!\",\"database\":{"
+                +"\"driverClass\":\"org.h2.Driver\",\"user\":\"scott\",\"password\":\"tiger\","
+                +"\"extra\":[1,2]}}";
+        assertEquals(EXP, json);
+    }
+
+    public void testBasicPOJO() throws Exception
+    {
+        ObjectMapper mapper = mapperForYAML();
+        final String YAML =
+"firstName: Billy\n"
++"lastName: Baggins\n"                
++"gender: MALE\n"        
++"verified: true\n"
++"userImage: AQIDBAU=" // [1,2,3,4,5]
+;
+        FiveMinuteUser user = mapper.readValue(YAML, FiveMinuteUser.class);
+        assertEquals("Billy", user.firstName);
+        assertEquals("Baggins", user.lastName);
+        assertEquals(FiveMinuteUser.Gender.MALE, user.getGender());
+        assertTrue(user.isVerified());
+        byte[] data = user.getUserImage();
+        assertNotNull(data);
+        Assert.assertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, data);
+    }
+
+    public void testIssue1() throws Exception
+    {
+        ObjectMapper mapper = mapperForYAML();
+        final byte[] YAML = "firstName: Billy".getBytes("UTF-8");
+        FiveMinuteUser user = new FiveMinuteUser();
+        user.firstName = "Bubba";
+        mapper.readerForUpdating(user).readValue(new ByteArrayInputStream(YAML));
+        assertEquals("Billy", user.firstName);
+    }
+
+    // [Issue-2]
+    public void testUUIDs() throws Exception
+    {
+        ObjectMapper mapper = mapperForYAML();
+        UUID uuid = new UUID(0, 0);
+        String yaml = mapper.writeValueAsString(uuid);
+        
+        UUID result = mapper.readValue(yaml, UUID.class);
+        
+        assertEquals(uuid, result);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java
new file mode 100644
index 0000000..f2aa032
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java
@@ -0,0 +1,76 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class SimpleGenerationTest extends ModuleTestBase
+{
+    public void testStreamingArray() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+        StringWriter w = new StringWriter();
+        JsonGenerator gen = f.createJsonGenerator(w);
+        gen.writeStartArray();
+        gen.writeNumber(3);
+        gen.writeString("foobar");
+        gen.writeEndArray();
+        gen.close();
+        
+        String yaml = w.toString();
+        // should probably parse...
+        assertEquals("---\n- 3\n- \"foobar\"\n", yaml);
+    }
+
+    public void testStreamingObject() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+        StringWriter w = new StringWriter();
+        JsonGenerator gen = f.createJsonGenerator(w);
+        gen.writeStartObject();
+        gen.writeStringField("name", "Brad");
+        gen.writeNumberField("age", 39);
+        gen.writeEndObject();
+        gen.close();
+        
+        String yaml = w.toString();
+        assertEquals("---\nname: \"Brad\"\nage: 39\n", yaml);
+    }
+    
+    public void testBasicPOJO() throws Exception
+    {
+        ObjectMapper mapper = mapperForYAML();
+        FiveMinuteUser user = new FiveMinuteUser("Bob", "Dabolito", false,
+                FiveMinuteUser.Gender.MALE, new byte[] { 1, 3, 13, 79 });
+        String yaml = mapper.writeValueAsString(user).trim();
+        String[] parts = yaml.split("\n");
+        assertEquals(6, parts.length);
+        // unify ordering, need to use TreeSets
+        TreeSet<String> exp = new TreeSet<String>();
+        for (String part : parts) {
+            exp.add(part.trim());
+        }
+        Iterator<String> it = exp.iterator();
+        assertEquals("---", it.next());
+        assertEquals("firstName: \"Bob\"", it.next());
+        assertEquals("gender: \"MALE\"", it.next());
+        assertEquals("lastName: \"Dabolito\"", it.next());
+        assertEquals("userImage: \"AQMNTw==\"", it.next());
+        assertEquals("verified: false", it.next());
+    }
+
+    // Issue#12:
+    public void testWithFile() throws Exception
+    {
+    	File f = File.createTempFile("test", ".yml");
+    	f.deleteOnExit();
+        ObjectMapper mapper = mapperForYAML();
+        mapper.writeValue(f, "Foobar");
+        assertTrue(f.canRead());
+        assertEquals(13L, f.length());
+        f.delete();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleParseTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleParseTest.java
new file mode 100644
index 0000000..554205a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleParseTest.java
@@ -0,0 +1,281 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+
+import java.math.BigInteger;
+
+/**
+ * Unit tests for checking functioning of the underlying
+ * parser implementation.
+ */
+public class SimpleParseTest extends ModuleTestBase
+{
+    // Parsing large numbers around the transition from int->long and long->BigInteger
+    public void testIntParsing() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+        String YAML;
+        JsonParser jp;
+
+        // Test positive max-int
+        YAML = "num: 2147483647";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Integer.MAX_VALUE, jp.getIntValue());
+        assertEquals(JsonParser.NumberType.INT, jp.getNumberType());
+        assertEquals("2147483647", jp.getText());
+        jp.close();
+
+        // Test negative max-int
+        YAML = "num: -2147483648";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Integer.MIN_VALUE, jp.getIntValue());
+        assertEquals(JsonParser.NumberType.INT, jp.getNumberType());
+        assertEquals("-2147483648", jp.getText());
+        jp.close();
+
+        // Test positive max-int + 1
+        YAML = "num: 2147483648";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Integer.MAX_VALUE + 1L, jp.getLongValue());
+        assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
+        assertEquals("2147483648", jp.getText());
+        jp.close();
+
+        // Test negative max-int - 1
+        YAML = "num: -2147483649";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Integer.MIN_VALUE - 1L, jp.getLongValue());
+        assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
+        assertEquals("-2147483649", jp.getText());
+        jp.close();
+
+        // Test positive max-long
+        YAML = "num: 9223372036854775807";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Long.MAX_VALUE, jp.getLongValue());
+        assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
+        assertEquals("9223372036854775807", jp.getText());
+        jp.close();
+
+        // Test negative max-long
+        YAML = "num: -9223372036854775808";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(Long.MIN_VALUE, jp.getLongValue());
+        assertEquals(JsonParser.NumberType.LONG, jp.getNumberType());
+        assertEquals("-9223372036854775808", jp.getText());
+        jp.close();
+
+        // Test positive max-long + 1
+        YAML = "num: 9223372036854775808";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), jp.getBigIntegerValue());
+        assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType());
+        assertEquals("9223372036854775808", jp.getText());
+        jp.close();
+
+        // Test negative max-long - 1
+        YAML = "num: -9223372036854775809";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken());
+        assertEquals(BigInteger.valueOf(Long.MIN_VALUE).subtract(BigInteger.ONE), jp.getBigIntegerValue());
+        assertEquals(JsonParser.NumberType.BIG_INTEGER, jp.getNumberType());
+        assertEquals("-9223372036854775809", jp.getText());
+        jp.close();
+    }
+
+    // [Issue-4]: accidental recognition as double, with multiple dots
+    public void testDoubleParsing() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+
+        // First, test out valid use case.
+        String YAML;
+
+        YAML = "num: +1_000.25"; // note underscores; legal in YAML apparently
+        JsonParser jp = f.createJsonParser(YAML);
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("num", jp.getCurrentName());
+        // should be considered a String...
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, jp.nextToken());
+        assertEquals(1000.25, jp.getDoubleValue());
+        // let's retain exact representation text however:
+        assertEquals("+1_000.25", jp.getText());
+        jp.close();
+        
+        // and then non-number that may be mistaken
+        
+        final String IP = "10.12.45.127";
+        YAML = "ip: "+IP+"\n";
+        jp = f.createJsonParser(YAML);
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("ip", jp.getCurrentName());
+        // should be considered a String...
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals(IP, jp.getText());
+        jp.close();
+    }
+
+    // [Issue#7]
+    // looks like colons in content can be problematic, if unquoted
+    public void testColons() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+
+        // First, test out valid use case. NOTE: spaces matter!
+        String YAML = "section:\n"
+                    +"  text: foo:bar\n";
+        JsonParser jp = f.createJsonParser(YAML);
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("section", jp.getCurrentName());
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("text", jp.getCurrentName());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals("foo:bar", jp.getText());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.nextToken());
+
+        jp.close();
+    }
+    
+    /**
+     * How should YAML Anchors be exposed?
+     */
+    public void testAnchorParsing() throws Exception
+    {
+        // silly doc, just to expose an id (anchor) and ref to it
+        final String YAML = "---\n"
+                +"parent: &id1\n"
+                +"    name: Bob\n"
+                +"child: &id2\n"
+                +"    name: Bill\n"
+                +"    parentRef: *id1"
+                ;
+        YAMLFactory f = new YAMLFactory();
+        YAMLParser yp = f.createParser(YAML);
+
+        assertToken(JsonToken.START_OBJECT, yp.nextToken());
+        assertFalse(yp.isCurrentAlias());
+        assertNull(yp.getCurrentAnchor());
+
+        assertToken(JsonToken.FIELD_NAME, yp.nextToken());
+        assertEquals("parent", yp.getCurrentName());
+        assertFalse(yp.isCurrentAlias());
+        assertNull(yp.getCurrentAnchor());
+
+        assertToken(JsonToken.START_OBJECT, yp.nextToken());
+        assertFalse(yp.isCurrentAlias());
+        assertEquals("id1", yp.getCurrentAnchor());
+        assertToken(JsonToken.FIELD_NAME, yp.nextToken());
+        assertEquals("name", yp.getCurrentName());
+        assertToken(JsonToken.VALUE_STRING, yp.nextToken());
+        assertEquals("Bob", yp.getText());
+        assertFalse(yp.isCurrentAlias());
+        assertToken(JsonToken.END_OBJECT, yp.nextToken());
+
+        assertToken(JsonToken.FIELD_NAME, yp.nextToken());
+        assertEquals("child", yp.getCurrentName());
+        assertFalse(yp.isCurrentAlias());
+        assertToken(JsonToken.START_OBJECT, yp.nextToken());
+        assertFalse(yp.isCurrentAlias());
+        assertEquals("id2", yp.getCurrentAnchor());
+        assertToken(JsonToken.FIELD_NAME, yp.nextToken());
+        assertEquals("name", yp.getCurrentName());
+        assertToken(JsonToken.VALUE_STRING, yp.nextToken());
+        assertEquals("Bill", yp.getText());
+        assertToken(JsonToken.FIELD_NAME, yp.nextToken());
+        assertEquals("parentRef", yp.getCurrentName());
+        assertToken(JsonToken.VALUE_STRING, yp.nextToken());
+        assertEquals("id1", yp.getText());
+        assertTrue(yp.isCurrentAlias());
+        assertToken(JsonToken.END_OBJECT, yp.nextToken());
+
+        assertToken(JsonToken.END_OBJECT, yp.nextToken());
+        
+        assertNull(yp.nextToken());
+        yp.close();
+    }
+
+    // [Issue#10]
+    // Scalars should not be parsed when not in the plain flow style.
+    public void testQuotedStyles() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+
+        String YAML = "strings: [\"true\", 'false']";
+        JsonParser jp = f.createJsonParser(YAML);
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("strings", jp.getCurrentName());
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals("true", jp.getText());
+        assertToken(JsonToken.VALUE_STRING, jp.nextToken());
+        assertEquals("false", jp.getText());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.nextToken());
+
+        jp.close();
+    }
+
+    // Scalars should be parsed when in the plain flow style.
+    public void testUnquotedStyles() throws Exception
+    {
+        YAMLFactory f = new YAMLFactory();
+
+        String YAML = "booleans: [true, false]";
+        JsonParser jp = f.createJsonParser(YAML);
+
+        assertToken(JsonToken.START_OBJECT, jp.nextToken());
+        assertToken(JsonToken.FIELD_NAME, jp.nextToken());
+        assertEquals("booleans", jp.getCurrentName());
+        assertToken(JsonToken.START_ARRAY, jp.nextToken());
+        assertToken(JsonToken.VALUE_TRUE, jp.nextToken());
+        assertToken(JsonToken.VALUE_FALSE, jp.nextToken());
+        assertToken(JsonToken.END_ARRAY, jp.nextToken());
+        assertToken(JsonToken.END_OBJECT, jp.nextToken());
+        assertNull(jp.nextToken());
+
+        jp.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/TestVersions.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/TestVersions.java
new file mode 100644
index 0000000..a62a303
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/TestVersions.java
@@ -0,0 +1,30 @@
+package com.fasterxml.jackson.dataformat.yaml;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+
+public class TestVersions extends ModuleTestBase
+{
+    public void testMapperVersions() throws IOException
+    {
+        YAMLFactory f = new YAMLFactory();
+        assertVersion(f);
+        YAMLParser jp = (YAMLParser) f.createJsonParser("123");
+        assertVersion(jp);
+        YAMLGenerator jgen = (YAMLGenerator) f.createJsonGenerator(new ByteArrayOutputStream());
+        assertVersion(jgen);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private void assertVersion(Versioned vers)
+    {
+        assertEquals(PackageVersion.VERSION, vers.version());
+    }
+}
+
diff --git a/src/test/java/perf/DeserPerf.java b/src/test/java/perf/DeserPerf.java
new file mode 100644
index 0000000..e3bc347
--- /dev/null
+++ b/src/test/java/perf/DeserPerf.java
@@ -0,0 +1,142 @@
+package perf;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+
+/**
+ * Micro-benchmark for comparing performance of bean deserialization
+ */
+public final class DeserPerf
+{
+    /*
+    /**********************************************************
+    /* Actual test
+    /**********************************************************
+     */
+
+    private final int REPS;
+
+    private DeserPerf() {
+        // Let's try to guestimate suitable size
+        REPS = 9000;
+    }
+
+    private MediaItem buildItem()
+    {
+        MediaItem.Content content = new MediaItem.Content();
+        content.setPlayer(MediaItem.Player.JAVA);
+        content.setUri("http://javaone.com/keynote.mpg");
+        content.setTitle("Javaone Keynote");
+        content.setWidth(640);
+        content.setHeight(480);
+        content.setFormat("video/mpeg4");
+        content.setDuration(18000000L);
+        content.setSize(58982400L);
+        content.setBitrate(262144);
+        content.setCopyright("None");
+        content.addPerson("Bill Gates");
+        content.addPerson("Steve Jobs");
+
+        MediaItem item = new MediaItem(content);
+
+        item.addPhoto(new MediaItem.Photo("http://javaone.com/keynote_large.jpg", "Javaone Keynote", 1024, 768, MediaItem.Size.LARGE));
+        item.addPhoto(new MediaItem.Photo("http://javaone.com/keynote_small.jpg", "Javaone Keynote", 320, 240, MediaItem.Size.SMALL));
+
+        return item;
+    }
+    
+    public void test()
+        throws Exception
+    {
+        int sum = 0;
+
+        final MediaItem item = buildItem();
+//        JsonFactory jsonF = new JsonFactory();
+//        final ObjectMapper jsonMapper = new ObjectMapper(jsonF);
+        JsonFactory yamlF = new com.fasterxml.jackson.dataformat.yaml.YAMLFactory();
+        final ObjectMapper yamlMapper = new ObjectMapper(yamlF);
+        
+//        final ObjectMapper jsonMapper = new ObjectMapper(jsonF);
+//        jsonMapper.configure(SerializationConfig.Feature.USE_STATIC_TYPING, true);
+
+        // Use Jackson?
+//        byte[] json = jsonMapper.writeValueAsBytes(item);
+        byte[] yaml = yamlMapper.writeValueAsBytes(item);
+        
+        System.out.println("Warmed up: data size is "+yaml.length+" bytes; "+REPS+" reps -> "
+                +((REPS * yaml.length) >> 10)+" kB per iteration");
+        System.out.println();
+
+// for debugging:
+// System.err.println("JSON = "+jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(item));
+        
+        int round = 0;
+        while (true) {
+//            try {  Thread.sleep(100L); } catch (InterruptedException ie) { }
+//            int round = 2;
+
+            long curr = System.currentTimeMillis();
+            String msg;
+            round = (++round % 2);
+
+//if (true) round = 3; 
+            
+            boolean lf = (round == 0);
+
+            switch (round) {
+            case 0:
+            case 1:
+                msg = "Deserialize, bind, YAML";
+                sum += testDeser(yamlMapper, yaml, REPS);
+                break;
+
+            /*
+            case 0:
+                msg = "Deserialize, manual, YAML";
+                sum += testDeser(yamlMapper.getJsonFactory(), yaml, REPS);
+                break;
+                */
+
+            default:
+                throw new Error("Internal error");
+            }
+
+            curr = System.currentTimeMillis() - curr;
+            if (lf) {
+                System.out.println();
+            }
+            System.out.println("Test '"+msg+"' -> "+curr+" msecs ("
+                               +(sum & 0xFF)+").");
+        }
+    }
+
+    protected int testDeser(ObjectMapper mapper, byte[] input, int reps)
+        throws Exception
+    {
+        JavaType type = TypeFactory.defaultInstance().constructType(MediaItem.class);
+        MediaItem item = null;
+        for (int i = 0; i < reps; ++i) {
+            item = mapper.readValue(input, 0, input.length, type);
+        }
+        return item.hashCode(); // just to get some non-optimizable number
+    }
+
+    protected int testDeser(JsonFactory jf, byte[] input, int reps)
+        throws Exception
+    {
+        MediaItem item = null;
+        for (int i = 0; i < reps; ++i) {
+            JsonParser jp = jf.createParser(input);
+            item = MediaItem.deserialize(jp);
+            jp.close();
+        }
+        return item.hashCode(); // just to get some non-optimizable number
+    }
+    
+    public static void main(String[] args) throws Exception
+    {
+        new DeserPerf().test();
+    }
+}
diff --git a/src/test/java/perf/MediaItem.java b/src/test/java/perf/MediaItem.java
new file mode 100644
index 0000000..d58c3ce
--- /dev/null
+++ b/src/test/java/perf/MediaItem.java
@@ -0,0 +1,396 @@
+package perf;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Value class for performance tests
+ */
+ at JsonPropertyOrder({"content", "images"})
+public class MediaItem
+{
+    final static String NAME_IMAGES = "images";
+    final static String NAME_CONTENT = "content";
+    
+    public enum Player { JAVA, FLASH;  }
+    public enum Size { SMALL, LARGE; }
+
+    private List<Photo> _photos;
+    private Content _content;
+
+    public MediaItem() { }
+
+    public MediaItem(Content c)
+    {
+        _content = c;
+    }
+
+    public void addPhoto(Photo p) {
+        if (_photos == null) {
+            _photos = new ArrayList<Photo>();
+        }
+        _photos.add(p);
+    }
+    
+    public List<Photo> getImages() { return _photos; }
+    public void setImages(List<Photo> p) { _photos = p; }
+
+    public Content getContent() { return _content; }
+    public void setContent(Content c) { _content = c; }
+
+    // Custom deser
+    public static MediaItem deserialize(JsonParser jp) throws IOException
+    {
+        if (jp.nextToken() != JsonToken.START_OBJECT) {
+            throw new IOException("Need START_OBJECT for MediaItem");
+        }
+        MediaItem item = new MediaItem();
+        while (jp.nextToken() == JsonToken.FIELD_NAME) {
+            String name = jp.getCurrentName();
+            if (name == "images") {
+                item._photos = deserializeImages(jp);
+            } else if (name == "content") {
+                item._content = Content.deserialize(jp);
+            } else throw new IOException("Unknown field");
+        }
+        if (jp.getCurrentToken() != JsonToken.END_OBJECT) {
+            throw new IOException("Need END_OBJECT to complete MediaItem");
+        }
+        return item;
+    }
+    
+    private static List<Photo> deserializeImages(JsonParser jp) throws IOException
+    {
+        if (jp.nextToken() != JsonToken.START_ARRAY) {
+            throw new IOException("Need START_ARRAY for List of Photos");
+        }
+        ArrayList<Photo> images = new ArrayList<Photo>(4);
+        while (jp.nextToken() == JsonToken.START_OBJECT) {
+            images.add(Photo.deserialize(jp));
+        }
+        if (jp.getCurrentToken() != JsonToken.END_ARRAY) {
+            throw new IOException("Need END_ARRAY to complete List of Photos");
+        }
+        return images;
+    }
+    
+    // Custom serializer
+    public void serialize(JsonGenerator jgen) throws IOException
+    {
+        jgen.writeStartObject();
+
+        jgen.writeFieldName("content");
+        if (_content == null) {
+            jgen.writeNull();
+        } else {
+            _content.serialize(jgen);
+        }
+        if (_photos == null) {
+            jgen.writeNullField("images");
+        } else {
+            jgen.writeArrayFieldStart("images");
+            for (Photo photo : _photos) {
+                photo.serialize(jgen);
+            }
+            jgen.writeEndArray();
+        }
+
+        jgen.writeEndObject();
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper types
+    /**********************************************************
+     */
+    
+    @JsonPropertyOrder({"uri","title","width","height","size"})
+    public static class Photo
+    {
+        public final static int F_URI = 1;
+        public final static int F_TITLE = 2;
+        public final static int F_WIDTH = 3;
+        public final static int F_HEIGHT = 4;
+        public final static int F_SIZE = 5;
+        
+        public final static HashMap<String,Integer> sFields = new HashMap<String,Integer>();
+        static {
+            // MediaItem fields
+            sFields.put("uri", F_URI);
+            sFields.put("title", F_TITLE);
+            sFields.put("width", F_WIDTH);
+            sFields.put("height", F_HEIGHT);
+            sFields.put("size", F_SIZE);
+        }
+        
+      private String _uri;
+      private String _title;
+      private int _width;
+      private int _height;
+      private Size _size;
+    
+      public Photo() {}
+      public Photo(String uri, String title, int w, int h, Size s)
+      {
+          _uri = uri;
+          _title = title;
+          _width = w;
+          _height = h;
+          _size = s;
+      }
+    
+      public String getUri() { return _uri; }
+      public String getTitle() { return _title; }
+      public int getWidth() { return _width; }
+      public int getHeight() { return _height; }
+      public Size getSize() { return _size; }
+    
+      public void setUri(String u) { _uri = u; }
+      public void setTitle(String t) { _title = t; }
+      public void setWidth(int w) { _width = w; }
+      public void setHeight(int h) { _height = h; }
+      public void setSize(Size s) { _size = s; }
+
+      private static Size findSize(String id)
+      {
+          if (id.charAt(0) == 'L') {
+              if ("LARGE".equals(id)) {
+                  return Size.LARGE;
+              }
+          } else if ("SMALL".equals(id)) {
+              return Size.SMALL;
+          }
+          throw new IllegalArgumentException();
+      }
+      
+      public static Photo deserialize(JsonParser jp) throws IOException
+      {
+          Photo photo = new Photo();
+          while (jp.nextToken() == JsonToken.FIELD_NAME) {
+              String name = jp.getCurrentName();
+              jp.nextToken();
+              Integer I = sFields.get(name);
+              if (I != null) {
+                  switch (I.intValue()) {
+                  case F_URI:
+                      photo.setUri(jp.getText());
+                      continue;
+                  case F_TITLE:
+                      photo.setTitle(jp.getText());
+                      continue;
+                  case F_WIDTH:
+                      photo.setWidth(jp.getIntValue());
+                      continue;
+                  case F_HEIGHT:
+                      photo.setHeight(jp.getIntValue());
+                      continue;
+                  case F_SIZE:
+                      photo.setSize(findSize(jp.getText()));
+                      continue;
+                  }
+              }
+              throw new IOException("Unknown field '"+name+"'");
+          }
+          if (jp.getCurrentToken() != JsonToken.END_OBJECT) {
+              throw new IOException("Need END_OBJECT to complete Photo");
+          }
+          return photo;
+      }
+      
+      public void serialize(JsonGenerator jgen) throws IOException
+      {
+          jgen.writeStartObject();
+          jgen.writeStringField("uri", _uri);
+          jgen.writeStringField("title", _title);
+          jgen.writeNumberField("width", _width);
+          jgen.writeNumberField("height", _height);
+          jgen.writeStringField("size", (_size == null) ? null : _size.name());
+          jgen.writeEndObject();
+      }          
+    }
+
+    @JsonPropertyOrder({"player","uri","title","width","height","format","duration","size","bitrate","persons","copyright"})
+    public static class Content
+    {
+        public final static int F_PLAYER = 0;
+        public final static int F_URI = 1;
+        public final static int F_TITLE = 2;
+        public final static int F_WIDTH = 3;
+        public final static int F_HEIGHT = 4;
+        public final static int F_FORMAT = 5;
+        public final static int F_DURATION = 6;
+        public final static int F_SIZE = 7;
+        public final static int F_BITRATE = 8;
+        public final static int F_PERSONS = 9;
+        public final static int F_COPYRIGHT = 10;
+        
+        public final static HashMap<String,Integer> sFields = new HashMap<String,Integer>();
+        static {
+            sFields.put("player", F_PLAYER);
+            sFields.put("uri", F_URI);
+            sFields.put("title", F_TITLE);
+            sFields.put("width", F_WIDTH);
+            sFields.put("height", F_HEIGHT);
+            sFields.put("format", F_FORMAT);
+            sFields.put("duration", F_DURATION);
+            sFields.put("size", F_SIZE);
+            sFields.put("bitrate", F_BITRATE);
+            sFields.put("persons", F_PERSONS);
+            sFields.put("copyright", F_COPYRIGHT);
+        }
+        
+        private Player _player;
+        private String _uri;
+        private String _title;
+        private int _width;
+        private int _height;
+        private String _format;
+        private long _duration;
+        private long _size;
+        private int _bitrate;
+        private List<String> _persons;
+        private String _copyright;
+    
+        public Content() { }
+
+        public void addPerson(String p) {
+            if (_persons == null) {
+                _persons = new ArrayList<String>();
+            }
+            _persons.add(p);
+        }
+        
+        public Player getPlayer() { return _player; }
+        public String getUri() { return _uri; }
+        public String getTitle() { return _title; }
+        public int getWidth() { return _width; }
+        public int getHeight() { return _height; }
+        public String getFormat() { return _format; }
+        public long getDuration() { return _duration; }
+        public long getSize() { return _size; }
+        public int getBitrate() { return _bitrate; }
+        public List<String> getPersons() { return _persons; }
+        public String getCopyright() { return _copyright; }
+    
+        public void setPlayer(Player p) { _player = p; }
+        public void setUri(String u) {  _uri = u; }
+        public void setTitle(String t) {  _title = t; }
+        public void setWidth(int w) {  _width = w; }
+        public void setHeight(int h) {  _height = h; }
+        public void setFormat(String f) {  _format = f;  }
+        public void setDuration(long d) {  _duration = d; }
+        public void setSize(long s) {  _size = s; }
+        public void setBitrate(int b) {  _bitrate = b; }
+        public void setPersons(List<String> p) {  _persons = p; }
+        public void setCopyright(String c) {  _copyright = c; }
+
+        private static Player findPlayer(String id)
+        {
+            if ("JAVA".equals(id)) {
+                return Player.JAVA;
+            }
+            if ("FLASH".equals(id)) {
+                return Player.FLASH;
+            }
+            throw new IllegalArgumentException("Weird Player value of '"+id+"'");
+        }
+        
+        public static Content deserialize(JsonParser jp) throws IOException
+        {
+            if (jp.nextToken() != JsonToken.START_OBJECT) {
+                throw new IOException("Need START_OBJECT for Content");
+            }
+            Content content = new Content();
+
+            while (jp.nextToken() == JsonToken.FIELD_NAME) {
+                String name = jp.getCurrentName();
+                jp.nextToken();
+                Integer I = sFields.get(name);
+                if (I != null) {
+                    switch (I.intValue()) {
+                    case F_PLAYER:
+                        content.setPlayer(findPlayer(jp.getText()));
+                    case F_URI:
+                        content.setUri(jp.getText());
+                        continue;
+                    case F_TITLE:
+                        content.setTitle(jp.getText());
+                        continue;
+                    case F_WIDTH:
+                        content.setWidth(jp.getIntValue());
+                        continue;
+                    case F_HEIGHT:
+                        content.setHeight(jp.getIntValue());
+                        continue;
+                    case F_FORMAT:
+                        content.setCopyright(jp.getText());
+                        continue;
+                    case F_DURATION:
+                        content.setDuration(jp.getLongValue());
+                        continue;
+                    case F_SIZE:
+                        content.setSize(jp.getLongValue());
+                        continue;
+                    case F_BITRATE:
+                        content.setBitrate(jp.getIntValue());
+                        continue;
+                    case F_PERSONS:
+                        content.setPersons(deserializePersons(jp));
+                        continue;
+                    case F_COPYRIGHT:
+                        content.setCopyright(jp.getText());
+                        continue;
+                    }
+                }
+                throw new IOException("Unknown field '"+name+"'");
+            }
+            if (jp.getCurrentToken() != JsonToken.END_OBJECT) {
+                throw new IOException("Need END_OBJECT to complete Content");
+            }
+            return content;
+        }
+        
+        private static List<String> deserializePersons(JsonParser jp) throws IOException
+        {
+            if (jp.getCurrentToken() != JsonToken.START_ARRAY) {
+                throw new IOException("Need START_ARRAY for List of Persons (got "+jp.getCurrentToken()+")");
+            }
+            ArrayList<String> persons = new ArrayList<String>(4);
+            while (jp.nextToken() == JsonToken.VALUE_STRING) {
+                persons.add(jp.getText());
+            }
+            if (jp.getCurrentToken() != JsonToken.END_ARRAY) {
+                throw new IOException("Need END_ARRAY to complete List of Persons");
+            }
+            return persons;
+        }
+        
+        public void serialize(JsonGenerator jgen) throws IOException
+        {
+            jgen.writeStartObject();
+            jgen.writeStringField("player", (_player == null) ? null : _player.name());
+            jgen.writeStringField("uri", _uri);
+            jgen.writeStringField("title", _title);
+            jgen.writeNumberField("width", _width);
+            jgen.writeNumberField("height", _height);
+            jgen.writeStringField("format", _format);
+            jgen.writeNumberField("duration", _duration);
+            jgen.writeNumberField("size", _size);
+            jgen.writeNumberField("bitrate", _bitrate);
+            jgen.writeStringField("copyright", _copyright);
+            if (_persons == null) {
+                jgen.writeNullField("persons");
+            } else {
+                jgen.writeArrayFieldStart("persons");
+                for (String p : _persons) {
+                    jgen.writeString(p);
+                }
+                jgen.writeEndArray();
+            }
+            jgen.writeEndObject();
+        }          
+    }
+}
diff --git a/src/test/java/perf/SerPerf.java b/src/test/java/perf/SerPerf.java
new file mode 100644
index 0000000..d3c3e32
--- /dev/null
+++ b/src/test/java/perf/SerPerf.java
@@ -0,0 +1,146 @@
+package perf;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+public final class SerPerf
+{
+    /*
+    /**********************************************************
+    /* Actual test
+    /**********************************************************
+     */
+
+    private final int REPS;
+
+    private SerPerf() throws Exception
+    {
+        // Let's try to guesstimate suitable size...
+        REPS = 6000;
+    }
+
+    private MediaItem buildItem()
+    {
+        MediaItem.Content content = new MediaItem.Content();
+        content.setPlayer(MediaItem.Player.JAVA);
+        content.setUri("http://javaone.com/keynote.mpg");
+        content.setTitle("Javaone Keynote");
+        content.setWidth(640);
+        content.setHeight(480);
+        content.setFormat("video/mpeg4");
+        content.setDuration(18000000L);
+        content.setSize(58982400L);
+        content.setBitrate(262144);
+        content.setCopyright("None");
+        content.addPerson("Bill Gates");
+        content.addPerson("Steve Jobs");
+
+        MediaItem item = new MediaItem(content);
+
+        item.addPhoto(new MediaItem.Photo("http://javaone.com/keynote_large.jpg", "Javaone Keynote", 1024, 768, MediaItem.Size.LARGE));
+        item.addPhoto(new MediaItem.Photo("http://javaone.com/keynote_small.jpg", "Javaone Keynote", 320, 240, MediaItem.Size.SMALL));
+
+        return item;
+    }
+    
+    public void test()
+        throws Exception
+    {
+        int i = 0;
+        int sum = 0;
+
+        ByteArrayOutputStream result = new ByteArrayOutputStream();
+
+        final MediaItem item = buildItem();
+        final JsonFactory jsonF = new YAMLFactory();
+            
+        final ObjectMapper jsonMapper = new ObjectMapper(jsonF);
+
+        JsonNode root = jsonMapper.valueToTree(item);
+        
+        while (true) {
+//            Thread.sleep(150L);
+            ++i;
+            int round = (i % 3);
+
+            // override?
+            round = 0;
+
+            long curr = System.currentTimeMillis();
+            String msg;
+
+            switch (round) {
+
+            case 0:
+                msg = "Serialize, JSON";
+                sum += testObjectSer(jsonMapper, item, REPS+REPS, result);
+                break;
+
+            case 1:
+                msg = "Serialize, JSON/manual";
+                sum += testObjectSer(jsonMapper.getFactory(), item, REPS+REPS, result);
+                break;
+
+            case 2:
+                msg = "Serialize, JsonNode";
+                sum += testNodeSer(jsonMapper, root, REPS+REPS, result);
+//                sum += testNodeSer(smileMapper, root, REPS+REPS, result);
+                break;
+
+            default:
+                throw new Error("Internal error");
+            }
+
+            curr = System.currentTimeMillis() - curr;
+            if (round == 0) {  System.out.println(); }
+            System.out.println("Test '"+msg+"' -> "+curr+" msecs ("+(sum & 0xFF)+").");
+            if ((i & 0x1F) == 0) { // GC every 64 rounds
+                System.out.println("[GC]");
+                Thread.sleep(20L);
+                System.gc();
+                Thread.sleep(20L);
+            }
+        }
+    }
+
+    protected int testObjectSer(ObjectMapper mapper, Object value, int reps, ByteArrayOutputStream result)
+        throws Exception
+    {
+        for (int i = 0; i < reps; ++i) {
+            result.reset();
+            mapper.writeValue(result, value);
+        }
+        return result.size(); // just to get some non-optimizable number
+    }
+
+    protected int testNodeSer(ObjectMapper mapper, JsonNode value, int reps, ByteArrayOutputStream result)
+            throws Exception
+        {
+            for (int i = 0; i < reps; ++i) {
+                result.reset();
+                mapper.writeValue(result, value);
+            }
+            return result.size(); // just to get some non-optimizable number
+        }
+    
+    protected int testObjectSer(JsonFactory jf, MediaItem value, int reps, ByteArrayOutputStream result)
+        throws Exception
+    {
+        for (int i = 0; i < reps; ++i) {
+            result.reset();
+            JsonGenerator jgen = jf.createGenerator(result, JsonEncoding.UTF8);
+            value.serialize(jgen);
+            jgen.close();
+        }
+        return result.size(); // just to get some non-optimizable number
+    }
+    
+    public static void main(String[] args) throws Exception
+    {
+        new SerPerf().test();
+    }
+}

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



More information about the pkg-java-commits mailing list