Merge commit 'cce31f6de8fea06821a8b57a9c0d2c687c8161f5' into nyc-dev

Bug: 27552463
Change-Id: Ieda4ab70675662ca687ac4a2e4f59f30034ffdca
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e0a8909
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+
+
+.classpath
+.project
+.settings/
+target/
+bin/
+
+
diff --git a/.hgeol b/.hgeol
new file mode 100644
index 0000000..5bd567d
--- /dev/null
+++ b/.hgeol
@@ -0,0 +1,8 @@
+[patterns]

+**.java  = LF

+**.txt   = LF

+**.xml   = LF

+**.yaml  = LF

+**.yml   = LF

+**.data  = LF

+**.canonical   = LF

diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..2f0116d
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,12 @@
+syntax: regexp
+^target$
+^bin$
+^\.settings$
+^\.project$
+^\.classpath$
+
+^\.idea$
+snakeyaml.iml
+snakeyaml.ipr
+snakeyaml.iws
+
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
new file mode 100644
index 0000000..e3d7780
--- /dev/null
+++ b/.mvn/extensions.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<extensions>
+  <extension>
+    <groupId>io.takari.polyglot</groupId>
+    <artifactId>polyglot-yaml</artifactId>
+    <version>0.1.12</version>
+  </extension>
+</extensions>
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..5fd4d50
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.jar
Binary files differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..ac516e2
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.1/apache-maven-3.3.1-bin.zip
\ No newline at end of file
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..e91fdb1
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,59 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Build support for snakeyaml within the Android Open Source Project
+# See https://source.android.com/source/building.html for more information
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# List of all files that need to be patched (see src/patches/android)
+snakeyaml_need_patch_src_files := \
+            src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java \
+            src/main/java/org/yaml/snakeyaml/constructor/Constructor.java \
+            src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java \
+            src/main/java/org/yaml/snakeyaml/representer/Representer.java
+# List of all files that are unsupported on android (see pom.xml)
+snakeyaml_unsupported_android_src_files := \
+            src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
+snakeyaml_src_files_unfiltered := $(call all-java-files-under, src/main)
+# We omit the list of files that need to be patched because those are included by LOCAL_GENERATED_SOURCES instead.
+snakeyaml_src_files := $(filter-out $(snakeyaml_need_patch_src_files) $(snakeyaml_unsupported_android_src_files),$(snakeyaml_src_files_unfiltered))
+
+# Host-side Java build
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(snakeyaml_src_files_unfiltered)
+LOCAL_MODULE := snakeyaml-host
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Host-side Dalvik build
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(snakeyaml_src_files)
+LOCAL_MODULE := snakeyaml-hostdex
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+# Apply all of the Android patches in src/patches/android by running patch-android-src script on them.
+intermediates:= $(local-generated-sources-dir)
+GEN := $(addprefix $(intermediates)/, $(snakeyaml_need_patch_src_files)) # List of all files that need to be patched.
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(PRIVATE_PATH)/patch-android-src $(PRIVATE_PATH)/ $< $@
+$(GEN): $(intermediates)/%.java : $(LOCAL_PATH)/%.java $(LOCAL_PATH)/patch-android-src
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+
+# TODO: Consider adding tests.
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..7ace389
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# Clean up generated code from snakeyaml-hostdex
+# (When removing a file from $(snakeyaml_need_patch_src_files),
+# make a rule similar to below to remove stale files from incremental builds).
+#
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/gen/JAVA_LIBRARIES/snakeyaml-hostdex_intermediates)/path/to/RemovedFile.java
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d9a10c0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,176 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..d9a10c0
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,176 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b51464e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+***The art of simplicity is a puzzle of complexity.***
+
+## Overview ##
+[YAML](http://yaml.org) is a data serialization format designed for human readability and interaction with scripting languages.
+
+SnakeYAML is a YAML processor for the Java Virtual Machine.
+
+## SnakeYAML features ##
+
+* a **complete** [YAML 1.1 processor](http://yaml.org/spec/1.1/current.html). In particular, SnakeYAML can parse all examples from the specification.
+* Unicode support including UTF-8/UTF-16 input/output.
+* high-level API for serializing and deserializing native Java objects.
+* support for all types from the [YAML types repository](http://yaml.org/type/index.html).
+* relatively sensible error messages.
+
+## Info ##
+ * [Changes](https://bitbucket.org/asomov/snakeyaml/wiki/Changes)
+ * [Documentation](https://bitbucket.org/asomov/snakeyaml/wiki/Documentation)
+
+## Contribute ##
+* Mercurial DVCS is used to dance with the [source code](https://bitbucket.org/asomov/snakeyaml/src).
+* If you find a bug in SnakeYAML, please [file a bug report](https://bitbucket.org/asomov/snakeyaml/issues?status=new&status=open).
+* You may discuss SnakeYAML at
+[the mailing list](http://groups.google.com/group/snakeyaml-core).
\ No newline at end of file
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..71ee1d2
--- /dev/null
+++ b/README.version
@@ -0,0 +1,4 @@
+URL: https://bitbucket.org/asomov/snakeyaml
+Version: hg id 29a091e21588
+BugComponent: 99142
+Owners: iam
diff --git a/docker-run-jdk6.sh b/docker-run-jdk6.sh
new file mode 100755
index 0000000..5904c90
--- /dev/null
+++ b/docker-run-jdk6.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+./docker-run.sh 6
+
diff --git a/docker-run-jdk7.sh b/docker-run-jdk7.sh
new file mode 100755
index 0000000..b2c9782
--- /dev/null
+++ b/docker-run-jdk7.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+./docker-run.sh 7
+
+
diff --git a/docker-run-jdk8.sh b/docker-run-jdk8.sh
new file mode 100755
index 0000000..aaf10f0
--- /dev/null
+++ b/docker-run-jdk8.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+./docker-run.sh 8
+
+
diff --git a/docker-run.sh b/docker-run.sh
new file mode 100755
index 0000000..3abaa88
--- /dev/null
+++ b/docker-run.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+if [ "$#" -ne 1 ]
+then
+  echo "Usage: docker-run.sh <JDK number> (number can be 6, 7, 8)"
+  exit 1
+fi
+
+docker run --rm -it               \
+    -u `id -u`:`id -g`            \
+    -v `pwd`:/work                \
+    -v ~:/my-home                 \
+    -e "HOME=/my-home"            \
+    -w /work                      \
+    maven:3-jdk-$1                \
+    mvn -Dmaven.repo.local=/my-home/.m2/repository clean test
+
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..c67cd41
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,234 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+           #
+           # Look for the Apple JDKs first to preserve the existing behaviour, and then look
+           # for the new JDKs provided by Oracle.
+           # 
+           if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
+             #
+             # Apple JDKs
+             #
+             export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
+           fi
+           
+           if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
+             #
+             # Apple JDKs
+             #
+             export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+           fi
+             
+           if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
+             #
+             # Oracle JDKs
+             #
+             export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+           fi           
+
+           if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
+             #
+             # Apple JDKs
+             #
+             export JAVA_HOME=`/usr/libexec/java_home`
+           fi
+           ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+  # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+  local basedir=$(pwd)
+  local wdir=$(pwd)
+  while [ "$wdir" != '/' ] ; do
+    wdir=$(cd "$wdir/.."; pwd)
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# Provide a "standardized" way to retrieve the CLI args that will 
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "./.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} "$@"
+
diff --git a/mvnw.bat b/mvnw.bat
new file mode 100644
index 0000000..7ca42b9
--- /dev/null
+++ b/mvnw.bat
@@ -0,0 +1,177 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto chkMHome
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:chkMHome
+if not "%M2_HOME%"=="" goto valMHome
+
+SET "M2_HOME=%~dp0.."
+if not "%M2_HOME%"=="" goto valMHome
+
+echo.
+echo Error: M2_HOME not found in your environment. >&2
+echo Please set the M2_HOME variable in your environment to match the >&2
+echo location of the Maven installation. >&2
+echo.
+goto error
+
+:valMHome
+
+:stripMHome
+if not "_%M2_HOME:~-1%"=="_\" goto checkMCmd
+set "M2_HOME=%M2_HOME:~0,-1%"
+goto stripMHome
+
+:checkMCmd
+if exist "%M2_HOME%\bin\mvn.cmd" goto init
+
+echo.
+echo Error: M2_HOME is set to an invalid directory. >&2
+echo M2_HOME = "%M2_HOME%" >&2
+echo Please set the M2_HOME variable in your environment to match the >&2
+echo location of the Maven installation >&2
+echo.
+goto error
+@REM ==== END VALIDATION ====
+
+:init
+
+set MAVEN_CMD_LINE_ARGS=%*
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+for %%i in ("%M2_HOME%"\boot\plexus-classworlds-*) do set CLASSWORLDS_JAR="%%i"
+
+set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.home=%M2_HOME%" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/patch-android-src b/patch-android-src
new file mode 100755
index 0000000..dacf1fc
--- /dev/null
+++ b/patch-android-src
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Given a source file, for example Constructor.java, find the patch file for it and apply it.
+# Patch is applied to a copy (as opposed to in-place), for example into a build artifact location.
+
+if [[ $# -lt 3 ]]; then
+  echo "Usage: $(basename $0) <src-file-prefix> <src-file> <dst-file>"
+  exit 1
+fi
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+ANDROID_PATCHES_DIR="$DIR/src/patches/android"
+
+src_file_prefix="$1"
+src_file="$2"
+src_file_with_prefix="$2"
+dst_file="$3"
+dst_dir="$(dirname "$dst_file")"
+
+# Sanity checking
+
+if ! [[ -d $ANDROID_PATCHES_DIR ]]; then
+  echo "FATAL: Android patch directory $ANDROID_PATCHES_DIR does not exist." >&2
+  exit 1
+fi
+
+if ! [[ $src_file == $src_file_prefix* ]]; then
+  echo "$src_file_prefix is not a valid prefix of $src_file" >&2
+  exit 1
+fi
+
+if ! [[ -f $src_file ]]; then
+  echo "Source file $src_file does not exist." >&2
+  exit 1
+fi
+
+# Remove the src file prefix, giving us the correct grep target for the .patch files.
+src_file="${src_file#$src_file_prefix}"
+
+patch_file_src=$(grep --files-with-matches -R "diff --git a/$src_file" "$ANDROID_PATCHES_DIR")
+if [[ $? -ne 0 ]]; then
+  echo "Error: Could not find a corresponding .patch file for $src_file" >&2
+  exit 1
+fi
+
+# Parse the source file from the .patch file (assumes exactly one file diff per .patch file)
+src_file_check=$(cat "$patch_file_src" | grep "diff --git a/" | sed -e "s:diff --git a\/::g" | sed -e "s: b\/.*::g")
+
+
+if ! [[ -f $patch_file_src ]]; then
+  echo "Patch file $patch_file_src does not exist." >&2
+  exit 1
+fi
+
+if ! cat "$patch_file_src" | grep "diff --git a/" > /dev/null; then
+  echo "File $patch_file_src is not a valid diff file" >&2
+  exit 1
+fi
+
+if [[ $src_file != $src_file_check ]]; then
+  echo "Check-fail: $src_file was not same as found in patch file ($src_file_check)" >&2
+  exit 1
+fi
+
+set -e
+
+# Copy the src file and then apply the patch to it.
+mkdir -p "$dst_dir"
+cp "$src_file_with_prefix" "$dst_file"
+if ! [[ -f "$dst_file" ]]; then
+  echo "File "$dst_file" does not exist; patching will fail" >&2
+  exit 1
+fi
+cd "$dst_dir"
+patch "$(basename "$dst_file")" "$patch_file_src"
+echo "Successfully applied patch $patch_file_src into copy of file $dst_file"
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..91886af
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,637 @@
+<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/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.yaml</groupId>
+    <artifactId>snakeyaml</artifactId>
+    <version>1.18-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.scm.id>bitbucket</project.scm.id>
+        <release.repo.url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</release.repo.url>
+        <snapshot.repo.url>https://oss.sonatype.org/content/repositories/snapshots/</snapshot.repo.url>
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+        <maven.javadoc.failOnError>false</maven.javadoc.failOnError>
+    </properties>
+    <name>SnakeYAML</name>
+    <description>YAML 1.1 parser and emitter for Java</description>
+    <inceptionYear>2008</inceptionYear>
+    <url>http://www.snakeyaml.org</url>
+    <issueManagement>
+        <system>Bitbucket</system>
+        <url>https://bitbucket.org/asomov/snakeyaml/issues</url>
+    </issueManagement>
+    <!--ciManagement>
+        <system>jenkins</system>
+        <url>https://snakeyaml.ci.cloudbees.com/job/SnakeYAML/</url>
+    </ciManagement-->
+    <mailingLists>
+        <mailingList>
+            <name>SnakeYAML developers and users List</name>
+            <post>snakeyaml-core@googlegroups.com</post>
+        </mailingList>
+    </mailingLists>
+    <scm>
+        <connection>scm:hg:http://bitbucket.org/asomov/snakeyaml</connection>
+        <developerConnection>scm:hg:ssh://hg@bitbucket.org/asomov/snakeyaml</developerConnection>
+        <url>https://bitbucket.org/asomov/snakeyaml/src</url>
+      <tag>HEAD</tag>
+  </scm>
+    <licenses>
+        <license>
+            <name>Apache License, Version 2.0</name>
+            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    <developers>
+        <developer>
+            <id>asomov</id>
+            <name>Andrey Somov</name>
+            <email>public.somov@gmail.com</email>
+        </developer>
+        <developer>
+            <id>maslovalex</id>
+            <name>Alexander Maslov</name>
+            <email>alexander.maslov@gmail.com</email>
+        </developer>
+        <developer>
+            <id>Jordan</id>
+            <name>Jordan Angold</name>
+            <email>jordanangold@gmail.com</email>
+        </developer>
+    </developers>
+    <prerequisites>
+        <maven>3.0.5</maven>
+    </prerequisites>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring</artifactId>
+            <version>2.5.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.velocity</groupId>
+            <artifactId>velocity</artifactId>
+            <version>1.6.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>1.6</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <distributionManagement>
+        <repository>
+            <id>sonatype-nexus-staging</id>
+            <name>Nexus Release Repository</name>
+            <url>${release.repo.url}</url>
+        </repository>
+        <snapshotRepository>
+            <id>sonatype-nexus-snapshots</id>
+            <name>Sonatype Nexus Snapshots</name>
+            <url>${snapshot.repo.url}</url>
+            <uniqueVersion>false</uniqueVersion>
+        </snapshotRepository>
+    </distributionManagement>
+    <build>
+        <testResources>
+            <testResource>
+                <directory>${basedir}/src/test/resources</directory>
+                <filtering>true</filtering>
+            </testResource>
+        </testResources>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <version>2.10.3</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-site-plugin</artifactId>
+                    <version>3.5</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    <source>${maven.compiler.source}</source>
+                    <target>${maven.compiler.target}</target>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.19.1</version>
+                <configuration>
+                    <argLine>-Xmx512m</argLine>
+                    <includes>
+                        <include>**/*Test.java</include>
+                    </includes>
+                    <excludes>
+                        <exclude>**/StressTest.java</exclude>
+                        <exclude>**/ParallelTest.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-eclipse-plugin</artifactId>
+                <version>2.10</version>
+                <configuration>
+                    <buildOutputDirectory>bin</buildOutputDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.7</version>
+                <configuration>
+                    <check>
+                        <totalBranchRate>80</totalBranchRate>
+                        <totalLineRate>95</totalLineRate>
+                    </check>
+                    <formats>
+                        <format>html</format>
+                        <format>xml</format>
+                    </formats>
+                    <instrumentation>
+                        <excludes>
+                            <exclude>org/yaml/snakeyaml/external/**</exclude>
+                        </excludes>
+                    </instrumentation>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>clean</goal>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-changes-plugin</artifactId>
+                <version>2.11</version>
+                <executions>
+                    <execution>
+                        <id>validate-changes</id>
+                        <phase>pre-site</phase>
+                        <goals>
+                            <goal>changes-validate</goal>
+                        </goals>
+                        <configuration>
+                            <failOnError>true</failOnError>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <links>
+                        <link>http://java.sun.com/javase/6/docs/api/</link>
+                    </links>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>com.mycila.maven-license-plugin</groupId>
+                <artifactId>maven-license-plugin</artifactId>
+                <version>1.10.b1</version>
+                <configuration>
+                    <header>src/etc/header.txt</header>
+                    <quiet>false</quiet>
+                    <failIfMissing>true</failIfMissing>
+                    <aggregate>false</aggregate>
+                    <includes>
+                        <include>src/**/*.java</include>
+                    </includes>
+                    <excludes>
+                        <exclude>src/main/java/org/yaml/snakeyaml/external/**</exclude>
+                    </excludes>
+                    <useDefaultExcludes>true</useDefaultExcludes>
+                    <useDefaultMapping>true</useDefaultMapping>
+                    <strictCheck>true</strictCheck>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>site</phase>
+                        <goals>
+                            <goal>format</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>3.0.1</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <_nouses>true</_nouses>
+                        <Export-Package>
+                            !org.yaml.snakeyaml.external*,
+                            org.yaml.snakeyaml.*;version=${project.version}
+                        </Export-Package>
+                        <Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.4</version>
+                <executions>
+                    <execution>
+                        <id>attach-descriptor</id>
+                        <goals>
+                            <goal>attach-descriptor</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-changes-plugin</artifactId>
+                <version>2.11</version>
+                <configuration>
+                    <issueLinkTemplate>https://bitbucket.org/asomov/snakeyaml/issues/%ISSUE%</issueLinkTemplate>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>changes-report</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-report-plugin</artifactId>
+                <version>2.19.1</version>
+                <configuration>
+                    <showSuccess>true</showSuccess>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <formats>
+                        <format>html</format>
+                        <format>xml</format>
+                    </formats>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <reportSets>
+                    <reportSet>
+                        <id>html</id>
+                        <configuration>
+                            <doctitle>API for ${project.name} ${project.version}</doctitle>
+                            <windowtitle>API for ${project.name} ${project.version}</windowtitle>
+                            <testDoctitle>Test API for ${project.name} ${project.version}</testDoctitle>
+                            <testWindowtitle>Test API for ${project.name} ${project.version}</testWindowtitle>
+                        </configuration>
+                        <reports>
+                            <report>javadoc</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+    <profiles>
+        <profile>
+            <id>toolchain</id>
+            <activation>
+                <file>
+                    <exists>${user.home}/.m2/toolchains.xml</exists>
+                </file>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-toolchains-plugin</artifactId>
+                        <version>1.1</version>
+                        <executions>
+                            <execution>
+                                <phase>validate</phase>
+                                <goals>
+                                    <goal>toolchain</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                        <toolchains>
+                            <jdk>
+                                <version>${maven.compiler.target}</version>
+                            </jdk>
+                        </toolchains>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+          <id>with-java8-tests</id>
+          <properties>
+            <maven.compiler.source>1.8</maven.compiler.source>
+            <maven.compiler.target>1.8</maven.compiler.target>
+          </properties>
+          <build>
+            <plugins>
+              <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.10</version>
+                <executions>
+                  <execution>
+                    <id>add-java8-test-source</id>
+                    <phase>generate-test-sources</phase>
+                    <goals>
+                      <goal>add-test-source</goal>
+                    </goals>
+                    <configuration>
+                      <sources>
+                        <source>${basedir}/src/test/java8/</source>
+                      </sources>
+                    </configuration>
+                  </execution>
+                </executions>
+              </plugin>
+            </plugins>
+          </build>
+        </profile>
+        <profile>
+            <id>release</id>
+            <activation>
+                <property>
+                    <name>performRelease</name>
+                    <value>true</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.6</version>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>findbugs</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>findbugs-maven-plugin</artifactId>
+                        <version>3.0.3</version>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-pmd-plugin</artifactId>
+                        <version>3.6</version>
+                    </plugin>
+                </plugins>
+            </build>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-jxr-plugin</artifactId>
+                        <version>2.5</version>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>findbugs-maven-plugin</artifactId>
+                        <version>3.0.3</version>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-pmd-plugin</artifactId>
+                        <version>3.6</version>
+                        <configuration>
+                            <linkXref>true</linkXref>
+                            <sourceEncoding>utf-8</sourceEncoding>
+                            <minimumTokens>100</minimumTokens>
+                            <targetJdk>1.5</targetJdk>
+                            <excludes>
+                                <exclude>**/external/*.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+        </profile>
+        <profile>
+            <id>android</id>
+            <properties>
+                <android.src>${project.build.directory}/android/src/</android.src>
+                <android.classes>${project.build.directory}/android/classes/</android.classes>
+                <android.test.classes>${project.build.directory}/android/test-classes/</android.test.classes>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <version>2.7</version>
+                        <executions>
+                            <execution>
+                                <id>copy-src-for-android</id>
+                                <phase>generate-sources</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${android.src}</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${basedir}/src/main/java</directory>
+                                            <filtering>false</filtering>
+                                            <excludes>
+                                                <exclude>org/yaml/snakeyaml/introspector/MethodProperty.java</exclude>
+                                            </excludes>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>copy-test-resources-for-android</id>
+                                <phase>process-test-resources</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${android.test.classes}</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>${basedir}/src/test/resources</directory>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-patch-plugin</artifactId>
+                        <version>1.2</version>
+                        <configuration>
+                            <patchDirectory>${basedir}/src/patches/android/</patchDirectory>
+                            <targetDirectory>${android.src}</targetDirectory>
+                            <skipApplication>false</skipApplication>
+                            <strip>4</strip>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <id>android-patches</id>
+                                <phase>process-sources</phase>
+                                <goals>
+                                    <goal>apply</goal>
+                                </goals>
+                                <configuration>
+                                    <patchTrackingFile>${project.build.directory}/android/patches-applied.txt
+                                    </patchTrackingFile>
+                                    <naturalOrderProcessing>true</naturalOrderProcessing>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-antrun-plugin</artifactId>
+                        <version>1.8</version>
+                        <executions>
+                            <execution>
+                                <id>build-for-android</id>
+                                <phase>compile</phase>
+                                <goals>
+                                    <goal>run</goal>
+                                </goals>
+                                <configuration>
+                                    <target>
+                                        <mkdir dir="${android.classes}" />
+                                        <mkdir dir="${android.test.classes}" />
+
+                                        <!-- compile patched sources -->
+                                        <javac srcdir="${android.src}" destdir="${android.classes}" classpath="${android.classes}" encoding="${project.build.sourceEncoding}" target="1.5" source="1.5" debug="true" includeantruntime="false" fork="true" />
+
+                                        <!-- compile test classes. Exclude some - not for BeanAccess.FIELD -->
+                                        <javac srcdir="${basedir}/src/test/java:${basedir}/src/test/resources" destdir="${android.test.classes}" classpath="${android.classes}:${android.test.classes}:${junit:junit:jar}:${org.springframework:spring:jar}:${org.apache.velocity:velocity:jar}:${joda-time:joda-time:jar}" encoding="${project.build.sourceEncoding}" target="1.5" source="1.5" debug="true" includeantruntime="false" fork="true">
+                                            <exclude name="org/yaml/snakeyaml/introspector/MethodPropertyTest.java" />
+                                            <exclude name="org/yaml/snakeyaml/representer/FilterPropertyToDumpTest.java" />
+                                            <exclude name="org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java" />
+                                            <exclude name="org/yaml/snakeyaml/issues/issue29/FlexibleScalarStylesInJavaBeanTest.java" />
+
+                                        </javac>
+                                    </target>
+
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>test-android</id>
+                                <phase>test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <classesDirectory>${android.classes}</classesDirectory>
+                                    <reportsDirectory>${project.build.directory}/android/surefire-reports
+                                    </reportsDirectory>
+                                    <testClassesDirectory>${android.test.classes}</testClassesDirectory>
+                                    <!--
+                                    We ignore test failures for android build at the moment.
+                                    Most of the FAILs are because of testing with "property" not "field" access.
+                                    But we still fail whole build if there are test errors.
+                                    -->
+                                    <testFailureIgnore>true</testFailureIgnore>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-jar-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>package-android-jar</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                                <configuration>
+                                    <classesDirectory>${android.classes}</classesDirectory>
+                                    <classifier>android</classifier>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
diff --git a/pom.yaml b/pom.yaml
new file mode 100644
index 0000000..2c2e39e
--- /dev/null
+++ b/pom.yaml
@@ -0,0 +1,360 @@
+modelVersion: 4.0.0
+groupId: org.yaml
+artifactId: snakeyaml
+version: 1.17-SNAPSHOT
+packaging: jar # TODO must be bundle
+properties: {project.scm.id: bitbucket, project.build.sourceEncoding: UTF-8}
+name: SnakeYAML
+description: YAML 1.1 parser and emitter for Java
+inceptionYear: '2008'
+url: http://www.snakeyaml.org
+issueManagement: {system: Bitbucket, url: 'https://bitbucket.org/asomov/snakeyaml/issues'}
+mailingLists:
+- {name: SnakeYAML developers and users List, post: snakeyaml-core@googlegroups.com}
+scm: {connection: 'scm:hg:http://bitbucket.org/asomov/snakeyaml', developerConnection: 'scm:hg:https://bitbucket.org/asomov/snakeyaml',
+  tag: HEAD, url: 'https://bitbucket.org/asomov/snakeyaml/src'}
+licenses:
+- {distribution: repo, name: 'Apache License, Version 2.0', url: 'http://www.apache.org/licenses/LICENSE-2.0.txt'}
+developers:
+- {email: public.somov@gmail.com, id: asomov, name: Andrey Somov}
+- {email: alexander.maslov@gmail.com, id: maslovalex, name: Alexander Maslov}
+- {email: jordanangold@gmail.com, id: Jordan, name: Jordan Angold}
+prerequisites: {maven: 3.3.1}
+dependencies:
+- {artifactId: junit, groupId: junit, optional: false, scope: test, type: jar, version: '4.12'}
+- {artifactId: spring, groupId: org.springframework, optional: false, scope: test,
+  type: jar, version: 2.5.6}
+- {artifactId: velocity, groupId: org.apache.velocity, optional: false, scope: test,
+  type: jar, version: 1.6.2}
+- {artifactId: joda-time, groupId: joda-time, optional: false, scope: test, type: jar,
+  version: '1.6'}
+distributionManagement:
+  repository: {id: sonatype-nexus-staging, layout: default, name: Nexus Release Repository,
+    uniqueVersion: true, url: 'https://oss.sonatype.org/service/local/staging/deploy/maven2/'}
+  snapshotRepository: {id: sonatype-nexus-snapshots, layout: default, name: Sonatype Nexus Snapshots,
+    uniqueVersion: false, url: 'https://oss.sonatype.org/content/repositories/snapshots/'}
+build:
+  pluginManagement:
+    plugins:
+    - {artifactId: maven-site-plugin, extensions: false, groupId: org.apache.maven.plugins,
+      inherited: true, version: '3.4'}
+  plugins:
+  - artifactId: maven-compiler-plugin
+    configuration: {source: '1.5', target: '1.5', encoding: '${project.build.sourceEncoding}'}
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: '3.3'
+  - artifactId: maven-surefire-plugin
+    configuration:
+      argLine: -Xmx512m
+      includes: {include: '**/*Test.java'}
+      excludes: {exclude: '**/ParallelTest.java'}
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: 2.18.1
+  - artifactId: maven-eclipse-plugin
+    configuration: {buildOutputDirectory: bin}
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: '2.10'
+  - artifactId: cobertura-maven-plugin
+    configuration:
+      check: {totalBranchRate: '80', totalLineRate: '95'}
+      formats: {format: xml}
+      instrumentation:
+        excludes: {exclude: org/yaml/snakeyaml/external/**}
+    executions:
+    - goals: [clean, check]
+      id: default
+      inherited: true
+      priority: 0
+    extensions: false
+    groupId: org.codehaus.mojo
+    inherited: true
+    version: '2.7'
+  - artifactId: maven-changes-plugin
+    executions:
+    - configuration: {failOnError: 'true'}
+      goals: [changes-validate]
+      id: validate-changes
+      inherited: true
+      phase: pre-site
+      priority: 0
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: '2.11'
+  - artifactId: maven-source-plugin
+    executions:
+    - goals: [jar]
+      id: default
+      inherited: true
+      priority: 0
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: '2.4'
+  - artifactId: maven-javadoc-plugin
+    configuration:
+      links: {link: 'http://java.sun.com/javase/6/docs/api/'}
+    executions:
+    - goals: [jar]
+      id: attach-javadocs
+      inherited: true
+      priority: 0
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: 2.10.3
+  - artifactId: maven-license-plugin
+    configuration:
+      header: src/etc/header.txt
+      quiet: 'false'
+      failIfMissing: 'true'
+      aggregate: 'false'
+      includes: {include: src/**/*.java}
+      excludes: {exclude: src/main/java/org/yaml/snakeyaml/external/**}
+      useDefaultExcludes: 'true'
+      useDefaultMapping: 'true'
+      strictCheck: 'true'
+      encoding: UTF-8
+    executions:
+    - goals: [format]
+      id: default
+      inherited: true
+      phase: site
+      priority: 0
+    extensions: false
+    groupId: com.mycila.maven-license-plugin
+    inherited: true
+    version: 1.10.b1
+  - artifactId: maven-bundle-plugin
+    configuration:
+      instructions: {_nouses: 'true', Export-Package: "!org.yaml.snakeyaml.external*,\n\
+          \                            org.yaml.snakeyaml.*;version=${project.version}",
+        Bundle-RequiredExecutionEnvironment: J2SE-1.5}
+    extensions: true
+    groupId: org.apache.felix
+    inherited: true
+    version: 2.5.4
+  - artifactId: maven-site-plugin
+    executions:
+    - goals: [attach-descriptor]
+      id: attach-descriptor
+      inherited: true
+      priority: 0
+    extensions: false
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: '3.4'
+profiles:
+- activation: {activeByDefault: false, jdk: '[1.8,)'}
+  build:
+    plugins:
+    - artifactId: maven-javadoc-plugin
+      configuration: {additionalparam: '-Xdoclint:none'}
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+    - artifactId: maven-site-plugin
+      configuration:
+        reportPlugins:
+          plugin:
+            groupId: org.apache.maven.plugins
+            artifactId: maven-javadoc-plugin
+            configuration: {additionalparam: '-Xdoclint:none'}
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+  id: jdk8
+  source: pom
+- build:
+    plugins:
+    - artifactId: maven-compiler-plugin
+      configuration: {source: '1.8', target: '1.8'}
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+    - artifactId: build-helper-maven-plugin
+      executions:
+      - configuration:
+          sources: {source: '${basedir}/src/test/java8/'}
+        goals: [add-test-source]
+        id: add-java8-test-source
+        inherited: true
+        phase: generate-test-sources
+        priority: 0
+      extensions: false
+      groupId: org.codehaus.mojo
+      inherited: true
+      version: '1.10'
+  id: with-java8-tests
+  source: pom
+- activation:
+    activeByDefault: false
+    property: {name: performRelease, value: 'true'}
+  build:
+    plugins:
+    - artifactId: maven-gpg-plugin
+      executions:
+      - goals: [sign]
+        id: sign-artifacts
+        inherited: true
+        phase: verify
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+      version: '1.6'
+  id: release
+  source: pom
+- build:
+    plugins:
+    - {artifactId: findbugs-maven-plugin, extensions: false, groupId: org.codehaus.mojo,
+      inherited: true, version: 3.0.2}
+    - {artifactId: maven-pmd-plugin, extensions: false, groupId: org.apache.maven.plugins,
+      inherited: true, version: '3.5'}
+  id: findbugs
+  reporting:
+    excludeDefaults: false
+    plugins:
+    - {artifactId: maven-jxr-plugin, groupId: org.apache.maven.plugins, inherited: true,
+      version: '2.5'}
+    - {artifactId: findbugs-maven-plugin, groupId: org.codehaus.mojo, inherited: true,
+      version: 3.0.0}
+    - artifactId: maven-pmd-plugin
+      configuration:
+        linkXref: 'true'
+        sourceEncoding: utf-8
+        minimumTokens: '100'
+        targetJdk: '1.5'
+        excludes: {exclude: '**/external/*.java'}
+      groupId: org.apache.maven.plugins
+      inherited: true
+      version: '3.4'
+  source: pom
+- build:
+    plugins:
+    - artifactId: maven-resources-plugin
+      executions:
+      - configuration:
+          outputDirectory: ${android.src}
+          resources:
+            resource:
+              directory: ${basedir}/src/main/java
+              filtering: 'false'
+              excludes: {exclude: org/yaml/snakeyaml/introspector/MethodProperty.java}
+        goals: [copy-resources]
+        id: copy-src-for-android
+        inherited: true
+        phase: generate-sources
+        priority: 0
+      - configuration:
+          outputDirectory: ${android.test.classes}
+          resources:
+            resource: {directory: '${basedir}/src/test/resources'}
+        goals: [copy-resources]
+        id: copy-test-resources-for-android
+        inherited: true
+        phase: process-test-resources
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+      version: '2.7'
+    - artifactId: maven-patch-plugin
+      configuration: {patchDirectory: '${basedir}/src/patches/android/', targetDirectory: '${android.src}',
+        skipApplication: 'false', strip: '4'}
+      executions:
+      - configuration: {patchTrackingFile: '${project.build.directory}/android/patches-applied.txt',
+          naturalOrderProcessing: 'true'}
+        goals: [apply]
+        id: android-patches
+        inherited: true
+        phase: process-sources
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+      version: '1.2'
+    - artifactId: maven-antrun-plugin
+      executions:
+      - configuration:
+          target:
+            javac: {}
+        goals: [run]
+        id: build-for-android
+        inherited: true
+        phase: compile
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+      version: '1.8'
+    - artifactId: maven-surefire-plugin
+      executions:
+      - configuration: {classesDirectory: '${android.classes}', reportsDirectory: '${project.build.directory}/android/surefire-reports',
+          testClassesDirectory: '${android.test.classes}', testFailureIgnore: 'true'}
+        goals: [test]
+        id: test-android
+        inherited: true
+        phase: test
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+    - artifactId: maven-jar-plugin
+      executions:
+      - configuration: {classesDirectory: '${android.classes}', classifier: android}
+        goals: [jar]
+        id: package-android-jar
+        inherited: true
+        phase: package
+        priority: 0
+      extensions: false
+      groupId: org.apache.maven.plugins
+      inherited: true
+  id: android
+  properties: {android.test.classes: '${project.build.directory}/android/test-classes/',
+    android.classes: '${project.build.directory}/android/classes/', android.src: '${project.build.directory}/android/src/'}
+  source: pom
+reporting:
+  excludeDefaults: false
+  plugins:
+  - artifactId: maven-changes-plugin
+    configuration: {issueLinkTemplate: 'https://bitbucket.org/asomov/snakeyaml/issues/%ISSUE%'}
+    groupId: org.apache.maven.plugins
+    inherited: true
+    reportSets:
+    - id: default
+      inherited: true
+      reports: [changes-report]
+    version: '2.11'
+  - artifactId: maven-surefire-report-plugin
+    configuration: {showSuccess: 'true'}
+    groupId: org.apache.maven.plugins
+    inherited: true
+    version: 2.18.1
+  - artifactId: cobertura-maven-plugin
+    configuration:
+      formats: {format: xml}
+    groupId: org.codehaus.mojo
+    inherited: true
+    version: '2.6'
+  - artifactId: maven-javadoc-plugin
+    groupId: org.apache.maven.plugins
+    inherited: true
+    reportSets:
+    - configuration: {doctitle: 'API for ${project.name} ${project.version}', windowtitle: 'API
+          for ${project.name} ${project.version}', testDoctitle: 'Test API for ${project.name}
+          ${project.version}', testWindowtitle: 'Test API for ${project.name} ${project.version}'}
+      id: html
+      inherited: true
+      reports: [javadoc]
+    version: 2.10.1
+
+
+
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
new file mode 100644
index 0000000..a927430
--- /dev/null
+++ b/src/changes/changes.xml
@@ -0,0 +1,964 @@
+<document xmlns="http://maven.apache.org/changes/1.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd">
+    <properties>
+        <title>YAML 1.1 parser and emitter</title>
+        <author email="public.somov@gmail.com">Andrey Somov</author>
+    </properties>
+    <body>
+        <release version="1.18-SNAPSHOT" date="in Mercurial" description="Maintenance">
+            <action dev="asomov" type="update" issue="332">
+                Add example for issue 332 (2016-02-24)
+            </action>
+            <action dev="asomov" type="update">
+                Build SnakeYAML for different JDKs with docker without building images (2016-02-22)
+            </action>
+            <action dev="asomov" type="update">
+                Update plugin versions (2016-02-19)
+            </action>
+        </release>
+        <release version="1.17" date="2016-02-19" description="Maintenance">
+            <action dev="maslovalex" type="fix" issue="318" due-to="Rog Sigal">
+                Use Thread.currentThread.getContextClassLoader in Class.forName instead of default one (2016-02-15)
+            </action>
+            <action dev="asomov" type="update" issue="329">
+                Add parameters to POM to change distribution server (2016-02-03)
+            </action>
+            <action dev="asomov" type="update" issue="326">
+                Relax some of the modifiers to make integration easier (2016-02-02)
+            </action>
+            <action dev="maslovalex" type="update" issue="310">
+                Make use of private/protected constructors for Scalars and 'immutable' objects.
+                Added 'src/test/java8' for Java8 specific tests (requires -Pwith-java8-tests) (2016-01-07)
+            </action>
+            <action dev="asomov" type="update" issue="320">
+                Better support to customise BaseConstructor. Make 'composer' field and constructDocument()
+                method protected (2015-10-03)
+            </action>
+            <action dev="asomov" type="fix" issue="306">
+                Better UUID support (2015-09-23)
+            </action>
+            <action dev="asomov" type="add">
+                Use Polyglot Maven: add support for YAML POM (2015-09-10)
+            </action>
+            <action dev="asomov" type="fix" issue="314">
+                Provide ability to customize anchor names (2015-08-25)
+            </action>
+        </release>
+        <release version="1.16" date="2015-08-18" description="Maintenance">
+            <action dev="asomov" type="fix" issue="308">
+                Provide Docker image for testing under different Java 8 (2015-06-19)
+            </action>
+            <action dev="asomov" type="fix" issue="302">
+                Convert binary byte[] to String when it matches the JavaBean property (2015-05-26)
+            </action>
+            <action dev="asomov" type="update">
+                The source code migrated to Bitbucket. Old commits (older then 6 years) have
+                been removed. (2015-05-20)
+            </action>
+            <action dev="asomov" type="fix"  issue="212">
+                Fix a typo in an error message in ScannerImpl (2015-05-19)
+            </action>
+            <action dev="asomov" type="fix"  issue="209">
+                Do not print special characters in the error message (2015-04-16)
+            </action>
+            <action dev="asomov" type="fix"  issue="199">
+                Evaluate implementations for duplicate mapping keys.
+            </action>
+        </release>
+        <release version="1.15" date="2015-02-19" description="Maintenance">
+            <action dev="asomov" type="update">
+                Apply FindBugs and PMD recommendations (2015-02-16)
+            </action>
+            <action dev="asomov" type="update">
+                Added split lines option to DumperOptions to allow line splitting to be disabled. (2015-02-08)
+            </action>
+            <action dev="asomov" type="update">
+                Use Maven 3 and update plugin versions (2015-02-05)
+            </action>
+            <action dev="asomov" type="update"  issue="205">
+                Add test to clarify iOS emoji character in the surrogate range (2015-02-03)
+            </action>
+            <action dev="asomov" type="fix"  issue="201">
+                Fix grammar error in exception message (2014-11-18)
+            </action>
+        </release>
+        <release version="1.14" date="2014-08-29" description="Maintenance">
+            <action dev="maslovalex" type="fix"  issue="197">
+                Load JavaBean with fields using custom constructors (2014-08-18)
+            </action>
+            <action dev="asomov" type="fix"  issue="192">
+                Drop support for "Value Key Language-Independent Type" and '=' a standard character (2014-05-22)
+            </action>
+            <action dev="maslovalex" type="fix"  issue="191">
+                Improve error message for invalid YAML document (2014-05-21)
+            </action>
+            <action dev="asomov" type="fix"  issue="188">
+                Improve error message for invalid YAML document (2014-03-26)
+            </action>
+            <action dev="asomov" type="fix"  issue="183">
+                Support Number class (2013-11-07)
+            </action>
+             <action dev="asomov" type="fix"  issue="182">
+                Double.POSITIVE_INFINITY applied to float fields (2013-11-07)
+             </action>
+        </release>
+        <release version="1.13" date="2013-08-29" description="Maintenance">
+             <action dev="asomov" type="fix"  issue="178">
+                OSGi: Specify version for exported packages (2013-06-27)
+             </action>
+             <action dev="asomov" type="fix"  issue="177">
+                Improve error report while parsing a JavaBean (2013-05-14)
+             </action>
+              <action dev="Jordan" type="fix"  issue="135">
+                Arrays of primitives are now fully supported (2013-04-16)
+             </action>
+             <action dev="asomov" type="fix"  issue="174">
+                Duplicate anchors in an input document should not be an error (2013-04-03)
+             </action>
+             <action dev="asomov" type="fix"  issue="172">
+                Using a locale with minimum number fraction digits breaks anchor generation (2013-03-30)
+             </action>
+             <action dev="asomov" type="fix"  issue="171">
+                Use more generic generics in BaseRepresenter (2013-03-30)
+             </action>
+        </release>
+        <release version="1.12" date="2013-03-02" description="Maintenance">
+             <action dev="asomov" type="update">
+                The build is using JDK 1.6 (2013-03-02)
+             </action>
+             <action dev="asomov" type="fix"  issue="169">
+                Make Constructor.typeDefinitions protected to be more flexible (2013-02-18)
+             </action>
+             <action dev="asomov" type="remove">
+                Remove file AUTHORS because it does not reflect the actual situation (2012-11-09)
+             </action>
+             <action dev="asomov" type="update">
+                Improve the error message when a TAB character starts a token (2012-11-06)
+             </action>
+        </release>
+        <release version="1.11" date="2012-09-29" description="Maintenance">
+             <action dev="asomov" type="fix"  issue="158">
+                Fix issue 158: improve support for 32-bit characters (UTF-16 surrogate pairs) (2012-09-29)
+             </action>
+             <action dev="asomov" type="fix"  issue="146">
+                Fix issue 146: empty tags should not force explicit document start (2012-09-29)
+             </action>
+             <action dev="asomov" type="fix"  issue="156">
+                Fix issue 156: setSkipMissingProperties fails for non-scalar values (2012-09-05)
+             </action>
+             <action dev="asomov" type="fix"  issue="155">
+                Fix issue 155: SnakeYAML must always eat its own food - a YAML document created by itself must
+                be read without exceptions (2012-09-04)
+             </action>
+             <action dev="asomov" type="update"  issue="154">
+                Fix issue 154: Add option to skip missing properties when deserializing YAML into Java object (2012-07-25)
+             </action>
+             <action dev="asomov" type="update">
+                Add a test for issue 150 (2012-06-17)
+             </action>
+             <action dev="asomov" type="update">
+                Add a test for issue 151 (2012-06-14)
+             </action>
+             <action dev="asomov" type="fix">
+                Fix issue 149: Directives are no longer lost between documents (2012-06-10)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor: use Version enum instead of Array of Integers.
+                This is done to simplify fixing issue 149 (2012-06-09)
+             </action>
+             <action dev="asomov" type="fix">
+                Add tests for issue 148  (2012-06-07)
+             </action>
+             <action dev="asomov" type="fix">
+                Fix issue 147: Serialized representation of character U+FFFD causes exception on deserialization  (2012-06-05)
+             </action>
+             <action dev="asomov" type="fix">
+                Fix issue 145: exception.getMessage() must show the line number as exception.toString() does (2012-04-03)
+             </action>
+             <action dev="maslovalex" type="fix">
+                Fix issue 144: improve type inference for Compact Object Notation (2012-03-16)
+             </action>
+             <action dev="maslovalex" type="add">
+                Improve Android support
+             </action>
+        </release>
+        <release version="1.10" date="2012-02-08" description="Maintenance">
+             <action dev="asomov" type="update">
+                Fix issue 141: TimeZone is configurable in DumperOptions (2012-02-03)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor with PMD: minor non-functional improvements (2012-01-28)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor with PMD: Avoid unused method parameter 'index' in
+                Serializer and Emitter (2012-01-28)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor with PMD: Composer - Avoid unused method parameter 'index' in
+                'Composer.composeNode(Node parent, Object index)''. It was used
+                in PyYAML for kind of XPath for YAML, but it was not imported from PyYAML (2012-01-28)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor Emitter: the SPACE mutable static field could be changed by malicious code or by accident.
+                Boxed value is unboxed and then immediately reboxed (2012-01-28)
+             </action>
+             <action dev="asomov" type="remove">
+                Refactor with FindBugs: remove unused ScalarAnalysis.allowDoubleQuoted (2012-01-28)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor with FindBugs: do not rely on default encoding (2012-01-28)
+             </action>
+             <action dev="asomov" type="update">
+                Refactor: apply FindBugs recommendations (2012-01-28)
+             </action>
+             <action dev="maslovalex" type="fix">
+                Fix issue 139: merge should use last key in map (2012-01-24)
+             </action>
+             <action dev="asomov" type="fix">
+                Fix issue 136: tabs are allowed in plain scalars. This is a deviation from PyYAML (2012-01-11)
+             </action>
+             <action dev="asomov" type="add" issue="138">
+                Expose internal data of ReaderException (2012-01-06)
+             </action>
+             <action dev="asomov" type="fix" issue="137">
+                Respect supplementary characters (2012-01-06)
+             </action>
+             <action dev="asomov" type="add">
+                Use http://mercurial.selenic.com/wiki/EolExtension to force LF  as line separator
+                for all operating systems (2011-12-20)
+             </action>
+             <action dev="asomov" type="add">
+                Add a test for issue 136 (2011-12-14)
+             </action>
+              <action dev="asomov" type="remove">
+                Deprecate the DumperOptions.calculateScalarStyle() method because it was introduced as a quick
+                fix for issue 29. Now it should not be required at all (because of the fix for issue 66),
+                or it should be implemented in the Representer (in RepresentString) (2011-10-10)
+             </action>
+             <action dev="asomov" type="fix">
+                Fix issue 66: literal scalar style is used by default for multiline scalars (2011-10-10)
+             </action>
+             <action dev="asomov" type="add">
+                An example added: how to dump multiline literal scalars (2011-10-04)
+             </action>
+             <action dev="asomov" type="add">
+                An example added: how to dump recursive object for issue 133 (2011-09-14)
+             </action>
+             <action dev="asomov" type="add">
+                A test added for issue 132 (2011-09-13)
+             </action>
+             <action dev="asomov" type="update">
+                Finish 1.9 release (2011-08-15)
+             </action>
+        </release>
+        <release version="1.9" date="2011-08-15" description="Introduce Yaml.loadAs() and Yaml.dumpAs() methods">
+            <action dev="asomov" type="update">
+                Add a test to prove that SnakeYAML is not affected by the problem reported for libyaml
+                at http://pyyaml.org/ticket/196 (2011-07-28)
+            </action>
+            <action dev="asomov" type="fix" issue="128">
+                Since timestamp contains ':' characters it is dumped with single quoted scalar style
+                in the flow context. The single quoted scalar style causes to dump the explicit tag.
+                In the block context the plain scalar can be used and the tag is not required. It may cause
+                unpredictable behaviour if the tag is required. See the comments in JodaTimeExampleTest (2011-07-25)
+            </action>
+            <action dev="asomov" type="fix" issue="130">
+                Fix scientific notation inconsistency in the YAML 1.1 specification:
+                scalar '8e-06' should be parsed as a Double (2011-07-24)
+            </action>
+            <action dev="asomov" type="remove" issue="127">
+                Do not allow to override BaseRepresenter.representData(Object data) because
+                users should instead implement Represent interface (2011-07-21)
+            </action>
+            <action dev="asomov" type="remove" issue="124">
+                Deprecate DumperOptions.explicitRoot (2011-07-20)
+            </action>
+            <action dev="asomov" type="add" issue="124">
+                Add Yaml.dumpAs(Object, Tag.MAP, FlowStyle) and Yaml.dumpAsMap(Object) methods. JavaBeanDumper is marked as deprecated (2011-07-16)
+            </action>
+            <action dev="asomov" type="add" issue="127">
+                Add example to show how to dump a custom class (2011-07-12)
+            </action>
+            <action dev="asomov" type="add" issue="129">
+                Add Yaml.serialize(Node) low level method to the public API (2011-07-14)
+            </action>
+            <action dev="asomov" type="add" issue="129">
+                Add Yaml.represent(Object) low level method to the public API (2011-07-14)
+            </action>
+            <action dev="asomov" type="add" issue="125">
+                Add support for Maven 3 via 'm3' profile (2011-07-10)
+            </action>
+            <action dev="asomov" type="remove" issue="124">
+                Remove deprecated JavaBeanParser (2011-07-05)
+            </action>
+            <action dev="asomov" type="remove" issue="124">
+                Remove redundant JavaBeanDumper.classTags set (2011-07-03)
+            </action>
+            <action dev="asomov" type="add" issue="124">
+                Add Yaml.loadAs() methods. JavaBeanLoader is marked as deprecated (2011-07-03)
+            </action>
+            <action dev="asomov" type="remove" issue="124">
+                Remove TypeDescription.root property to prepare issue 124. This is a minor backwards incompatible change.
+                Now instead of setting as root, the TypeDescription must be passed to the Contructor's constructor
+                to be taken as the root definition (2011-07-03)
+            </action>
+            <action dev="asomov" type="fix" issue="121" due-to="Jaromir">
+                Fix: close files in tests to avoid a possible file handle limit (2011-06-09)
+            </action>
+            <action dev="asomov" type="fix" issue="116" due-to="Jim Peterson">
+                Fix: Improved support for empty JavaBeans (2011-06-09)
+            </action>
+            <action dev="asomov" type="fix" issue="112" due-to="Lethargish">
+                Fix: Improved support for parameterised types in collections (2011-05-25)
+            </action>
+            <action dev="asomov" type="fix" issue="115" due-to="elkniwt">
+                Fix: parameterised JavaBeans fail to load and dump because they are treated as Maps (2011-05-16)
+            </action>
+            <action dev="asomov" type="fix" issue="114" due-to="gileadis">
+                Fix: Do not remove root tags of JavaBeans when it is not explicitly requested (2011-04-20)
+            </action>
+            <action dev="asomov" type="fix" issue="111" due-to="JordanAngold">
+                Fix: Long escaped tag URI sequences throw BufferOverflowException (2011-03-03)
+            </action>
+            <action dev="asomov" type="fix" issue="110" due-to="dmitry.s.mamonov">
+                Fix: introduce a package for external libraries and move there the 64Coder
+                and the Google's URL encoder (2011-02-24)
+            </action>
+            <action dev="asomov" type="fix" issue="109" due-to="cjalmeida">
+                Fix: ancient years must be dumped with leading zeros (2011-02-19)
+            </action>
+            <action dev="asomov" type="remove" due-to="JordanAngold">
+                Remove unused code in Constructor: Modifier.isAbstract() is not needed any more (2011-02-18)
+            </action>
+            <action dev="JordanAngold" type="fix" issue="108">
+                Enum's name property shall be dumped instead of the 'toString()' output (2011-02-16)
+            </action>
+        </release>
+        <release version="1.8" date="2011-02-15" description="Performance improvement">
+            <action dev="asomov" type="add">
+                Add example to howto Wiki:
+                How_to_substitute_object_in_YAML_document_with_a_custom_object (2011-01-27)
+            </action>
+            <action dev="asomov" type="update">
+                When the YAML document to be loaded is provided as String parse it directly
+                without making a Reader first (2011-01-23)
+            </action>
+            <action dev="asomov" type="fix" issue="106">
+                Immutable data structures in StreamReader allow to share the same buffer for all
+                the Mark instances. It makes 'withMarkContext' setting redundant (2011-01-19)
+            </action>
+            <action dev="maslovalex" type="update" issue="100">
+                Merge JavaBean properties when an explicit tag is provided (2011-01-11)
+            </action>
+            <action dev="asomov" type="update" issue="99">
+                Add an example for escaping line breaks in binary content (2011-01-03)
+            </action>
+            <action dev="asomov" type="update" issue="97">
+                Propose a solution for JavaBeans to support SortedSet property when it is encoded
+                as a sequence (2010-11-24)
+            </action>
+            <action dev="asomov" type="update" issue="59">
+                Simplify the way how the order of JavaBean properties is specified. Introduce
+                PropertyUtils.createPropertySet() method to be overridden when a specific order
+                is expected (2010-11-23)
+            </action>
+            <action dev="maslovalex" type="fix" issue="95">
+                Fix: Loading of generic collections with Array parameter(s). (2010-11-16)
+            </action>
+            <action dev="asomov" type="update" issue="94">
+                Add ChangeRuntimeClassTest as an example how to change a class for a global tag (2010-11-05)
+            </action>
+            <action dev="asomov" type="update">
+                Inner objects in Constructor become protected to be more flexible when Constructor
+                is expended (2010-10-03)
+            </action>
+            <action dev="asomov" type="update" issue="91">
+                Apply www.snakeyaml.org domain name (2010-09-20)
+            </action>
+            <action dev="asomov" type="fix" issue="90">
+                Move Base64Coder into another package to keep a separate copyright statement.
+                Base64Coder is left unchanged (2010-09-19)
+            </action>
+            <action dev="asomov" type="fix" issue="69">
+                Iterable should not be serialised as sequence (2010-09-16)
+            </action>
+            <action dev="asomov" type="update">
+                Introduce 'fast' Maven profile to quickly build cobertura reports (2010-09-16)
+            </action>
+            <action dev="asomov" type="update" issue="89">
+                Fix: Specify plugin versions in POM (2010-09-15)
+            </action>
+            <action dev="maslovalex" type="fix" issue="88">
+                Fix: Custom tag erased when referenced from generic collection (2010-09-15)
+            </action>
+            <action dev="asomov" type="update">
+                Minor refactoring in Emitter to improve performance: save calls to Constant.has() (2010-09-13)
+            </action>
+            <action dev="maslovalex" type="update">
+                Minor refactorings in Emitter to improve performance: create less Strings [r9185e0b3] (2010-09-10)
+            </action>
+            <action dev="asomov" type="update" issue="79">
+                Introduce LoaderOptions to be able to specify configuration while loading (2010-09-03)
+            </action>
+            <action dev="asomov" type="fix" issue="81">
+                Representer.representJavaBeanProperty() is given the wrong tag. Instead of the property tag,
+                the tag for the JavaBean itself is provided. (2010-09-01)
+            </action>
+            <action dev="asomov" type="update">
+                Rename JvmDetector into GenericsBugDetector (2010-08-31)
+            </action>
+            <action dev="asomov" type="fix" issue="80" due-to="SebastienRainville">
+                Fix: Timestamp is not parsed properly when milliseconds start with 0 (2010-08-24)
+            </action>
+            <action dev="maslovalex" type="update" issue="79">
+                Context for error reporting consumes a lot of resources (2010-08-21)
+            </action>
+            <action dev="asomov" type="remove">
+                Cleanup unused code in deprecated Loader and Dumper (2010-08-13)
+            </action>
+        </release>
+        <release version="1.7" date="2010-08-12" description="Simplify public API (drop Loader and Dumper)">
+            <action dev="asomov" type="update">
+                Eclipse does not run JUnit 4 tests when they are launched for the whole project (2010-08-11)
+            </action>
+            <action dev="maslovalex" type="update" issue="55">
+                Share PropertyUtils if not explicitly set in Constructor or Representer
+                (BeanAccess.FIELD works properly when JavaBean is identified by a root tag) (2010-08-11)
+            </action>
+            <action dev="asomov" type="update">
+                Create 1.7 Release Candidate 1 (2010-08-11)
+            </action>
+            <action dev="asomov" type="update" issue="77">
+                Simplify public API: Drop Dumper (2010-08-06)
+            </action>
+            <action dev="asomov" type="update" issue="77">
+                Simplify public API: Drop Loader (2010-08-05)
+            </action>
+            <action dev="asomov" type="update" issue="75" due-to="jon.p.hermes">
+                Add examples to create scalars that match custom regular expression:
+                CustomImplicitResolverTest, CustomBeanResolverTest (2010-08-03)
+            </action>
+            <action dev="asomov" type="fix" issue="74" due-to="Kevin Menard">
+                Do not use redundant tags for arrays which are JavaBean properties. (2010-07-21)
+            </action>
+            <action dev="asomov" type="update">
+                RecursiveSetTest proves that it is possible to construct a recursive set (2010-07-20)
+            </action>
+            <action dev="asomov" type="add" issue="73" due-to="birnbuazn">
+                Provide sequence support for loading java.util.Set. Also provide an example
+                to serialise a java.util.Set as a sequence. (2010-07-19)
+            </action>
+            <action dev="asomov" type="add" issue="72" due-to="birnbuazn">
+                Support java.util.Collection as a parent for List and Set (2010-07-09)
+            </action>
+            <action dev="maslovalex" type="add" issue="55" due-to="birnbuazn">
+                Allow direct field access bypassing setters and getters. Empty constructor
+                is required to support 2-step construction (2010-07-02)
+            </action>
+            <action dev="asomov" type="update" issue="69">
+                Serialise Iterator and Iterable as sequences (2010-06-25)
+            </action>
+            <action dev="asomov" type="update" due-to="maslovalex">
+                Change error message when 'No suitable constructor with N arguments found for class' (2010-06-23)
+            </action>
+            <action dev="asomov" type="add" due-to="Antony Stubbs">
+                Add JodaTime example (2010-06-04)
+            </action>
+            <action dev="asomov" type="add" issue="67" due-to="Manuel Sugawara">
+                Add possibility to create a Tag out of an URI (2010-05-31)
+            </action>
+            <action dev="asomov" type="update">
+                URLDecoder.decode() does not fail when UTF-8 is invalid. Use
+                CodingErrorAction.REPORT to implement the failure (2010-05-21)
+            </action>
+            <action dev="maslovalex" type="update">
+                Fix generic collections which contain other collections (2010-05-18)
+            </action>
+            <action dev="asomov" type="fix" issue="67" due-to="Manuel Sugawara">
+                Fix: java classes containing non-ASCII characters in names are
+                incorrectly encoded (2010-05-14)
+            </action>
+            <action dev="asomov" type="fix" issue="65" due-to="lerch.johannes">
+                Fix: add checks for null arguments for JavaBeanDumper (2010-04-27)
+            </action>
+            <action dev="asomov" type="add">
+                Add a test to see how stack trace is serialised (2010-04-27)
+            </action>
+            <action dev="asomov" type="fix" issue="64" due-to="maxim.moschko">
+                ClassCastException in Representer when working with ParameterizedType (2010-04-25)
+            </action>
+            <action dev="asomov" type="update">
+                Improve toString() method for Node. Since scalars cannot be recursive
+                they can be printed (2010-04-15)
+            </action>
+            <action dev="maslovalex" type="fix" issue="63" due-to="Udo">
+                Refactor the way arrays are constructed (2010-04-15)
+            </action>
+            <action dev="asomov" type="fix" issue="62">
+                Add examples for dumping custom values for !!bool and !!null (2010-04-13)
+            </action>
+            <action dev="asomov" type="fix" issue="61">
+                Fix: ClassCastException when dumping generic bean (2010-04-11)
+            </action>
+            <action dev="asomov" type="fix" issue="59">
+                Provide an example for changing JavaBean properties order (2010-04-01)
+            </action>
+            <action dev="asomov" type="fix" issue="60">
+                Provide example for skipping null and empty collections (2010-03-29)
+            </action>
+            <action dev="asomov" type="fix" issue="58" due-to="jeff.caulfield">
+                JavaBeanDumper.dump throws NullPointerException on list property
+                with null element (2010-03-23)
+            </action>
+            <action dev="asomov" type="fix" issue="56" due-to="DZeiss">
+                Make constructors in SafeConstructor public (2010-03-16)
+            </action>
+            <action dev="asomov" type="update" due-to="David Bernard">
+                Releases and snapshots are available in the Sonatype Maven repository.
+                https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide
+            </action>
+            <action dev="obastard" type="fix" issue="53" due-to="obastard">
+                Enhancement for a pretty format that combines BLOCK and FLOW (2010-03-03)
+            </action>
+            <action dev="asomov" type="fix" issue="50" due-to="sualeh.fatehi">
+                Unable to dump JavaBean that inherits from a protected base class (2010-03-02)
+            </action>
+            <action dev="asomov" type="update">
+                Format source (2010-03-01)
+            </action>
+            <action dev="asomov" type="update">
+                Use Token.ID and Event.ID instead of just ID (2010-03-01)
+            </action>
+            <action dev="asomov" type="update">
+                Issue 50 fails in Eclipse but works with Maven (2010-03-01)
+            </action>
+        </release>
+        <release version="1.6" date="2010-02-26" description="introduce Tag class">
+            <action dev="asomov" type="update">
+                Release Candidate 2 is available (2010-02-24)
+            </action>
+            <action dev="asomov" type="fix" issue="47" due-to="obastard">
+                Don't dump read-only properties by default. DumperOptions gets a setting to
+                include read-only JavaBean properties.
+                This is no backwards compatible change (2010-02-19)
+            </action>
+            <action dev="asomov" type="fix" issue="49" due-to="obastard">
+                Support GregorianCalendar. Due to Daylight Saving Time parsing the timestamp with
+                a TimeZone cannot determine the exact time (2010-02-19)
+            </action>
+            <action dev="asomov" type="fix" issue="51" due-to="johann.Werner">
+                Some Unicode characters are wrongly replaced by \x{fffd} during
+                double quoted style dump (2010-02-15)
+            </action>
+            <action dev="asomov" type="fix" issue="48" due-to="obastard">
+                Introduce representJavaBeanProperty() method in Representer. The method
+                can be overridden to simplify custom JavaBean representation (2010-02-12)
+            </action>
+            <action dev="asomov" type="update">
+                Release Candidate 1 is available (2010-02-01)
+            </action>
+            <action dev="asomov" type="add">
+                Representer.representJavaBean() returns MappingNode (2010-01-26)
+            </action>
+            <action dev="asomov" type="add">
+                Add example of serialising static fields  (2010-01-26)
+            </action>
+            <action dev="asomov" type="add">
+                Add example of serialising java.io.File as scalar node for issue 46  (2010-01-25)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: introduce Chomping to avoid using null as value for Boolean.
+                Stay in line with Scala port where null is not allowed (2010-01-19)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use Event.ID enum instead of classes (2010-01-15)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use Token.ID enum instead of classes (2010-01-15)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use generic classes for DirectiveToken (2010-01-14)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: rename Reader to StreamReader to avoid name conflict with java.io.Reader (2010-01-13)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use StringBuilder instead of StringBuffer (2010-01-12)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: introduce Constant class to share String constants (2010-01-12)
+            </action>
+            <action dev="asomov" type="update">
+                Keep Tag.equals(String) to simplify transition to Tag class (2010-01-08)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: introduce Tag instead of Tags. Nodes use Tag class instead of String (2010-01-05)
+            </action>
+            <action dev="asomov" type="fix" issue="42" due-to="Artem">
+                BaseConstructor shall give more flexibility to choose a constructor at runtime (2010-01-08)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: introduce TagTuple instead of String[] (2010-01-04)
+            </action>
+            <action dev="asomov" type="fix" issue="40" due-to="sitrious">
+                Ignore tags when they are compatible with the runtime class (2010-01-04)
+            </action>
+            <action dev="asomov" type="add">
+                Add example to ignore unknown tags (2009-12-08)
+            </action>
+            <action dev="asomov" type="add">
+                Add Ruby example (2009-12-08)
+            </action>
+            <action dev="asomov" type="update">
+                Do not omit the tag for JavaBean properties when the tag is explicitly defined (2009-12-08)
+            </action>
+            <action dev="asomov" type="fix" issue="38" due-to="gchpaco">
+                Fix ID format for numbers over 999 (2009-12-05)
+            </action>
+            <action dev="asomov" type="fix" issue="29" due-to="grignaak">
+                Allow separate option in DumperOptions for long strings (2009-11-16)
+            </action>
+            <action dev="asomov" type="add">
+                JavaBeanDumper: add possibility to define a custom Representer (2009-11-25)
+            </action>
+            <action dev="asomov" type="fix" issue="36">
+                Introduce multi contructors (tag prefix). A family of tags may be processed
+                by a single constructor (2009-11-25)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor BaseConstructor: simplify second step for recursive structures (2009-11-25)
+            </action>
+            <action dev="asomov" type="add">
+                Add FilterPropertyToDumpTest to show how to filter JavaBean properties (2009-11-24)
+            </action>
+            <action dev="asomov" type="add">
+                Add FilterClassesConstructorTest to show how to filter created classes (2009-11-16)
+            </action>
+            <action dev="asomov" type="update" due-to="Stefan">
+                Improve JavaDoc (2009-11-12)
+            </action>
+            <action dev="asomov" type="add">
+                Add Velocity example (2009-11-03)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: rename Tuple to RecursiveTuple and hide it inside BaseConstructor (2009-11-03)
+            </action>
+        </release>
+        <release version="1.5" date="2009-10-30" description="Improve usage of generic collections in JavaBeans">
+            <action dev="asomov" type="fix" issue="27" due-to="Polyglot Maven team">
+                Extend Resolver to support custom implicit types (2009-10-27)
+            </action>
+            <action dev="asomov" type="update">
+                Performance improvement: use ArrayStack instead of Stack which extends Vector (2009-10-20)
+            </action>
+            <action dev="asomov" type="fix" issue="25" due-to="Benjamin Bentmann">
+                Improve usage of generic collections: while type erase makes no difference between
+                Class&lt; Foo&gt; and Class&lt; Bar&gt; at runtime, the information about generics is still
+                accessible via reflection from Method/Field. (2009-10-19)
+            </action>
+            <action dev="asomov" type="update">
+                Fix ConstructYamlObject: support recursive objects. Introduce 'resolved'
+                property for Nodes. This property supposed to help to distinguish explicit tag
+                from the resolved tag (2009-10-19)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use rootTag instead of rootType (for Class) in BaseConstructor. This is done to
+                solve the priority problem: normally explicit tag has more priority then runtime class but
+                for the root tag it is the other way around (2009-10-19)
+            </action>
+            <action dev="asomov" type="fix" issue="24" due-to="shrode">
+                Line numbers reported in Exceptions are Zero based, should be 1 based (2009-10-12)
+            </action>
+            <action dev="asomov" type="fix" issue="21" due-to="ashwin.jayaprakash">
+                Support arrays of reference types as JavaBean properties (2009-09-22)
+            </action>
+            <action dev="asomov" type="fix" issue="17" due-to="jcucurull">
+                Respect root tag for sequences (2009-09-04)
+            </action>
+            <action dev="asomov" type="fix" issue="18" due-to="creiniger">
+                SafeRepresenter respects custom tags for standard Java classes where standard tag has
+                more then one Java implementation available (Long, List, Map, Date etc) (2009-09-03)
+            </action>
+            <action dev="asomov" type="add">
+                Add possibility to define a custom Class Loader. (2009-09-01)
+            </action>
+            <action dev="asomov" type="fix">
+                Fixed an obscure scanner error not reported when there is no line break at the end
+                of the stream. The fix is imported from PyYAML 3.09 {ticket 118} (2009-08-31)
+            </action>
+            <action dev="asomov" type="fix" issue="16" due-to="infinity0x">
+                Cache JavaBean class properties. Tests show that the loading has become a few percents faster (2009-08-31)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce ArrayStack to use push() and pop() instead of standard (and too verbose)
+                'remove(size()-1)'  (2009-08-27)
+            </action>
+            <action dev="asomov" type="fix" issue="14" due-to="infinity0x">
+                Fix: ArrayList is more efficient than LinkedList  (2009-08-26)
+            </action>
+        </release>
+        <release version="1.4" date="2009-08-26" description="better support for loading immutable objects">
+            <action dev="asomov" type="update">
+                Apply Apache License Version 2.0 (2009-08-14)
+            </action>
+            <action dev="asomov" type="fix" issue="13" due-to="infinity0x">
+                Provide javadocs link to Sun Java API (2009-08-10)
+            </action>
+            <action dev="asomov" type="add">
+                Build 1.4 Release Candidate 1 (2009-08-07)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce Tags.getGlobalTagForClass() to simplify tag generation in custom constructors (2009-08-06)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: introduce ImplicitTuple (2009-08-06)
+            </action>
+            <action dev="asomov" type="fix" issue="11" due-to="infinity0x">
+                Fix: create a Java instance with the following priority to choose the class:
+                Explicit tag -> Runtime class (defined in JavaBean) -> implicit tag  (2009-08-06)
+            </action>
+            <action dev="asomov" type="fix" issue="9" due-to="wwagner4">
+                Fix: Bean with no property cannot be instantiated. This is implemented via better
+                support for immutable objects. Custom Constructor may be used when there are more
+                then 1 way to create an instance (2009-08-04)
+            </action>
+            <action dev="asomov" type="add">
+                Deliver possibility to load immutable instances with no global tags. Reflection for
+                constructor arguments is used to get the runtime classes (2009-08-04)
+            </action>
+            <action dev="asomov" type="update">
+                Use more informative error message when a JavaBean property cannot
+                be created (2009-08-02)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: Constructor is rewritten. Do not overwrite methods from BaseConstructor.
+                Instead introduce ConstructScalar, ConstructSequence, ConstructMapping (2009-07-31)
+            </action>
+            <action dev="asomov" type="update">
+                Change Maven repository path: groupId='org.yaml', artifactId='snakeyaml' (2009-07-31)
+            </action>
+            <action dev="asomov" type="fix" issue="10" due-to="derrick.rice">
+                Fix: dump omits JavaBean class name when used with an alias (2009-07-28)
+            </action>
+            <action dev="asomov" type="add">
+                Generate sources and Javadoc (2009-07-27)
+            </action>
+            <action dev="asomov" type="update">
+                Node does not have the value. It is delegated to the non-abstract classes (2009-07-27)
+            </action>
+            <action dev="asomov" type="add">
+                Extends JavaBeanDumper to allow skipping global tags inside type-safe collections.
+                Introduce method setMapTagForBean() (2009-07-22)
+            </action>
+            <action dev="asomov" type="add">
+                Add ConstructEmptyBeanTest to test JavaBean construction with no
+                properties in the YAML document(2009-07-22)
+            </action>
+            <action dev="asomov" type="remove">
+                Refactor: redesign tag management for JavaBeans in Representer.
+                Drop dynamic root tag concept (2009-07-22)
+            </action>
+            <action dev="asomov" type="remove">
+                Remove unused TypeDescription in Representer (2009-07-21)
+            </action>
+            <action dev="asomov" type="update">
+                Use NodeTuple instead of Node[] for mappings (2009-07-21)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce JavaBeanLoader and JavaBeanDumper. Deprecate JavaBeanParser (2009-07-21)
+            </action>
+            <action dev="asomov" type="fix" issue="8" due-to="Alan Gutierrez">
+                Fix: Representer was keeping state between invocations (2009-07-21)
+            </action>
+        </release>
+        <release version="1.3" date="2009-07-20" description="complete support for recursive objects">
+            <action dev="asomov" type="fix" issue="6" due-to="infinity0x">
+                Fix: values returned by System.identityHashCode() are not guaranteed to be unique (2009-07-14)
+            </action>
+            <action dev="asomov" type="add">
+                Add a simple test for Java Generics (BirdTest). Unfortunately it shows that some JVM
+                implementations do not recognise classes for JavaBean properties at runtime.
+                It leads to unnecessary global tags. See http://code.google.com/p/snakeyaml/wiki/Documentation#Generics
+                for details (2009-07-13)
+            </action>
+            <action dev="asomov" type="fix" issue="5" due-to="infinity0x">
+                Fix: set the "cause" field for MarkedYAMLException (2009-07-10)
+            </action>
+            <action dev="maslovalex" type="fix" issue="1">
+                Fix: Recursive objects are now fully supported (2009-07-09)
+            </action>
+            <action dev="asomov" type="add">
+                Add support for BigDecimal as a JavaBean property (2009-07-07)
+            </action>
+            <action dev="asomov" type="update">
+                Improve test coverage for Constructor. Allow construction of JavaBeans
+                with only setter without the corresponding getter (2009-07-07)
+            </action>
+            <action dev="asomov" type="add">
+                Add a test to check the proper report for IOException (2009-07-03)
+            </action>
+            <action dev="asomov" type="fix" issue="3" due-to="infinity0x">
+                Fix: represent proper tags for JavaBeans when they are not the root of the YAML
+                document but a member of a collection (2009-07-03)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: run PMD and apply some of the recommendations (2009-06-18)
+            </action>
+            <action dev="asomov" type="add" issue="1">
+                Create an issue for Recursive objects to be remembered (2009-06-08)
+            </action>
+            <action dev="asomov" type="update">
+                Migrate project hosting from Assembla to Google code (2009-06-08)
+            </action>
+            <action dev="asomov" type="fix" due-to="Magne">
+                Fix: null as a JavaBean property was not handled properly (2009-06-04)
+            </action>
+            <action dev="asomov" type="update">
+                Validate changes.xml file (2009-05-25)
+            </action>
+            <action dev="asomov" type="fix" due-to="Magne">
+                Fix ticket 40 in Assembla: getting an error when javabean contains java.sql.Timestamp fields (2009-05-25)
+            </action>
+        </release>
+        <release version="1.2" date="2009-04-27" description="expose low-level API">
+            <action dev="asomov" type="add">
+                Add 'Yaml.parse()' method which return Events to support low level YAML processing (2009-04-20)
+            </action>
+            <action dev="asomov" type="add" due-to="Bob Jalex">
+                Introduce LineBreak.getPlatformLineBreak (ticket 5 in Assembla) (2009-04-18)
+            </action>
+            <action dev="asomov" type="update" due-to="Bob Jalex">
+                 Rename LineBreak.LINUX to LineBreak.UNIX (ticket 5 in Assembla) (2009-04-18)
+            </action>
+            <action dev="asomov" type="add">
+                Add 'Yaml.compose()' methods which return Nodes to support YEdit (2009-04-17)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: rename enums in DumperOptions to make the names consistent (2009-04-07)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use Character instead of char primitive for style in Emitter (2009-04-07)
+            </action>
+            <action dev="asomov" type="add">
+                Add possibility to parse all scalars as Strings (2009-03-30)
+            </action>
+            <action dev="asomov" type="update">
+                Merge changeset 347 from PyYAML (2009-03-30)
+            </action>
+            <action dev="asomov" type="fix">
+                Respect DumperOptions with a custom Representer (2009-03-18)
+            </action>
+            <action dev="asomov" type="fix">
+                Represent TAB as '\t' instead of '(9' in the error message (2009-03-17)
+            </action>
+        </release>
+        <release version="1.1" date="2009-03-14" description="improve performance and test coverage">
+            <action dev="asomov" type="add">
+                Introduce JavaBeanParser (2009-03-14)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce DumperOptions.Version enum (2009-03-13)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce DumperOptions.LineBreak enum (2009-03-10)
+            </action>
+            <action dev="asomov" type="update">
+                Use byte[] for binary type. (2009-03-09)
+            </action>
+            <action dev="asomov" type="update">
+                Restore Regular Expressions in Resolver. Ragel gives only 5% performance increase.
+                Fix a bug in Resolver with expanded regular expressions which caused the
+                performance problem. (2009-03-06)
+            </action>
+            <action dev="asomov" type="add">
+                Better Spring support: it is now possible to create a constructor with a String
+                as the class name. (2009-03-05)
+            </action>
+            <action dev="asomov" type="update">
+                Throw an exception when the same Loader or Dumper instance is shared between
+                different Yaml instances. Because they are statefull it is not Thread-safe. (2009-03-05)
+            </action>
+            <action dev="asomov" type="add">
+                Add possibility to set a meaningful name for Yaml instance to be shown in toString(). (2009-03-05)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: declare classes which are not expected to be extended as final. (2009-03-04)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use 'final' keyword to identify immutable fields. (2009-03-04)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: do not use 'final' keyword for local variables. (2009-03-04)
+            </action>
+            <action dev="asomov" type="fix">
+                Fix: respect implicit resolvers with 'null' as a first character. (2009-03-02)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use Character instead of String as a key for implicit resolvers. (2009-03-02)
+            </action>
+            <action dev="asomov" type="add">
+                Use Ragel instead of Regular Expressions for implicit types. (2009-03-02)
+            </action>
+            <action dev="asomov" type="fix" due-to="Christophe Desguez">
+                Fix ticket #4 (in Assembla): java.sql.Date not handled. (2009-02-28)
+            </action>
+             <action dev="asomov" type="add">
+                Introduce DumperOptions.DefaultFlowStyle enum (2009-02-24)
+            </action>
+            <action dev="asomov" type="add">
+                Introduce DumperOptions.DefaultScalarStyle enum (2009-02-24)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use 'switch' with Enum instead of multiple 'if' statements to distinguish nodes (2009-02-19)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: use Enum instead of String as NodeId (2009-02-19)
+            </action>
+        </release>
+        <release version="1.0.1" date="2009-02-18" description="implement Enum support">
+            <action dev="asomov" type="fix">
+                Do not emit anchors for Enum (2009-02-18)
+            </action>
+            <action dev="asomov" type="fix">
+                Enum as a JavaBean property (when the Enum class is implicitly defined) does
+                not need tags for both loading and dumping (2009-02-17)
+            </action>
+            <action dev="asomov" type="fix">
+                Enum is emitted as a scalar node (2009-02-17)
+            </action>
+            <action dev="asomov" type="fix" due-to="James Nissel">
+                Enum is parsed as a scalar node or as a JavaBean property (2009-02-17)
+            </action>
+            <action dev="asomov" type="update">
+                Refactor: for performance ScannerImpl.stalePossibleSimpleKeys() does not copy key Set (2009-02-10)
+            </action>
+            <action dev="asomov" type="update">
+                By default allowUnicode=true. If it is necessary to escape Unicode use
+                DumperOptions.setAllowUnicode(false) (2009-02-09)
+            </action>
+            <action dev="asomov" type="add">
+                Implement allowUnicode setting (to escape Unicode characters on non UTF-8 terminals) (2009-02-09)
+            </action>
+            <action dev="asomov" type="add">
+                Add possibility to specify tags for dumping (2009-02-09)
+            </action>
+            <action dev="asomov" type="update">
+                Rename getExpRoot to getExplicitRoot to conform with
+                standard JavaBean naming convention (2009-02-09)
+            </action>
+            <action dev="asomov" type="update">
+                Rename explictStart and explicitEnd to standard setters to conform with
+                standard JavaBean naming convention (2009-02-09)
+            </action>
+            <action dev="asomov" type="fix">
+                Add possibility to specify a line break (2009-02-09)
+            </action>
+        </release>
+        <release version="1.0" date="2009-02-06" description="final 1.0 release">
+            <action dev="asomov" type="add">
+                Deliver first release (2009-02-06)
+            </action>
+        </release>
+    </body>
+</document>
diff --git a/src/etc/Eclipse-format.xml b/src/etc/Eclipse-format.xml
new file mode 100644
index 0000000..4dcc5a8
--- /dev/null
+++ b/src/etc/Eclipse-format.xml
@@ -0,0 +1,264 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<profiles version="11">
+<profile kind="CodeFormatterProfile" name="SnakeYAML" version="11">
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.source" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="100"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.assertIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.compiler.problem.enumIdentifier" value="error"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.compiler.compliance" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_binary_expression" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.wrap_before_binary_operator" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.compiler.codegen.targetPlatform" value="1.5"/>
+<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="0"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
+<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
+</profile>
+</profiles>
\ No newline at end of file
diff --git a/src/etc/announcement.msg b/src/etc/announcement.msg
new file mode 100644
index 0000000..a1cd13f
--- /dev/null
+++ b/src/etc/announcement.msg
@@ -0,0 +1,39 @@
+From: Andrey Somov <public.somov@gmail.com>

+To: yaml-core@lists.sourceforge.net

+Subject: [ANN] SnakeYAML-1.17 final is available

+

+==========================

+ Announcing SnakeYAML-1.17

+==========================

+

+A new release of SnakeYAML is now available:

+

+    http://www.snakeyaml.org

+

+This release delivers minor changes and bug fixes.

+

+The complete list of changes is here: https://bitbucket.org/asomov/snakeyaml/wiki/Changes

+

+Resources

+==========

+

+SnakeYAML homepage: http://www.snakeyaml.org

+SnakeYAML documentation: https://bitbucket.org/asomov/snakeyaml/wiki/Home

+

+JAR package: http://repo2.maven.org/maven2/org/yaml/snakeyaml/1.17/snakeyaml-1.17.jar

+

+YAML homepage: http://yaml.org/

+YAML-core mailing list: http://lists.sourceforge.net/lists/listinfo/yaml-core

+

+About SnakeYAML

+================

+

+SnakeYAML is a YAML parser and emitter for Java 6.

+

+SnakeYAML features a complete YAML 1.1 parser.

+SnakeYAML is applicable for a broad range of tasks from complex

+configuration files to object serialization and persistence.

+

+Copyright

+==========

+SnakeYAML is released under the Apache License Version 2.0

diff --git a/src/etc/header.txt b/src/etc/header.txt
new file mode 100644
index 0000000..8db00b5
--- /dev/null
+++ b/src/etc/header.txt
@@ -0,0 +1,13 @@
+Copyright (c) 2008, http://www.snakeyaml.org
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/src/main/java/org/yaml/snakeyaml/DumperOptions.java b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
new file mode 100644
index 0000000..0762198
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
@@ -0,0 +1,405 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.serializer.AnchorGenerator;
+import org.yaml.snakeyaml.serializer.NumberAnchorGenerator;
+
+public class DumperOptions {
+    /**
+     * YAML provides a rich set of scalar styles. Block scalar styles include
+     * the literal style and the folded style; flow scalar styles include the
+     * plain style and two quoted styles, the single-quoted style and the
+     * double-quoted style. These styles offer a range of trade-offs between
+     * expressive power and readability.
+     *
+     * @see <a href="http://yaml.org/spec/1.1/#id903915">Chapter 9. Scalar
+     *      Styles</a>
+     * @see <a href="http://yaml.org/spec/1.1/#id858081">2.3. Scalars</a>
+     */
+    public enum ScalarStyle {
+        DOUBLE_QUOTED(Character.valueOf('"')), SINGLE_QUOTED(Character.valueOf('\'')), LITERAL(
+                Character.valueOf('|')), FOLDED(Character.valueOf('>')), PLAIN(null);
+        private Character styleChar;
+
+        private ScalarStyle(Character style) {
+            this.styleChar = style;
+        }
+
+        public Character getChar() {
+            return styleChar;
+        }
+
+        @Override
+        public String toString() {
+            return "Scalar style: '" + styleChar + "'";
+        }
+
+        public static ScalarStyle createStyle(Character style) {
+            if (style == null) {
+                return PLAIN;
+            } else {
+                switch (style) {
+                case '"':
+                    return DOUBLE_QUOTED;
+                case '\'':
+                    return SINGLE_QUOTED;
+                case '|':
+                    return LITERAL;
+                case '>':
+                    return FOLDED;
+                default:
+                    throw new YAMLException("Unknown scalar style character: " + style);
+                }
+            }
+        }
+    }
+
+    /**
+     * Block styles use indentation to denote nesting and scope within the
+     * document. In contrast, flow styles rely on explicit indicators to denote
+     * nesting and scope.
+     *
+     * @see <a href="http://www.yaml.org/spec/current.html#id2509255">3.2.3.1.
+     *      Node Styles (http://yaml.org/spec/1.1)</a>
+     */
+    public enum FlowStyle {
+        FLOW(Boolean.TRUE), BLOCK(Boolean.FALSE), AUTO(null);
+
+        private Boolean styleBoolean;
+
+        private FlowStyle(Boolean flowStyle) {
+            styleBoolean = flowStyle;
+        }
+
+        public Boolean getStyleBoolean() {
+            return styleBoolean;
+        }
+
+        @Override
+        public String toString() {
+            return "Flow style: '" + styleBoolean + "'";
+        }
+    }
+
+    /**
+     * Platform dependent line break.
+     */
+    public enum LineBreak {
+        WIN("\r\n"), MAC("\r"), UNIX("\n");
+
+        private String lineBreak;
+
+        private LineBreak(String lineBreak) {
+            this.lineBreak = lineBreak;
+        }
+
+        public String getString() {
+            return lineBreak;
+        }
+
+        @Override
+        public String toString() {
+            return "Line break: " + name();
+        }
+
+        public static LineBreak getPlatformLineBreak() {
+            String platformLineBreak = System.getProperty("line.separator");
+            for (LineBreak lb : values()) {
+                if (lb.lineBreak.equals(platformLineBreak)) {
+                    return lb;
+                }
+            }
+            return LineBreak.UNIX;
+        }
+    }
+
+    /**
+     * Specification version. Currently supported 1.0 and 1.1
+     */
+    public enum Version {
+        V1_0(new Integer[] { 1, 0 }), V1_1(new Integer[] { 1, 1 });
+
+        private Integer[] version;
+
+        private Version(Integer[] version) {
+            this.version = version;
+        }
+
+        public int major() { return version[0]; }
+        public int minor() { return version[1]; }
+
+        public String getRepresentation() {
+            return version[0] + "." + version[1];
+        }
+
+        @Override
+        public String toString() {
+            return "Version: " + getRepresentation();
+        }
+    }
+
+    private ScalarStyle defaultStyle = ScalarStyle.PLAIN;
+    private FlowStyle defaultFlowStyle = FlowStyle.AUTO;
+    private boolean canonical = false;
+    private boolean allowUnicode = true;
+    private boolean allowReadOnlyProperties = false;
+    private int indent = 2;
+    private int indicatorIndent = 0;
+    private int bestWidth = 80;
+    private boolean splitLines = true;
+    private LineBreak lineBreak = LineBreak.UNIX;
+    private boolean explicitStart = false;
+    private boolean explicitEnd = false;
+    private TimeZone timeZone = null;
+
+    private Version version = null;
+    private Map<String, String> tags = null;
+    private Boolean prettyFlow = false;
+    private AnchorGenerator anchorGenerator = new NumberAnchorGenerator(0);
+
+    public boolean isAllowUnicode() {
+        return allowUnicode;
+    }
+
+    /**
+     * Specify whether to emit non-ASCII printable Unicode characters.
+     * The default value is true.
+     * When set to false then printable non-ASCII characters (Cyrillic, Chinese etc)
+     * will be not printed but escaped (to support ASCII terminals)
+     *
+     * @param allowUnicode
+     *            if allowUnicode is false then all non-ASCII characters are
+     *            escaped
+     */
+    public void setAllowUnicode(boolean allowUnicode) {
+        this.allowUnicode = allowUnicode;
+    }
+
+    public ScalarStyle getDefaultScalarStyle() {
+        return defaultStyle;
+    }
+
+    /**
+     * Set default style for scalars. See YAML 1.1 specification, 2.3 Scalars
+     * (http://yaml.org/spec/1.1/#id858081)
+     *
+     * @param defaultStyle
+     *            set the style for all scalars
+     */
+    public void setDefaultScalarStyle(ScalarStyle defaultStyle) {
+        if (defaultStyle == null) {
+            throw new NullPointerException("Use ScalarStyle enum.");
+        }
+        this.defaultStyle = defaultStyle;
+    }
+
+    public void setIndent(int indent) {
+        if (indent < Emitter.MIN_INDENT) {
+            throw new YAMLException("Indent must be at least " + Emitter.MIN_INDENT);
+        }
+        if (indent > Emitter.MAX_INDENT) {
+            throw new YAMLException("Indent must be at most " + Emitter.MAX_INDENT);
+        }
+        this.indent = indent;
+    }
+
+    public int getIndent() {
+        return this.indent;
+    }
+
+    public void setIndicatorIndent(int indicatorIndent) {
+        if (indicatorIndent < 0) {
+            throw new YAMLException("Indicator indent must be non-negative.");
+        }
+        if (indicatorIndent > Emitter.MAX_INDENT - 1) {
+            throw new YAMLException("Indicator indent must be at most Emitter.MAX_INDENT-1: " + (Emitter.MAX_INDENT - 1));
+        }
+        this.indicatorIndent = indicatorIndent;
+    }
+
+    public int getIndicatorIndent() {
+        return this.indicatorIndent;
+    }
+
+    public void setVersion(Version version) {
+        this.version = version;
+    }
+
+    public Version getVersion() {
+        return this.version;
+    }
+
+    /**
+     * Force the emitter to produce a canonical YAML document.
+     *
+     * @param canonical
+     *            true produce canonical YAML document
+     */
+    public void setCanonical(boolean canonical) {
+        this.canonical = canonical;
+    }
+
+    public boolean isCanonical() {
+        return this.canonical;
+    }
+
+    /**
+     * Force the emitter to produce a pretty YAML document when using the flow
+     * style.
+     *
+     * @param prettyFlow
+     *            true produce pretty flow YAML document
+     */
+    public void setPrettyFlow(boolean prettyFlow) {
+        this.prettyFlow = prettyFlow;
+    }
+
+    public boolean isPrettyFlow() {
+        return this.prettyFlow;
+    }
+
+    /**
+     * Specify the preferred width to emit scalars. When the scalar
+     * representation takes more then the preferred with the scalar will be
+     * split into a few lines. The default is 80.
+     *
+     * @param bestWidth
+     *            the preferred width for scalars.
+     */
+    public void setWidth(int bestWidth) {
+        this.bestWidth = bestWidth;
+    }
+
+    public int getWidth() {
+        return this.bestWidth;
+    }
+
+    /**
+     * Specify whether to split lines exceeding preferred width for
+     * scalars. The default is true.
+     *
+     * @param splitLines
+     *            whether to split lines exceeding preferred width for scalars.
+     */
+    public void setSplitLines(boolean splitLines) {
+        this.splitLines = splitLines;
+    }
+
+    public boolean getSplitLines() {
+        return this.splitLines;
+    }
+
+    public LineBreak getLineBreak() {
+        return lineBreak;
+    }
+
+    public void setDefaultFlowStyle(FlowStyle defaultFlowStyle) {
+        if (defaultFlowStyle == null) {
+            throw new NullPointerException("Use FlowStyle enum.");
+        }
+        this.defaultFlowStyle = defaultFlowStyle;
+    }
+
+    public FlowStyle getDefaultFlowStyle() {
+        return defaultFlowStyle;
+    }
+
+    /**
+     * Specify the line break to separate the lines. It is platform specific:
+     * Windows - "\r\n", old MacOS - "\r", Unix - "\n". The default value is the
+     * one for Unix.
+     */
+    public void setLineBreak(LineBreak lineBreak) {
+        if (lineBreak == null) {
+            throw new NullPointerException("Specify line break.");
+        }
+        this.lineBreak = lineBreak;
+    }
+
+    public boolean isExplicitStart() {
+        return explicitStart;
+    }
+
+    public void setExplicitStart(boolean explicitStart) {
+        this.explicitStart = explicitStart;
+    }
+
+    public boolean isExplicitEnd() {
+        return explicitEnd;
+    }
+
+    public void setExplicitEnd(boolean explicitEnd) {
+        this.explicitEnd = explicitEnd;
+    }
+
+    public Map<String, String> getTags() {
+        return tags;
+    }
+
+    // TODO should use Tag ???
+    public void setTags(Map<String, String> tags) {
+        this.tags = tags;
+    }
+
+    /**
+     * Report whether read-only JavaBean properties (the ones without setters)
+     * should be included in the YAML document
+     *
+     * @return false when read-only JavaBean properties are not emitted
+     */
+    public boolean isAllowReadOnlyProperties() {
+        return allowReadOnlyProperties;
+    }
+
+    /**
+     * Set to true to include read-only JavaBean properties (the ones without
+     * setters) in the YAML document. By default these properties are not
+     * included to be able to parse later the same JavaBean.
+     *
+     * @param allowReadOnlyProperties
+     *            - true to dump read-only JavaBean properties
+     */
+    public void setAllowReadOnlyProperties(boolean allowReadOnlyProperties) {
+        this.allowReadOnlyProperties = allowReadOnlyProperties;
+    }
+
+    public TimeZone getTimeZone() {
+        return timeZone;
+    }
+
+    /**
+     * Set the timezone to be used for Date. If set to <code>null</code> UTC is
+     * used.
+     */
+    public void setTimeZone(TimeZone timeZone) {
+        this.timeZone = timeZone;
+    }
+
+
+    public AnchorGenerator getAnchorGenerator() {
+        return anchorGenerator;
+    }
+
+    public void setAnchorGenerator(AnchorGenerator anchorGenerator) {
+        this.anchorGenerator = anchorGenerator;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/TypeDescription.java b/src/main/java/org/yaml/snakeyaml/TypeDescription.java
new file mode 100644
index 0000000..4c38307
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/TypeDescription.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Provides additional runtime information necessary to create a custom Java
+ * instance.
+ */
+public final class TypeDescription {
+    private final Class<? extends Object> type;
+    private Tag tag;
+    private Map<String, Class<? extends Object>> listProperties;
+    private Map<String, Class<? extends Object>> keyProperties;
+    private Map<String, Class<? extends Object>> valueProperties;
+
+    public TypeDescription(Class<? extends Object> clazz, Tag tag) {
+        this.type = clazz;
+        this.tag = tag;
+        listProperties = new HashMap<String, Class<? extends Object>>();
+        keyProperties = new HashMap<String, Class<? extends Object>>();
+        valueProperties = new HashMap<String, Class<? extends Object>>();
+    }
+
+    public TypeDescription(Class<? extends Object> clazz, String tag) {
+        this(clazz, new Tag(tag));
+    }
+
+    public TypeDescription(Class<? extends Object> clazz) {
+        this(clazz, (Tag) null);
+    }
+
+    /**
+     * Get tag which shall be used to load or dump the type (class).
+     * 
+     * @return tag to be used. It may be a tag for Language-Independent Types
+     *         (http://www.yaml.org/type/)
+     */
+    public Tag getTag() {
+        return tag;
+    }
+
+    /**
+     * Set tag to be used to load or dump the type (class).
+     * 
+     * @param tag
+     *            local or global tag
+     */
+    public void setTag(Tag tag) {
+        this.tag = tag;
+    }
+
+    public void setTag(String tag) {
+        setTag(new Tag(tag));
+    }
+
+    /**
+     * Get represented type (class)
+     * 
+     * @return type (class) to be described.
+     */
+    public Class<? extends Object> getType() {
+        return type;
+    }
+
+    /**
+     * Specify that the property is a type-safe <code>List</code>.
+     * 
+     * @param property
+     *            name of the JavaBean property
+     * @param type
+     *            class of List values
+     */
+    public void putListPropertyType(String property, Class<? extends Object> type) {
+        listProperties.put(property, type);
+    }
+
+    /**
+     * Get class of List values for provided JavaBean property.
+     * 
+     * @param property
+     *            property name
+     * @return class of List values
+     */
+    public Class<? extends Object> getListPropertyType(String property) {
+        return listProperties.get(property);
+    }
+
+    /**
+     * Specify that the property is a type-safe <code>Map</code>.
+     * 
+     * @param property
+     *            property name of this JavaBean
+     * @param key
+     *            class of keys in Map
+     * @param value
+     *            class of values in Map
+     */
+    public void putMapPropertyType(String property, Class<? extends Object> key,
+            Class<? extends Object> value) {
+        keyProperties.put(property, key);
+        valueProperties.put(property, value);
+    }
+
+    /**
+     * Get keys type info for this JavaBean
+     * 
+     * @param property
+     *            property name of this JavaBean
+     * @return class of keys in the Map
+     */
+    public Class<? extends Object> getMapKeyType(String property) {
+        return keyProperties.get(property);
+    }
+
+    /**
+     * Get values type info for this JavaBean
+     * 
+     * @param property
+     *            property name of this JavaBean
+     * @return class of values in the Map
+     */
+    public Class<? extends Object> getMapValueType(String property) {
+        return valueProperties.get(property);
+    }
+
+    @Override
+    public String toString() {
+        return "TypeDescription for " + getType() + " (tag='" + getTag() + "')";
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/Yaml.java b/src/main/java/org/yaml/snakeyaml/Yaml.java
new file mode 100644
index 0000000..5c4559c
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/Yaml.java
@@ -0,0 +1,661 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.constructor.BaseConstructor;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.emitter.Emitable;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+import org.yaml.snakeyaml.representer.Representer;
+import org.yaml.snakeyaml.resolver.Resolver;
+import org.yaml.snakeyaml.serializer.Serializer;
+
+/**
+ * Public YAML interface. Each Thread must have its own instance.
+ */
+public class Yaml {
+    protected final Resolver resolver;
+    private String name;
+    protected BaseConstructor constructor;
+    protected Representer representer;
+    protected DumperOptions dumperOptions;
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     */
+    public Yaml() {
+        this(new Constructor(), new Representer(), new DumperOptions(), new Resolver());
+    }
+
+    /**
+     * Create Yaml instance.
+     * 
+     * @param dumperOptions
+     *            DumperOptions to configure outgoing objects
+     */
+    public Yaml(DumperOptions dumperOptions) {
+        this(new Constructor(), new Representer(), dumperOptions);
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param representer
+     *            Representer to emit outgoing objects
+     */
+    public Yaml(Representer representer) {
+        this(new Constructor(), representer);
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param constructor
+     *            BaseConstructor to construct incoming documents
+     */
+    public Yaml(BaseConstructor constructor) {
+        this(constructor, new Representer());
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param constructor
+     *            BaseConstructor to construct incoming documents
+     * @param representer
+     *            Representer to emit outgoing objects
+     */
+    public Yaml(BaseConstructor constructor, Representer representer) {
+        this(constructor, representer, new DumperOptions());
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param representer
+     *            Representer to emit outgoing objects
+     * @param dumperOptions
+     *            DumperOptions to configure outgoing objects
+     */
+    public Yaml(Representer representer, DumperOptions dumperOptions) {
+        this(new Constructor(), representer, dumperOptions, new Resolver());
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param constructor
+     *            BaseConstructor to construct incoming documents
+     * @param representer
+     *            Representer to emit outgoing objects
+     * @param dumperOptions
+     *            DumperOptions to configure outgoing objects
+     */
+    public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions) {
+        this(constructor, representer, dumperOptions, new Resolver());
+    }
+
+    /**
+     * Create Yaml instance. It is safe to create a few instances and use them
+     * in different Threads.
+     * 
+     * @param constructor
+     *            BaseConstructor to construct incoming documents
+     * @param representer
+     *            Representer to emit outgoing objects
+     * @param dumperOptions
+     *            DumperOptions to configure outgoing objects
+     * @param resolver
+     *            Resolver to detect implicit type
+     */
+    public Yaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions,
+            Resolver resolver) {
+        if (!constructor.isExplicitPropertyUtils()) {
+            constructor.setPropertyUtils(representer.getPropertyUtils());
+        } else if (!representer.isExplicitPropertyUtils()) {
+            representer.setPropertyUtils(constructor.getPropertyUtils());
+        }
+        this.constructor = constructor;
+        representer.setDefaultFlowStyle(dumperOptions.getDefaultFlowStyle());
+        representer.setDefaultScalarStyle(dumperOptions.getDefaultScalarStyle());
+        representer.getPropertyUtils().setAllowReadOnlyProperties(
+                dumperOptions.isAllowReadOnlyProperties());
+        representer.setTimeZone(dumperOptions.getTimeZone());
+        this.representer = representer;
+        this.dumperOptions = dumperOptions;
+        this.resolver = resolver;
+        this.name = "Yaml:" + System.identityHashCode(this);
+    }
+
+    /**
+     * Serialize a Java object into a YAML String.
+     * 
+     * @param data
+     *            Java object to be Serialized to YAML
+     * @return YAML String
+     */
+    public String dump(Object data) {
+        List<Object> list = new ArrayList<Object>(1);
+        list.add(data);
+        return dumpAll(list.iterator());
+    }
+
+    /**
+     * Produce the corresponding representation tree for a given Object.
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing
+     *      Overview</a>
+     * @param data
+     *            instance to build the representation tree for
+     * @return representation tree
+     */
+    public Node represent(Object data) {
+        return representer.represent(data);
+    }
+
+    /**
+     * Serialize a sequence of Java objects into a YAML String.
+     * 
+     * @param data
+     *            Iterator with Objects
+     * @return YAML String with all the objects in proper sequence
+     */
+    public String dumpAll(Iterator<? extends Object> data) {
+        StringWriter buffer = new StringWriter();
+        dumpAll(data, buffer, null);
+        return buffer.toString();
+    }
+
+    /**
+     * Serialize a Java object into a YAML stream.
+     * 
+     * @param data
+     *            Java object to be serialized to YAML
+     * @param output
+     *            stream to write to
+     */
+    public void dump(Object data, Writer output) {
+        List<Object> list = new ArrayList<Object>(1);
+        list.add(data);
+        dumpAll(list.iterator(), output, null);
+    }
+
+    /**
+     * Serialize a sequence of Java objects into a YAML stream.
+     * 
+     * @param data
+     *            Iterator with Objects
+     * @param output
+     *            stream to write to
+     */
+    public void dumpAll(Iterator<? extends Object> data, Writer output) {
+        dumpAll(data, output, null);
+    }
+
+    private void dumpAll(Iterator<? extends Object> data, Writer output, Tag rootTag) {
+        Serializer serializer = new Serializer(new Emitter(output, dumperOptions), resolver,
+                dumperOptions, rootTag);
+        try {
+            serializer.open();
+            while (data.hasNext()) {
+                Node node = representer.represent(data.next());
+                serializer.serialize(node);
+            }
+            serializer.close();
+        } catch (IOException e) {
+            throw new YAMLException(e);
+        }
+    }
+
+    /**
+     * <p>
+     * Serialize a Java object into a YAML string. Override the default root tag
+     * with <code>rootTag</code>.
+     * </p>
+     * 
+     * <p>
+     * This method is similar to <code>Yaml.dump(data)</code> except that the
+     * root tag for the whole document is replaced with the given tag. This has
+     * two main uses.
+     * </p>
+     * 
+     * <p>
+     * First, if the root tag is replaced with a standard YAML tag, such as
+     * <code>Tag.MAP</code>, then the object will be dumped as a map. The root
+     * tag will appear as <code>!!map</code>, or blank (implicit !!map).
+     * </p>
+     * 
+     * <p>
+     * Second, if the root tag is replaced by a different custom tag, then the
+     * document appears to be a different type when loaded. For example, if an
+     * instance of MyClass is dumped with the tag !!YourClass, then it will be
+     * handled as an instance of YourClass when loaded.
+     * </p>
+     * 
+     * @param data
+     *            Java object to be serialized to YAML
+     * @param rootTag
+     *            the tag for the whole YAML document. The tag should be Tag.MAP
+     *            for a JavaBean to make the tag disappear (to use implicit tag
+     *            !!map). If <code>null</code> is provided then the standard tag
+     *            with the full class name is used.
+     * @param flowStyle
+     *            flow style for the whole document. See Chapter 10. Collection
+     *            Styles http://yaml.org/spec/1.1/#id930798. If
+     *            <code>null</code> is provided then the flow style from
+     *            DumperOptions is used.
+     * 
+     * @return YAML String
+     */
+    public String dumpAs(Object data, Tag rootTag, FlowStyle flowStyle) {
+        FlowStyle oldStyle = representer.getDefaultFlowStyle();
+        if (flowStyle != null) {
+            representer.setDefaultFlowStyle(flowStyle);
+        }
+        List<Object> list = new ArrayList<Object>(1);
+        list.add(data);
+        StringWriter buffer = new StringWriter();
+        dumpAll(list.iterator(), buffer, rootTag);
+        representer.setDefaultFlowStyle(oldStyle);
+        return buffer.toString();
+    }
+
+    /**
+     * <p>
+     * Serialize a Java object into a YAML string. Override the default root tag
+     * with <code>Tag.MAP</code>.
+     * </p>
+     * <p>
+     * This method is similar to <code>Yaml.dump(data)</code> except that the
+     * root tag for the whole document is replaced with <code>Tag.MAP</code> tag
+     * (implicit !!map).
+     * </p>
+     * <p>
+     * Block Mapping is used as the collection style. See 10.2.2. Block Mappings
+     * (http://yaml.org/spec/1.1/#id934537)
+     * </p>
+     * 
+     * @param data
+     *            Java object to be serialized to YAML
+     * @return YAML String
+     */
+    public String dumpAsMap(Object data) {
+        return dumpAs(data, Tag.MAP, FlowStyle.BLOCK);
+    }
+
+    /**
+     * Serialize the representation tree into Events.
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
+     * @param data
+     *            representation tree
+     * @return Event list
+     */
+    public List<Event> serialize(Node data) {
+        SilentEmitter emitter = new SilentEmitter();
+        Serializer serializer = new Serializer(emitter, resolver, dumperOptions, null);
+        try {
+            serializer.open();
+            serializer.serialize(data);
+            serializer.close();
+        } catch (IOException e) {
+            throw new YAMLException(e);
+        }
+        return emitter.getEvents();
+    }
+
+    private static class SilentEmitter implements Emitable {
+        private List<Event> events = new ArrayList<Event>(100);
+
+        public List<Event> getEvents() {
+            return events;
+        }
+
+        public void emit(Event event) throws IOException {
+            events.add(event);
+        }
+    }
+
+    /**
+     * Parse the only YAML document in a String and produce the corresponding
+     * Java object. (Because the encoding in known BOM is not respected.)
+     * 
+     * @param yaml
+     *            YAML data to load from (BOM must not be present)
+     * @return parsed object
+     */
+    public Object load(String yaml) {
+        return loadFromReader(new StreamReader(yaml), Object.class);
+    }
+
+    /**
+     * Parse the only YAML document in a stream and produce the corresponding
+     * Java object.
+     * 
+     * @param io
+     *            data to load from (BOM is respected and removed)
+     * @return parsed object
+     */
+    public Object load(InputStream io) {
+        return loadFromReader(new StreamReader(new UnicodeReader(io)), Object.class);
+    }
+
+    /**
+     * Parse the only YAML document in a stream and produce the corresponding
+     * Java object.
+     * 
+     * @param io
+     *            data to load from (BOM must not be present)
+     * @return parsed object
+     */
+    public Object load(Reader io) {
+        return loadFromReader(new StreamReader(io), Object.class);
+    }
+
+    /**
+     * Parse the only YAML document in a stream and produce the corresponding
+     * Java object.
+     * 
+     * @param <T>
+     *            Class is defined by the second argument
+     * @param io
+     *            data to load from (BOM must not be present)
+     * @param type
+     *            Class of the object to be created
+     * @return parsed object
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T loadAs(Reader io, Class<T> type) {
+        return (T) loadFromReader(new StreamReader(io), type);
+    }
+
+    /**
+     * Parse the only YAML document in a String and produce the corresponding
+     * Java object. (Because the encoding in known BOM is not respected.)
+     * 
+     * @param <T>
+     *            Class is defined by the second argument
+     * @param yaml
+     *            YAML data to load from (BOM must not be present)
+     * @param type
+     *            Class of the object to be created
+     * @return parsed object
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T loadAs(String yaml, Class<T> type) {
+        return (T) loadFromReader(new StreamReader(yaml), type);
+    }
+
+    /**
+     * Parse the only YAML document in a stream and produce the corresponding
+     * Java object.
+     * 
+     * @param <T>
+     *            Class is defined by the second argument
+     * @param input
+     *            data to load from (BOM is respected and removed)
+     * @param type
+     *            Class of the object to be created
+     * @return parsed object
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T loadAs(InputStream input, Class<T> type) {
+        return (T) loadFromReader(new StreamReader(new UnicodeReader(input)), type);
+    }
+
+    private Object loadFromReader(StreamReader sreader, Class<?> type) {
+        Composer composer = new Composer(new ParserImpl(sreader), resolver);
+        constructor.setComposer(composer);
+        return constructor.getSingleData(type);
+    }
+
+    /**
+     * Parse all YAML documents in a String and produce corresponding Java
+     * objects. The documents are parsed only when the iterator is invoked.
+     * 
+     * @param yaml
+     *            YAML data to load from (BOM must not be present)
+     * @return an iterator over the parsed Java objects in this String in proper
+     *         sequence
+     */
+    public Iterable<Object> loadAll(Reader yaml) {
+        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
+        constructor.setComposer(composer);
+        Iterator<Object> result = new Iterator<Object>() {
+            public boolean hasNext() {
+                return constructor.checkData();
+            }
+
+            public Object next() {
+                return constructor.getData();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+        return new YamlIterable(result);
+    }
+
+    private static class YamlIterable implements Iterable<Object> {
+        private Iterator<Object> iterator;
+
+        public YamlIterable(Iterator<Object> iterator) {
+            this.iterator = iterator;
+        }
+
+        public Iterator<Object> iterator() {
+            return iterator;
+        }
+    }
+
+    /**
+     * Parse all YAML documents in a String and produce corresponding Java
+     * objects. (Because the encoding in known BOM is not respected.) The
+     * documents are parsed only when the iterator is invoked.
+     * 
+     * @param yaml
+     *            YAML data to load from (BOM must not be present)
+     * @return an iterator over the parsed Java objects in this String in proper
+     *         sequence
+     */
+    public Iterable<Object> loadAll(String yaml) {
+        return loadAll(new StringReader(yaml));
+    }
+
+    /**
+     * Parse all YAML documents in a stream and produce corresponding Java
+     * objects. The documents are parsed only when the iterator is invoked.
+     * 
+     * @param yaml
+     *            YAML data to load from (BOM is respected and ignored)
+     * @return an iterator over the parsed Java objects in this stream in proper
+     *         sequence
+     */
+    public Iterable<Object> loadAll(InputStream yaml) {
+        return loadAll(new UnicodeReader(yaml));
+    }
+
+    /**
+     * Parse the first YAML document in a stream and produce the corresponding
+     * representation tree. (This is the opposite of the represent() method)
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id859333">Figure 3.1. Processing
+     *      Overview</a>
+     * @param yaml
+     *            YAML document
+     * @return parsed root Node for the specified YAML document
+     */
+    public Node compose(Reader yaml) {
+        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
+        constructor.setComposer(composer);
+        return composer.getSingleNode();
+    }
+
+    /**
+     * Parse all YAML documents in a stream and produce corresponding
+     * representation trees.
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
+     * @param yaml
+     *            stream of YAML documents
+     * @return parsed root Nodes for all the specified YAML documents
+     */
+    public Iterable<Node> composeAll(Reader yaml) {
+        final Composer composer = new Composer(new ParserImpl(new StreamReader(yaml)), resolver);
+        constructor.setComposer(composer);
+        Iterator<Node> result = new Iterator<Node>() {
+            public boolean hasNext() {
+                return composer.checkNode();
+            }
+
+            public Node next() {
+                return composer.getNode();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+        return new NodeIterable(result);
+    }
+
+    private static class NodeIterable implements Iterable<Node> {
+        private Iterator<Node> iterator;
+
+        public NodeIterable(Iterator<Node> iterator) {
+            this.iterator = iterator;
+        }
+
+        public Iterator<Node> iterator() {
+            return iterator;
+        }
+    }
+
+    /**
+     * Add an implicit scalar detector. If an implicit scalar value matches the
+     * given regexp, the corresponding tag is assigned to the scalar.
+     * 
+     * @param tag
+     *            tag to assign to the node
+     * @param regexp
+     *            regular expression to match against
+     * @param first
+     *            a sequence of possible initial characters or null (which means
+     *            any).
+     */
+    public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
+        resolver.addImplicitResolver(tag, regexp, first);
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Get a meaningful name. It simplifies debugging in a multi-threaded
+     * environment. If nothing is set explicitly the address of the instance is
+     * returned.
+     * 
+     * @return human readable name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Set a meaningful name to be shown in toString()
+     * 
+     * @param name
+     *            human readable name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Parse a YAML stream and produce parsing events.
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id859333">Processing Overview</a>
+     * @param yaml
+     *            YAML document(s)
+     * @return parsed events
+     */
+    public Iterable<Event> parse(Reader yaml) {
+        final Parser parser = new ParserImpl(new StreamReader(yaml));
+        Iterator<Event> result = new Iterator<Event>() {
+            public boolean hasNext() {
+                return parser.peekEvent() != null;
+            }
+
+            public Event next() {
+                return parser.getEvent();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+        return new EventIterable(result);
+    }
+
+    private static class EventIterable implements Iterable<Event> {
+        private Iterator<Event> iterator;
+
+        public EventIterable(Iterator<Event> iterator) {
+            this.iterator = iterator;
+        }
+
+        public Iterator<Event> iterator() {
+            return iterator;
+        }
+    }
+
+    public void setBeanAccess(BeanAccess beanAccess) {
+        constructor.getPropertyUtils().setBeanAccess(beanAccess);
+        representer.getPropertyUtils().setBeanAccess(beanAccess);
+    }
+
+}
diff --git a/src/main/java/org/yaml/snakeyaml/composer/Composer.java b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
new file mode 100644
index 0000000..f8223c2
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/composer/Composer.java
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.composer;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.NodeEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+/**
+ * Creates a node graph from parser events.
+ * <p>
+ * Corresponds to the 'Compose' step as described in chapter 3.1 of the <a
+ * href="http://yaml.org/spec/1.1/">YAML Specification</a>.
+ * </p>
+ */
+public class Composer {
+    protected final Parser parser;
+    private final Resolver resolver;
+    private final Map<String, Node> anchors;
+    private final Set<Node> recursiveNodes;
+
+    public Composer(Parser parser, Resolver resolver) {
+        this.parser = parser;
+        this.resolver = resolver;
+        this.anchors = new HashMap<String, Node>();
+        this.recursiveNodes = new HashSet<Node>();
+    }
+
+    /**
+     * Checks if further documents are available.
+     * 
+     * @return <code>true</code> if there is at least one more document.
+     */
+    public boolean checkNode() {
+        // Drop the STREAM-START event.
+        if (parser.checkEvent(Event.ID.StreamStart)) {
+            parser.getEvent();
+        }
+        // If there are more documents available?
+        return !parser.checkEvent(Event.ID.StreamEnd);
+    }
+
+    /**
+     * Reads and composes the next document.
+     * 
+     * @return The root node of the document or <code>null</code> if no more
+     *         documents are available.
+     */
+    public Node getNode() {
+        // Get the root node of the next document.
+        if (!parser.checkEvent(Event.ID.StreamEnd)) {
+            return composeDocument();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Reads a document from a source that contains only one document.
+     * <p>
+     * If the stream contains more than one document an exception is thrown.
+     * </p>
+     * 
+     * @return The root node of the document or <code>null</code> if no document
+     *         is available.
+     */
+    public Node getSingleNode() {
+        // Drop the STREAM-START event.
+        parser.getEvent();
+        // Compose a document if the stream is not empty.
+        Node document = null;
+        if (!parser.checkEvent(Event.ID.StreamEnd)) {
+            document = composeDocument();
+        }
+        // Ensure that the stream contains no more documents.
+        if (!parser.checkEvent(Event.ID.StreamEnd)) {
+            Event event = parser.getEvent();
+            throw new ComposerException("expected a single document in the stream",
+                    document.getStartMark(), "but found another document", event.getStartMark());
+        }
+        // Drop the STREAM-END event.
+        parser.getEvent();
+        return document;
+    }
+
+    private Node composeDocument() {
+        // Drop the DOCUMENT-START event.
+        parser.getEvent();
+        // Compose the root node.
+        Node node = composeNode(null);
+        // Drop the DOCUMENT-END event.
+        parser.getEvent();
+        this.anchors.clear();
+        recursiveNodes.clear();
+        return node;
+    }
+
+    private Node composeNode(Node parent) {
+        recursiveNodes.add(parent);
+        if (parser.checkEvent(Event.ID.Alias)) {
+            AliasEvent event = (AliasEvent) parser.getEvent();
+            String anchor = event.getAnchor();
+            if (!anchors.containsKey(anchor)) {
+                throw new ComposerException(null, null, "found undefined alias " + anchor,
+                        event.getStartMark());
+            }
+            Node result = anchors.get(anchor);
+            if (recursiveNodes.remove(result)) {
+                result.setTwoStepsConstruction(true);
+            }
+            return result;
+        }
+        NodeEvent event = (NodeEvent) parser.peekEvent();
+        String anchor = null;
+        anchor = event.getAnchor();
+        // the check for duplicate anchors has been removed (issue 174)
+        Node node = null;
+        if (parser.checkEvent(Event.ID.Scalar)) {
+            node = composeScalarNode(anchor);
+        } else if (parser.checkEvent(Event.ID.SequenceStart)) {
+            node = composeSequenceNode(anchor);
+        } else {
+            node = composeMappingNode(anchor);
+        }
+        recursiveNodes.remove(parent);
+        return node;
+    }
+
+    protected Node composeScalarNode(String anchor) {
+        ScalarEvent ev = (ScalarEvent) parser.getEvent();
+        String tag = ev.getTag();
+        boolean resolved = false;
+        Tag nodeTag;
+        if (tag == null || tag.equals("!")) {
+            nodeTag = resolver.resolve(NodeId.scalar, ev.getValue(), ev.getImplicit()
+                    .canOmitTagInPlainScalar());
+            resolved = true;
+        } else {
+            nodeTag = new Tag(tag);
+        }
+        Node node = new ScalarNode(nodeTag, resolved, ev.getValue(), ev.getStartMark(),
+                ev.getEndMark(), ev.getStyle());
+        if (anchor != null) {
+            anchors.put(anchor, node);
+        }
+        return node;
+    }
+
+    protected Node composeSequenceNode(String anchor) {
+        SequenceStartEvent startEvent = (SequenceStartEvent) parser.getEvent();
+        String tag = startEvent.getTag();
+        Tag nodeTag;
+        boolean resolved = false;
+        if (tag == null || tag.equals("!")) {
+            nodeTag = resolver.resolve(NodeId.sequence, null, startEvent.getImplicit());
+            resolved = true;
+        } else {
+            nodeTag = new Tag(tag);
+        }
+        final ArrayList<Node> children = new ArrayList<Node>();
+        SequenceNode node = new SequenceNode(nodeTag, resolved, children,
+                startEvent.getStartMark(), null, startEvent.getFlowStyle());
+        if (anchor != null) {
+            anchors.put(anchor, node);
+        }
+        while (!parser.checkEvent(Event.ID.SequenceEnd)) {
+            children.add(composeNode(node));
+        }
+        Event endEvent = parser.getEvent();
+        node.setEndMark(endEvent.getEndMark());
+        return node;
+    }
+
+    protected Node composeMappingNode(String anchor) {
+        MappingStartEvent startEvent = (MappingStartEvent) parser.getEvent();
+        String tag = startEvent.getTag();
+        Tag nodeTag;
+        boolean resolved = false;
+        if (tag == null || tag.equals("!")) {
+            nodeTag = resolver.resolve(NodeId.mapping, null, startEvent.getImplicit());
+            resolved = true;
+        } else {
+            nodeTag = new Tag(tag);
+        }
+
+        final List<NodeTuple> children = new ArrayList<NodeTuple>();
+        MappingNode node = new MappingNode(nodeTag, resolved, children, startEvent.getStartMark(),
+                null, startEvent.getFlowStyle());
+        if (anchor != null) {
+            anchors.put(anchor, node);
+        }
+        while (!parser.checkEvent(Event.ID.MappingEnd)) {
+            composeMappingChildren(children, node);
+        }
+        Event endEvent = parser.getEvent();
+        node.setEndMark(endEvent.getEndMark());
+        return node;
+    }
+
+    protected void composeMappingChildren(List<NodeTuple> children, MappingNode node) {
+        Node itemKey = composeKeyNode(node);
+        if (itemKey.getTag().equals(Tag.MERGE)) {
+            node.setMerged(true);
+        }
+        Node itemValue = composeValueNode(node);
+        children.add(new NodeTuple(itemKey, itemValue));
+    }
+
+    protected Node composeKeyNode(MappingNode node) {
+        return composeNode(node);
+    }
+
+    protected Node composeValueNode(MappingNode node) {
+        return composeNode(node);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/composer/ComposerException.java b/src/main/java/org/yaml/snakeyaml/composer/ComposerException.java
new file mode 100644
index 0000000..5d20c1d
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/composer/ComposerException.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.composer;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.MarkedYAMLException;
+
+public class ComposerException extends MarkedYAMLException {
+    private static final long serialVersionUID = 2146314636913113935L;
+
+    protected ComposerException(String context, Mark contextMark, String problem, Mark problemMark) {
+        super(context, contextMark, problem, problemMark);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java b/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java
new file mode 100644
index 0000000..61d14f6
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/AbstractConstruct.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.Node;
+
+/**
+ * Because recursive structures are not very common we provide a way to save
+ * some typing when extending a constructor
+ */
+public abstract class AbstractConstruct implements Construct {
+
+    /**
+     * Fail with a reminder to provide the seconds step for a recursive
+     * structure
+     * 
+     * @see org.yaml.snakeyaml.constructor.Construct#construct2ndStep(org.yaml.snakeyaml.nodes.Node,
+     *      java.lang.Object)
+     */
+    public void construct2ndStep(Node node, Object data) {
+        if (node.isTwoStepsConstruction()) {
+            throw new IllegalStateException("Not Implemented in " + getClass().getName());
+        } else {
+            throw new YAMLException("Unexpected recursive structure for Node: " + node);
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
new file mode 100644
index 0000000..3a504cb
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/BaseConstructor.java
@@ -0,0 +1,450 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.composer.ComposerException;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public abstract class BaseConstructor {
+    /**
+     * It maps the node kind to the the Construct implementation. When the
+     * runtime class is known then the implicit tag is ignored.
+     */
+    protected final Map<NodeId, Construct> yamlClassConstructors = new EnumMap<NodeId, Construct>(
+            NodeId.class);
+    /**
+     * It maps the (explicit or implicit) tag to the Construct implementation.
+     * It is used: <br/>
+     * 1) explicit tag - if present. <br/>
+     * 2) implicit tag - when the runtime class of the instance is unknown (the
+     * node has the Object.class)
+     */
+    protected final Map<Tag, Construct> yamlConstructors = new HashMap<Tag, Construct>();
+    /**
+     * It maps the (explicit or implicit) tag to the Construct implementation.
+     * It is used when no exact match found.
+     */
+    protected final Map<String, Construct> yamlMultiConstructors = new HashMap<String, Construct>();
+
+    protected Composer composer;
+    private final Map<Node, Object> constructedObjects;
+    private final Set<Node> recursiveObjects;
+    private final ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>> maps2fill;
+    private final ArrayList<RecursiveTuple<Set<Object>, Object>> sets2fill;
+
+    protected Tag rootTag;
+    private PropertyUtils propertyUtils;
+    private boolean explicitPropertyUtils;
+
+    public BaseConstructor() {
+        constructedObjects = new HashMap<Node, Object>();
+        recursiveObjects = new HashSet<Node>();
+        maps2fill = new ArrayList<RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>>();
+        sets2fill = new ArrayList<RecursiveTuple<Set<Object>, Object>>();
+        rootTag = null;
+        explicitPropertyUtils = false;
+    }
+
+    public void setComposer(Composer composer) {
+        this.composer = composer;
+    }
+
+    /**
+     * Check if more documents available
+     * 
+     * @return true when there are more YAML documents in the stream
+     */
+    public boolean checkData() {
+        // If there are more documents available?
+        return composer.checkNode();
+    }
+
+    /**
+     * Construct and return the next document
+     * 
+     * @return constructed instance
+     */
+    public Object getData() {
+        // Construct and return the next document.
+        composer.checkNode();
+        Node node = composer.getNode();
+        if (rootTag != null) {
+            node.setTag(rootTag);
+        }
+        return constructDocument(node);
+    }
+
+    /**
+     * Ensure that the stream contains a single document and construct it
+     * 
+     * @return constructed instance
+     * @throws ComposerException
+     *             in case there are more documents in the stream
+     */
+    public Object getSingleData(Class<?> type) {
+        // Ensure that the stream contains a single document and construct it
+        Node node = composer.getSingleNode();
+        if (node != null) {
+            if (Object.class != type) {
+                node.setTag(new Tag(type));
+            } else if (rootTag != null) {
+                node.setTag(rootTag);
+            }
+            return constructDocument(node);
+        }
+        return null;
+    }
+
+    /**
+     * Construct complete YAML document. Call the second step in case of
+     * recursive structures. At the end cleans all the state.
+     * 
+     * @param node
+     *            root Node
+     * @return Java instance
+     */
+    protected final Object constructDocument(Node node) {
+        Object data = constructObject(node);
+        fillRecursive();
+        constructedObjects.clear();
+        recursiveObjects.clear();
+        return data;
+    }
+
+    private void fillRecursive() {
+        if (!maps2fill.isEmpty()) {
+            for (RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>> entry : maps2fill) {
+                RecursiveTuple<Object, Object> key_value = entry._2();
+                entry._1().put(key_value._1(), key_value._2());
+            }
+            maps2fill.clear();
+        }
+        if (!sets2fill.isEmpty()) {
+            for (RecursiveTuple<Set<Object>, Object> value : sets2fill) {
+                value._1().add(value._2());
+            }
+            sets2fill.clear();
+        }
+    }
+
+    /**
+     * Construct object from the specified Node. Return existing instance if the
+     * node is already constructed.
+     * 
+     * @param node
+     *            Node to be constructed
+     * @return Java instance
+     */
+    protected Object constructObject(Node node) {
+        if (constructedObjects.containsKey(node)) {
+            return constructedObjects.get(node);
+        }
+        if (recursiveObjects.contains(node)) {
+            throw new ConstructorException(null, null, "found unconstructable recursive node",
+                    node.getStartMark());
+        }
+        recursiveObjects.add(node);
+        Construct constructor = getConstructor(node);
+        Object data = constructor.construct(node);
+        constructedObjects.put(node, data);
+        recursiveObjects.remove(node);
+        if (node.isTwoStepsConstruction()) {
+            constructor.construct2ndStep(node, data);
+        }
+        return data;
+    }
+
+    /**
+     * Get the constructor to construct the Node. For implicit tags if the
+     * runtime class is known a dedicated Construct implementation is used.
+     * Otherwise the constructor is chosen by the tag.
+     * 
+     * @param node
+     *            Node to be constructed
+     * @return Construct implementation for the specified node
+     */
+    protected Construct getConstructor(Node node) {
+        if (node.useClassConstructor()) {
+            return yamlClassConstructors.get(node.getNodeId());
+        } else {
+            Construct constructor = yamlConstructors.get(node.getTag());
+            if (constructor == null) {
+                for (String prefix : yamlMultiConstructors.keySet()) {
+                    if (node.getTag().startsWith(prefix)) {
+                        return yamlMultiConstructors.get(prefix);
+                    }
+                }
+                return yamlConstructors.get(null);
+            }
+            return constructor;
+        }
+    }
+
+    protected Object constructScalar(ScalarNode node) {
+        return node.getValue();
+    }
+
+    protected List<Object> createDefaultList(int initSize) {
+        return new ArrayList<Object>(initSize);
+    }
+
+    protected Set<Object> createDefaultSet(int initSize) {
+        return new LinkedHashSet<Object>(initSize);
+    }
+
+    protected Object createArray(Class<?> type, int size) {
+        return Array.newInstance(type.getComponentType(), size);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected List<? extends Object> constructSequence(SequenceNode node) {
+        List<Object> result;
+        if (List.class.isAssignableFrom(node.getType()) && !node.getType().isInterface()) {
+            // the root class may be defined (Vector for instance)
+            try {
+                result = (List<Object>) node.getType().newInstance();
+            } catch (Exception e) {
+                throw new YAMLException(e);
+            }
+        } else {
+            result = createDefaultList(node.getValue().size());
+        }
+        constructSequenceStep2(node, result);
+        return result;
+
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Set<? extends Object> constructSet(SequenceNode node) {
+        Set<Object> result;
+        if (!node.getType().isInterface()) {
+            // the root class may be defined
+            try {
+                result = (Set<Object>) node.getType().newInstance();
+            } catch (Exception e) {
+                throw new YAMLException(e);
+            }
+        } else {
+            result = createDefaultSet(node.getValue().size());
+        }
+        constructSequenceStep2(node, result);
+        return result;
+
+    }
+
+    protected Object constructArray(SequenceNode node) {
+        return constructArrayStep2(node, createArray(node.getType(), node.getValue().size()));
+    }
+
+    protected void constructSequenceStep2(SequenceNode node, Collection<Object> collection) {
+        for (Node child : node.getValue()) {
+            collection.add(constructObject(child));
+        }
+    }
+
+    protected Object constructArrayStep2(SequenceNode node, Object array) {
+        final Class<?> componentType = node.getType().getComponentType();
+
+        int index = 0;
+        for (Node child : node.getValue()) {
+            // Handle multi-dimensional arrays...
+            if (child.getType() == Object.class) {
+                child.setType(componentType);
+            }
+
+            final Object value = constructObject(child);
+
+            if (componentType.isPrimitive()) {
+                // Null values are disallowed for primitives
+                if (value == null) {
+                    throw new NullPointerException("Unable to construct element value for " + child);
+                }
+
+                // Primitive arrays require quite a lot of work.
+                if (byte.class.equals(componentType)) {
+                    Array.setByte(array, index, ((Number) value).byteValue());
+
+                } else if (short.class.equals(componentType)) {
+                    Array.setShort(array, index, ((Number) value).shortValue());
+
+                } else if (int.class.equals(componentType)) {
+                    Array.setInt(array, index, ((Number) value).intValue());
+
+                } else if (long.class.equals(componentType)) {
+                    Array.setLong(array, index, ((Number) value).longValue());
+
+                } else if (float.class.equals(componentType)) {
+                    Array.setFloat(array, index, ((Number) value).floatValue());
+
+                } else if (double.class.equals(componentType)) {
+                    Array.setDouble(array, index, ((Number) value).doubleValue());
+
+                } else if (char.class.equals(componentType)) {
+                    Array.setChar(array, index, ((Character) value).charValue());
+
+                } else if (boolean.class.equals(componentType)) {
+                    Array.setBoolean(array, index, ((Boolean) value).booleanValue());
+
+                } else {
+                    throw new YAMLException("unexpected primitive type");
+                }
+
+            } else {
+                // Non-primitive arrays can simply be assigned:
+                Array.set(array, index, value);
+            }
+
+            ++index;
+        }
+        return array;
+    }
+
+    protected Map<Object, Object> createDefaultMap() {
+        // respect order from YAML document
+        return new LinkedHashMap<Object, Object>();
+    }
+
+    protected Set<Object> createDefaultSet() {
+        // respect order from YAML document
+        return new LinkedHashSet<Object>();
+    }
+
+    protected Set<Object> constructSet(MappingNode node) {
+        Set<Object> set = createDefaultSet();
+        constructSet2ndStep(node, set);
+        return set;
+    }
+
+    protected Map<Object, Object> constructMapping(MappingNode node) {
+        Map<Object, Object> mapping = createDefaultMap();
+        constructMapping2ndStep(node, mapping);
+        return mapping;
+    }
+
+    protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
+        List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
+        for (NodeTuple tuple : nodeValue) {
+            Node keyNode = tuple.getKeyNode();
+            Node valueNode = tuple.getValueNode();
+            Object key = constructObject(keyNode);
+            if (key != null) {
+                try {
+                    key.hashCode();// check circular dependencies
+                } catch (Exception e) {
+                    throw new ConstructorException("while constructing a mapping",
+                            node.getStartMark(), "found unacceptable key " + key, tuple
+                                    .getKeyNode().getStartMark(), e);
+                }
+            }
+            Object value = constructObject(valueNode);
+            if (keyNode.isTwoStepsConstruction()) {
+                /*
+                 * if keyObject is created it 2 steps we should postpone putting
+                 * it in map because it may have different hash after
+                 * initialization compared to clean just created one. And map of
+                 * course does not observe key hashCode changes.
+                 */
+                maps2fill.add(0,
+                        new RecursiveTuple<Map<Object, Object>, RecursiveTuple<Object, Object>>(
+                                mapping, new RecursiveTuple<Object, Object>(key, value)));
+            } else {
+                mapping.put(key, value);
+            }
+        }
+    }
+
+    protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
+        List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
+        for (NodeTuple tuple : nodeValue) {
+            Node keyNode = tuple.getKeyNode();
+            Object key = constructObject(keyNode);
+            if (key != null) {
+                try {
+                    key.hashCode();// check circular dependencies
+                } catch (Exception e) {
+                    throw new ConstructorException("while constructing a Set", node.getStartMark(),
+                            "found unacceptable key " + key, tuple.getKeyNode().getStartMark(), e);
+                }
+            }
+            if (keyNode.isTwoStepsConstruction()) {
+                /*
+                 * if keyObject is created it 2 steps we should postpone putting
+                 * it into the set because it may have different hash after
+                 * initialization compared to clean just created one. And set of
+                 * course does not observe value hashCode changes.
+                 */
+                sets2fill.add(0, new RecursiveTuple<Set<Object>, Object>(set, key));
+            } else {
+                set.add(key);
+            }
+        }
+    }
+
+    public void setPropertyUtils(PropertyUtils propertyUtils) {
+        this.propertyUtils = propertyUtils;
+        explicitPropertyUtils = true;
+    }
+
+    public final PropertyUtils getPropertyUtils() {
+        if (propertyUtils == null) {
+            propertyUtils = new PropertyUtils();
+        }
+        return propertyUtils;
+    }
+
+    private static class RecursiveTuple<T, K> {
+        private final T _1;
+        private final K _2;
+
+        public RecursiveTuple(T _1, K _2) {
+            this._1 = _1;
+            this._2 = _2;
+        }
+
+        public K _2() {
+            return _2;
+        }
+
+        public T _1() {
+            return _1;
+        }
+    }
+
+    public final boolean isExplicitPropertyUtils() {
+        return explicitPropertyUtils;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Construct.java b/src/main/java/org/yaml/snakeyaml/constructor/Construct.java
new file mode 100644
index 0000000..4fa9118
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Construct.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import org.yaml.snakeyaml.nodes.Node;
+
+/**
+ * Provide a way to construct a Java instance out of the composed Node. Support
+ * recursive objects if it is required. (create Native Data Structure out of
+ * Node Graph)
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/#id859109">Chapter 3. Processing YAML
+ *      Information</a>
+ */
+public interface Construct {
+    /**
+     * Construct a Java instance with all the properties injected when it is
+     * possible.
+     * 
+     * @param node
+     *            composed Node
+     * @return a complete Java instance
+     */
+    Object construct(Node node);
+
+    /**
+     * Apply the second step when constructing recursive structures. Because the
+     * instance is already created it can assign a reference to itself.
+     * 
+     * @param node
+     *            composed Node
+     * @param object
+     *            the instance constructed earlier by
+     *            <code>construct(Node node)</code> for the provided Node
+     */
+    void construct2ndStep(Node node, Object object);
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
new file mode 100644
index 0000000..943702f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
@@ -0,0 +1,682 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.beans.IntrospectionException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Construct a custom Java instance.
+ */
+public class Constructor extends SafeConstructor {
+    private final Map<Tag, Class<? extends Object>> typeTags;
+    protected final Map<Class<? extends Object>, TypeDescription> typeDefinitions;
+
+    public Constructor() {
+        this(Object.class);
+    }
+
+    /**
+     * Create Constructor for the specified class as the root.
+     * 
+     * @param theRoot
+     *            - the class (usually JavaBean) to be constructed
+     */
+    public Constructor(Class<? extends Object> theRoot) {
+        this(new TypeDescription(checkRoot(theRoot)));
+    }
+
+    /**
+     * Ugly Java way to check the argument in the constructor
+     */
+    private static Class<? extends Object> checkRoot(Class<? extends Object> theRoot) {
+        if (theRoot == null) {
+            throw new NullPointerException("Root class must be provided.");
+        } else
+            return theRoot;
+    }
+
+    public Constructor(TypeDescription theRoot) {
+        if (theRoot == null) {
+            throw new NullPointerException("Root type must be provided.");
+        }
+        this.yamlConstructors.put(null, new ConstructYamlObject());
+        if (!Object.class.equals(theRoot.getType())) {
+            rootTag = new Tag(theRoot.getType());
+        }
+        typeTags = new HashMap<Tag, Class<? extends Object>>();
+        typeDefinitions = new HashMap<Class<? extends Object>, TypeDescription>();
+        yamlClassConstructors.put(NodeId.scalar, new ConstructScalar());
+        yamlClassConstructors.put(NodeId.mapping, new ConstructMapping());
+        yamlClassConstructors.put(NodeId.sequence, new ConstructSequence());
+        addTypeDescription(theRoot);
+    }
+
+    /**
+     * Create Constructor for a class which does not have to be in the classpath
+     * or for a definition from a Spring ApplicationContext.
+     * 
+     * @param theRoot
+     *            fully qualified class name of the root class (usually
+     *            JavaBean)
+     * @throws ClassNotFoundException
+     */
+    public Constructor(String theRoot) throws ClassNotFoundException {
+        this(Class.forName(check(theRoot)));
+    }
+
+    private static final String check(String s) {
+        if (s == null) {
+            throw new NullPointerException("Root type must be provided.");
+        }
+        if (s.trim().length() == 0) {
+            throw new YAMLException("Root type must be provided.");
+        }
+        return s;
+    }
+
+    /**
+     * Make YAML aware how to parse a custom Class. If there is no root Class
+     * assigned in constructor then the 'root' property of this definition is
+     * respected.
+     * 
+     * @param definition
+     *            to be added to the Constructor
+     * @return the previous value associated with <tt>definition</tt>, or
+     *         <tt>null</tt> if there was no mapping for <tt>definition</tt>.
+     */
+    public TypeDescription addTypeDescription(TypeDescription definition) {
+        if (definition == null) {
+            throw new NullPointerException("TypeDescription is required.");
+        }
+        Tag tag = definition.getTag();
+        typeTags.put(tag, definition.getType());
+        return typeDefinitions.put(definition.getType(), definition);
+    }
+
+    /**
+     * Construct mapping instance (Map, JavaBean) when the runtime class is
+     * known.
+     */
+    protected class ConstructMapping implements Construct {
+
+        /**
+         * Construct JavaBean. If type safe collections are used please look at
+         * <code>TypeDescription</code>.
+         * 
+         * @param node
+         *            node where the keys are property names (they can only be
+         *            <code>String</code>s) and values are objects to be created
+         * @return constructed JavaBean
+         */
+        public Object construct(Node node) {
+            MappingNode mnode = (MappingNode) node;
+            if (Properties.class.isAssignableFrom(node.getType())) {
+                Properties properties = new Properties();
+                if (!node.isTwoStepsConstruction()) {
+                    constructMapping2ndStep(mnode, properties);
+                } else {
+                    throw new YAMLException("Properties must not be recursive.");
+                }
+                return properties;
+            } else if (SortedMap.class.isAssignableFrom(node.getType())) {
+                SortedMap<Object, Object> map = new TreeMap<Object, Object>();
+                if (!node.isTwoStepsConstruction()) {
+                    constructMapping2ndStep(mnode, map);
+                }
+                return map;
+            } else if (Map.class.isAssignableFrom(node.getType())) {
+                if (node.isTwoStepsConstruction()) {
+                    return createDefaultMap();
+                } else {
+                    return constructMapping(mnode);
+                }
+            } else if (SortedSet.class.isAssignableFrom(node.getType())) {
+                SortedSet<Object> set = new TreeSet<Object>();
+                // XXX why this is not used ?
+                // if (!node.isTwoStepsConstruction()) {
+                constructSet2ndStep(mnode, set);
+                // }
+                return set;
+            } else if (Collection.class.isAssignableFrom(node.getType())) {
+                if (node.isTwoStepsConstruction()) {
+                    return createDefaultSet();
+                } else {
+                    return constructSet(mnode);
+                }
+            } else {
+                if (node.isTwoStepsConstruction()) {
+                    return createEmptyJavaBean(mnode);
+                } else {
+                    return constructJavaBean2ndStep(mnode, createEmptyJavaBean(mnode));
+                }
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void construct2ndStep(Node node, Object object) {
+            if (Map.class.isAssignableFrom(node.getType())) {
+                constructMapping2ndStep((MappingNode) node, (Map<Object, Object>) object);
+            } else if (Set.class.isAssignableFrom(node.getType())) {
+                constructSet2ndStep((MappingNode) node, (Set<Object>) object);
+            } else {
+                constructJavaBean2ndStep((MappingNode) node, object);
+            }
+        }
+
+        protected Object createEmptyJavaBean(MappingNode node) {
+            try {
+                /**
+                 * Using only default constructor. Everything else will be
+                 * initialized on 2nd step. If we do here some partial
+                 * initialization, how do we then track what need to be done on
+                 * 2nd step? I think it is better to get only object here (to
+                 * have it as reference for recursion) and do all other thing on
+                 * 2nd step.
+                 */
+                java.lang.reflect.Constructor<?> c = node.getType().getDeclaredConstructor();
+                c.setAccessible(true);
+                return c.newInstance();
+            } catch (Exception e) {
+                throw new YAMLException(e);
+            }
+        }
+
+        protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
+            flattenMapping(node);
+            Class<? extends Object> beanType = node.getType();
+            List<NodeTuple> nodeValue = node.getValue();
+            for (NodeTuple tuple : nodeValue) {
+                ScalarNode keyNode;
+                if (tuple.getKeyNode() instanceof ScalarNode) {
+                    // key must be scalar
+                    keyNode = (ScalarNode) tuple.getKeyNode();
+                } else {
+                    throw new YAMLException("Keys must be scalars but found: " + tuple.getKeyNode());
+                }
+                Node valueNode = tuple.getValueNode();
+                // keys can only be Strings
+                keyNode.setType(String.class);
+                String key = (String) constructObject(keyNode);
+                try {
+                    Property property = getProperty(beanType, key);
+                    valueNode.setType(property.getType());
+                    TypeDescription memberDescription = typeDefinitions.get(beanType);
+                    boolean typeDetected = false;
+                    if (memberDescription != null) {
+                        switch (valueNode.getNodeId()) {
+                        case sequence:
+                            SequenceNode snode = (SequenceNode) valueNode;
+                            Class<? extends Object> memberType = memberDescription
+                                    .getListPropertyType(key);
+                            if (memberType != null) {
+                                snode.setListType(memberType);
+                                typeDetected = true;
+                            } else if (property.getType().isArray()) {
+                                snode.setListType(property.getType().getComponentType());
+                                typeDetected = true;
+                            }
+                            break;
+                        case mapping:
+                            MappingNode mnode = (MappingNode) valueNode;
+                            Class<? extends Object> keyType = memberDescription.getMapKeyType(key);
+                            if (keyType != null) {
+                                mnode.setTypes(keyType, memberDescription.getMapValueType(key));
+                                typeDetected = true;
+                            }
+                            break;
+                        default: // scalar
+                        }
+                    }
+                    if (!typeDetected && valueNode.getNodeId() != NodeId.scalar) {
+                        // only if there is no explicit TypeDescription
+                        Class<?>[] arguments = property.getActualTypeArguments();
+                        if (arguments != null && arguments.length > 0) {
+                            // type safe (generic) collection may contain the
+                            // proper class
+                            if (valueNode.getNodeId() == NodeId.sequence) {
+                                Class<?> t = arguments[0];
+                                SequenceNode snode = (SequenceNode) valueNode;
+                                snode.setListType(t);
+                            } else if (valueNode.getTag().equals(Tag.SET)) {
+                                Class<?> t = arguments[0];
+                                MappingNode mnode = (MappingNode) valueNode;
+                                mnode.setOnlyKeyType(t);
+                                mnode.setUseClassConstructor(true);
+                            } else if (property.getType().isAssignableFrom(Map.class)) {
+                                Class<?> ketType = arguments[0];
+                                Class<?> valueType = arguments[1];
+                                MappingNode mnode = (MappingNode) valueNode;
+                                mnode.setTypes(ketType, valueType);
+                                mnode.setUseClassConstructor(true);
+                            } else {
+                                // the type for collection entries cannot be
+                                // detected
+                            }
+                        }
+                    }
+
+                    Object value = constructObject(valueNode);
+                    // Correct when the property expects float but double was
+                    // constructed
+                    if (property.getType() == Float.TYPE || property.getType() == Float.class) {
+                        if (value instanceof Double) {
+                            value = ((Double) value).floatValue();
+                        }
+                    }
+                    // Correct when the property a String but the value is binary
+                    if (property.getType() == String.class && Tag.BINARY.equals(valueNode.getTag()) && value instanceof byte[]) {
+                        value = new String((byte[])value);
+                    }
+
+                    property.set(object, value);
+                } catch (Exception e) {
+                    throw new ConstructorException("Cannot create property=" + key
+                            + " for JavaBean=" + object, node.getStartMark(), e.getMessage(),
+                            valueNode.getStartMark(), e);
+                }
+            }
+            return object;
+        }
+
+        protected Property getProperty(Class<? extends Object> type, String name)
+                throws IntrospectionException {
+            return getPropertyUtils().getProperty(type, name);
+        }
+    }
+
+    /**
+     * Construct an instance when the runtime class is not known but a global
+     * tag with a class name is defined. It delegates the construction to the
+     * appropriate constructor based on the node kind (scalar, sequence,
+     * mapping)
+     */
+    protected class ConstructYamlObject implements Construct {
+
+        private Construct getConstructor(Node node) {
+            Class<?> cl = getClassForNode(node);
+            node.setType(cl);
+            // call the constructor as if the runtime class is defined
+            Construct constructor = yamlClassConstructors.get(node.getNodeId());
+            return constructor;
+        }
+
+        public Object construct(Node node) {
+            Object result = null;
+            try {
+                result = getConstructor(node).construct(node);
+            } catch (ConstructorException e) {
+                throw e;
+            } catch (Exception e) {
+                throw new ConstructorException(null, null, "Can't construct a java object for "
+                        + node.getTag() + "; exception=" + e.getMessage(), node.getStartMark(), e);
+            }
+            return result;
+        }
+
+        public void construct2ndStep(Node node, Object object) {
+            try {
+                getConstructor(node).construct2ndStep(node, object);
+            } catch (Exception e) {
+                throw new ConstructorException(null, null,
+                        "Can't construct a second step for a java object for " + node.getTag()
+                                + "; exception=" + e.getMessage(), node.getStartMark(), e);
+            }
+        }
+    }
+
+    /**
+     * Construct scalar instance when the runtime class is known. Recursive
+     * structures are not supported.
+     */
+    protected class ConstructScalar extends AbstractConstruct {
+        public Object construct(Node nnode) {
+            ScalarNode node = (ScalarNode) nnode;
+            Class<?> type = node.getType();
+            Object result;
+            if (type.isPrimitive() || type == String.class || Number.class.isAssignableFrom(type)
+                    || type == Boolean.class || Date.class.isAssignableFrom(type)
+                    || type == Character.class || type == BigInteger.class
+                    || type == BigDecimal.class || Enum.class.isAssignableFrom(type)
+                    || Tag.BINARY.equals(node.getTag()) || Calendar.class.isAssignableFrom(type) || type == UUID.class) {
+                // standard classes created directly
+                result = constructStandardJavaInstance(type, node);
+            } else {
+                // there must be only 1 constructor with 1 argument
+                java.lang.reflect.Constructor<?>[] javaConstructors = type
+                        .getDeclaredConstructors();
+                int oneArgCount = 0;
+                java.lang.reflect.Constructor<?> javaConstructor = null;
+                for (java.lang.reflect.Constructor<?> c : javaConstructors) {
+                    if (c.getParameterTypes().length == 1) {
+                        oneArgCount++;
+                        javaConstructor = c;
+                    }
+                }
+                Object argument;
+                if (javaConstructor == null) {
+                    throw new YAMLException("No single argument constructor found for " + type);
+                } else if (oneArgCount == 1) {
+                    argument = constructStandardJavaInstance(
+                            javaConstructor.getParameterTypes()[0], node);
+                } else {
+                    // TODO it should be possible to use implicit types instead
+                    // of forcing String. Resolver must be available here to
+                    // obtain the implicit tag. Then we can set the tag and call
+                    // callConstructor(node) to create the argument instance.
+                    // On the other hand it may be safer to require a custom
+                    // constructor to avoid guessing the argument class
+                    argument = constructScalar(node);
+                    try {
+                        javaConstructor = type.getDeclaredConstructor(String.class);
+                    } catch (Exception e) {
+                        throw new YAMLException("Can't construct a java object for scalar "
+                                + node.getTag() + "; No String constructor found. Exception="
+                                + e.getMessage(), e);
+                    }
+                }
+                try {
+                    javaConstructor.setAccessible(true);
+                    result = javaConstructor.newInstance(argument);
+                } catch (Exception e) {
+                    throw new ConstructorException(null, null,
+                            "Can't construct a java object for scalar " + node.getTag()
+                                    + "; exception=" + e.getMessage(), node.getStartMark(), e);
+                }
+            }
+            return result;
+        }
+
+        @SuppressWarnings("unchecked")
+        private Object constructStandardJavaInstance(@SuppressWarnings("rawtypes")
+        Class type, ScalarNode node) {
+            Object result;
+            if (type == String.class) {
+                Construct stringConstructor = yamlConstructors.get(Tag.STR);
+                result = stringConstructor.construct(node);
+            } else if (type == Boolean.class || type == Boolean.TYPE) {
+                Construct boolConstructor = yamlConstructors.get(Tag.BOOL);
+                result = boolConstructor.construct(node);
+            } else if (type == Character.class || type == Character.TYPE) {
+                Construct charConstructor = yamlConstructors.get(Tag.STR);
+                String ch = (String) charConstructor.construct(node);
+                if (ch.length() == 0) {
+                    result = null;
+                } else if (ch.length() != 1) {
+                    throw new YAMLException("Invalid node Character: '" + ch + "'; length: "
+                            + ch.length());
+                } else {
+                    result = Character.valueOf(ch.charAt(0));
+                }
+            } else if (Date.class.isAssignableFrom(type)) {
+                Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
+                Date date = (Date) dateConstructor.construct(node);
+                if (type == Date.class) {
+                    result = date;
+                } else {
+                    try {
+                        java.lang.reflect.Constructor<?> constr = type.getConstructor(long.class);
+                        result = constr.newInstance(date.getTime());
+                    } catch (RuntimeException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        throw new YAMLException("Cannot construct: '" + type + "'");
+                    }
+                }
+            } else if (type == Float.class || type == Double.class || type == Float.TYPE
+                    || type == Double.TYPE || type == BigDecimal.class) {
+                if (type == BigDecimal.class) {
+                    result = new BigDecimal(node.getValue());
+                } else {
+                    Construct doubleConstructor = yamlConstructors.get(Tag.FLOAT);
+                    result = doubleConstructor.construct(node);
+                    if (type == Float.class || type == Float.TYPE) {
+                        result = new Float((Double) result);
+                    }
+                }
+            } else if (type == Byte.class || type == Short.class || type == Integer.class
+                    || type == Long.class || type == BigInteger.class || type == Byte.TYPE
+                    || type == Short.TYPE || type == Integer.TYPE || type == Long.TYPE) {
+                Construct intConstructor = yamlConstructors.get(Tag.INT);
+                result = intConstructor.construct(node);
+                if (type == Byte.class || type == Byte.TYPE) {
+                    result = Byte.valueOf(result.toString());
+                } else if (type == Short.class || type == Short.TYPE) {
+                    result = Short.valueOf(result.toString());
+                } else if (type == Integer.class || type == Integer.TYPE) {
+                    result = Integer.parseInt(result.toString());
+                } else if (type == Long.class || type == Long.TYPE) {
+                    result = Long.valueOf(result.toString());
+                } else {
+                    // only BigInteger left
+                    result = new BigInteger(result.toString());
+                }
+            } else if (Enum.class.isAssignableFrom(type)) {
+                String enumValueName = node.getValue();
+                try {
+                    result = Enum.valueOf(type, enumValueName);
+                } catch (Exception ex) {
+                    throw new YAMLException("Unable to find enum value '" + enumValueName
+                            + "' for enum class: " + type.getName());
+                }
+            } else if (Calendar.class.isAssignableFrom(type)) {
+                ConstructYamlTimestamp contr = new ConstructYamlTimestamp();
+                contr.construct(node);
+                result = contr.getCalendar();
+            } else if (Number.class.isAssignableFrom(type)) {
+                ConstructYamlNumber contr = new ConstructYamlNumber();
+                result = contr.construct(node);
+            }  else if (UUID.class == type) {
+                result = UUID.fromString(node.getValue());
+            } else {
+                if (yamlConstructors.containsKey(node.getTag())) {
+                    result = yamlConstructors.get(node.getTag()).construct(node);
+                } else {
+                    throw new YAMLException("Unsupported class: " + type);
+                }
+            }
+            return result;
+        }
+    }
+
+    /**
+     * Construct sequence (List, Array, or immutable object) when the runtime
+     * class is known.
+     */
+    protected class ConstructSequence implements Construct {
+        @SuppressWarnings("unchecked")
+        public Object construct(Node node) {
+            SequenceNode snode = (SequenceNode) node;
+            if (Set.class.isAssignableFrom(node.getType())) {
+                if (node.isTwoStepsConstruction()) {
+                    throw new YAMLException("Set cannot be recursive.");
+                } else {
+                    return constructSet(snode);
+                }
+            } else if (Collection.class.isAssignableFrom(node.getType())) {
+                if (node.isTwoStepsConstruction()) {
+                    return createDefaultList(snode.getValue().size());
+                } else {
+                    return constructSequence(snode);
+                }
+            } else if (node.getType().isArray()) {
+                if (node.isTwoStepsConstruction()) {
+                    return createArray(node.getType(), snode.getValue().size());
+                } else {
+                    return constructArray(snode);
+                }
+            } else {
+                // create immutable object
+                List<java.lang.reflect.Constructor<?>> possibleConstructors = new ArrayList<java.lang.reflect.Constructor<?>>(
+                        snode.getValue().size());
+                for (java.lang.reflect.Constructor<?> constructor : node
+                        .getType().getDeclaredConstructors()) {
+                    if (snode.getValue()
+                            .size() == constructor.getParameterTypes().length) {
+                        possibleConstructors.add(constructor);
+                    }
+                }
+                if (!possibleConstructors.isEmpty()) {
+                    if (possibleConstructors.size() == 1) {
+                        Object[] argumentList = new Object[snode.getValue().size()];
+                        java.lang.reflect.Constructor<?> c = possibleConstructors.get(0);
+                        int index = 0;
+                        for (Node argumentNode : snode.getValue()) {
+                            Class<?> type = c.getParameterTypes()[index];
+                            // set runtime classes for arguments
+                            argumentNode.setType(type);
+                            argumentList[index++] = constructObject(argumentNode);
+                        }
+
+                        try {
+                            c.setAccessible(true);
+                            return c.newInstance(argumentList);
+                        } catch (Exception e) {
+                            throw new YAMLException(e);
+                        }
+                    }
+
+                    // use BaseConstructor
+                    List<Object> argumentList = (List<Object>) constructSequence(snode);
+                    Class<?>[] parameterTypes = new Class[argumentList.size()];
+                    int index = 0;
+                    for (Object parameter : argumentList) {
+                        parameterTypes[index] = parameter.getClass();
+                        index++;
+                    }
+
+                    for (java.lang.reflect.Constructor<?> c : possibleConstructors) {
+                        Class<?>[] argTypes = c.getParameterTypes();
+                        boolean foundConstructor = true;
+                        for (int i = 0; i < argTypes.length; i++) {
+                            if (!wrapIfPrimitive(argTypes[i]).isAssignableFrom(parameterTypes[i])) {
+                                foundConstructor = false;
+                                break;
+                            }
+                        }
+                        if (foundConstructor) {
+                            try {
+                                c.setAccessible(true);
+                                return c.newInstance(argumentList.toArray());
+                            } catch (Exception e) {
+                                throw new YAMLException(e);
+                            }
+                        }
+                    }
+                }
+                throw new YAMLException("No suitable constructor with "
+                        + String.valueOf(snode.getValue().size()) + " arguments found for "
+                        + node.getType());
+
+            }
+        }
+
+        private final Class<? extends Object> wrapIfPrimitive(Class<?> clazz) {
+            if (!clazz.isPrimitive()) {
+                return clazz;
+            }
+            if (clazz == Integer.TYPE) {
+                return Integer.class;
+            }
+            if (clazz == Float.TYPE) {
+                return Float.class;
+            }
+            if (clazz == Double.TYPE) {
+                return Double.class;
+            }
+            if (clazz == Boolean.TYPE) {
+                return Boolean.class;
+            }
+            if (clazz == Long.TYPE) {
+                return Long.class;
+            }
+            if (clazz == Character.TYPE) {
+                return Character.class;
+            }
+            if (clazz == Short.TYPE) {
+                return Short.class;
+            }
+            if (clazz == Byte.TYPE) {
+                return Byte.class;
+            }
+            throw new YAMLException("Unexpected primitive " + clazz);
+        }
+
+        @SuppressWarnings("unchecked")
+        public void construct2ndStep(Node node, Object object) {
+            SequenceNode snode = (SequenceNode) node;
+            if (List.class.isAssignableFrom(node.getType())) {
+                List<Object> list = (List<Object>) object;
+                constructSequenceStep2(snode, list);
+            } else if (node.getType().isArray()) {
+                constructArrayStep2(snode, object);
+            } else {
+                throw new YAMLException("Immutable objects cannot be recursive.");
+            }
+        }
+    }
+
+    protected Class<?> getClassForNode(Node node) {
+        Class<? extends Object> classForTag = typeTags.get(node.getTag());
+        if (classForTag == null) {
+            String name = node.getTag().getClassName();
+            Class<?> cl;
+            try {
+                cl = getClassForName(name);
+            } catch (ClassNotFoundException e) {
+                throw new YAMLException("Class not found: " + name);
+            }
+            typeTags.put(node.getTag(), cl);
+            return cl;
+        } else {
+            return classForTag;
+        }
+    }
+
+    protected Class<?> getClassForName(String name) throws ClassNotFoundException {
+        try {
+            return Class.forName(name, true, Thread.currentThread().getContextClassLoader());
+        } catch (ClassNotFoundException e) {
+            return Class.forName(name);
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/ConstructorException.java b/src/main/java/org/yaml/snakeyaml/constructor/ConstructorException.java
new file mode 100644
index 0000000..336e3ba
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/ConstructorException.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.MarkedYAMLException;
+
+public class ConstructorException extends MarkedYAMLException {
+    private static final long serialVersionUID = -8816339931365239910L;
+
+    protected ConstructorException(String context, Mark contextMark, String problem,
+            Mark problemMark, Throwable cause) {
+        super(context, contextMark, problem, problemMark, cause);
+    }
+
+    protected ConstructorException(String context, Mark contextMark, String problem,
+            Mark problemMark) {
+        this(context, contextMark, problem, problemMark, null);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructor.java
new file mode 100644
index 0000000..edb163d
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructor.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+/**
+ * Construct instances with a custom Class Loader.
+ */
+public class CustomClassLoaderConstructor extends Constructor {
+    private ClassLoader loader = CustomClassLoaderConstructor.class.getClassLoader();
+
+    public CustomClassLoaderConstructor(ClassLoader cLoader) {
+        this(Object.class, cLoader);
+    }
+
+    public CustomClassLoaderConstructor(Class<? extends Object> theRoot, ClassLoader theLoader) {
+        super(theRoot);
+        if (theLoader == null) {
+            throw new NullPointerException("Loader must be provided.");
+        }
+        this.loader = theLoader;
+    }
+
+    @Override
+    protected Class<?> getClassForName(String name) throws ClassNotFoundException {
+        return Class.forName(name, true, loader);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
new file mode 100644
index 0000000..b5572ea
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/constructor/SafeConstructor.java
@@ -0,0 +1,510 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.math.BigInteger;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Construct standard Java classes
+ */
+public class SafeConstructor extends BaseConstructor {
+
+    public static final ConstructUndefined undefinedConstructor = new ConstructUndefined();
+
+    public SafeConstructor() {
+        this.yamlConstructors.put(Tag.NULL, new ConstructYamlNull());
+        this.yamlConstructors.put(Tag.BOOL, new ConstructYamlBool());
+        this.yamlConstructors.put(Tag.INT, new ConstructYamlInt());
+        this.yamlConstructors.put(Tag.FLOAT, new ConstructYamlFloat());
+        this.yamlConstructors.put(Tag.BINARY, new ConstructYamlBinary());
+        this.yamlConstructors.put(Tag.TIMESTAMP, new ConstructYamlTimestamp());
+        this.yamlConstructors.put(Tag.OMAP, new ConstructYamlOmap());
+        this.yamlConstructors.put(Tag.PAIRS, new ConstructYamlPairs());
+        this.yamlConstructors.put(Tag.SET, new ConstructYamlSet());
+        this.yamlConstructors.put(Tag.STR, new ConstructYamlStr());
+        this.yamlConstructors.put(Tag.SEQ, new ConstructYamlSeq());
+        this.yamlConstructors.put(Tag.MAP, new ConstructYamlMap());
+        this.yamlConstructors.put(null, undefinedConstructor);
+        this.yamlClassConstructors.put(NodeId.scalar, undefinedConstructor);
+        this.yamlClassConstructors.put(NodeId.sequence, undefinedConstructor);
+        this.yamlClassConstructors.put(NodeId.mapping, undefinedConstructor);
+    }
+
+    protected void flattenMapping(MappingNode node) {
+        // perform merging only on nodes containing merge node(s)
+        if (node.isMerged()) {
+            node.setValue(mergeNode(node, true, new HashMap<Object, Integer>(),
+                    new ArrayList<NodeTuple>()));
+        }
+    }
+
+    /**
+     * Does merge for supplied mapping node.
+     * 
+     * @param node
+     *            where to merge
+     * @param isPreffered
+     *            true if keys of node should take precedence over others...
+     * @param key2index
+     *            maps already merged keys to index from values
+     * @param values
+     *            collects merged NodeTuple
+     * @return list of the merged NodeTuple (to be set as value for the
+     *         MappingNode)
+     */
+    private List<NodeTuple> mergeNode(MappingNode node, boolean isPreffered,
+            Map<Object, Integer> key2index, List<NodeTuple> values) {
+        List<NodeTuple> nodeValue = node.getValue();
+        // reversed for http://code.google.com/p/snakeyaml/issues/detail?id=139
+        Collections.reverse(nodeValue);
+        for (Iterator<NodeTuple> iter = nodeValue.iterator(); iter.hasNext();) {
+            final NodeTuple nodeTuple = iter.next();
+            final Node keyNode = nodeTuple.getKeyNode();
+            final Node valueNode = nodeTuple.getValueNode();
+            if (keyNode.getTag().equals(Tag.MERGE)) {
+                iter.remove();
+                switch (valueNode.getNodeId()) {
+                case mapping:
+                    MappingNode mn = (MappingNode) valueNode;
+                    mergeNode(mn, false, key2index, values);
+                    break;
+                case sequence:
+                    SequenceNode sn = (SequenceNode) valueNode;
+                    List<Node> vals = sn.getValue();
+                    for (Node subnode : vals) {
+                        if (!(subnode instanceof MappingNode)) {
+                            throw new ConstructorException("while constructing a mapping",
+                                    node.getStartMark(),
+                                    "expected a mapping for merging, but found "
+                                            + subnode.getNodeId(), subnode.getStartMark());
+                        }
+                        MappingNode mnode = (MappingNode) subnode;
+                        mergeNode(mnode, false, key2index, values);
+                    }
+                    break;
+                default:
+                    throw new ConstructorException("while constructing a mapping",
+                            node.getStartMark(),
+                            "expected a mapping or list of mappings for merging, but found "
+                                    + valueNode.getNodeId(), valueNode.getStartMark());
+                }
+            } else {
+                // we need to construct keys to avoid duplications
+                Object key = constructObject(keyNode);
+                if (!key2index.containsKey(key)) { // 1st time merging key
+                    values.add(nodeTuple);
+                    // keep track where tuple for the key is
+                    key2index.put(key, values.size() - 1);
+                } else if (isPreffered) { // there is value for the key, but we
+                                          // need to override it
+                    // change value for the key using saved position
+                    values.set(key2index.get(key), nodeTuple);
+                }
+            }
+        }
+        return values;
+    }
+
+    protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
+        flattenMapping(node);
+        super.constructMapping2ndStep(node, mapping);
+    }
+
+    @Override
+    protected void constructSet2ndStep(MappingNode node, Set<Object> set) {
+        flattenMapping(node);
+        super.constructSet2ndStep(node, set);
+    }
+
+    public class ConstructYamlNull extends AbstractConstruct {
+        public Object construct(Node node) {
+            constructScalar((ScalarNode) node);
+            return null;
+        }
+    }
+
+    private final static Map<String, Boolean> BOOL_VALUES = new HashMap<String, Boolean>();
+    static {
+        BOOL_VALUES.put("yes", Boolean.TRUE);
+        BOOL_VALUES.put("no", Boolean.FALSE);
+        BOOL_VALUES.put("true", Boolean.TRUE);
+        BOOL_VALUES.put("false", Boolean.FALSE);
+        BOOL_VALUES.put("on", Boolean.TRUE);
+        BOOL_VALUES.put("off", Boolean.FALSE);
+    }
+
+    public class ConstructYamlBool extends AbstractConstruct {
+        public Object construct(Node node) {
+            String val = (String) constructScalar((ScalarNode) node);
+            return BOOL_VALUES.get(val.toLowerCase());
+        }
+    }
+
+    public class ConstructYamlInt extends AbstractConstruct {
+        public Object construct(Node node) {
+            String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
+            int sign = +1;
+            char first = value.charAt(0);
+            if (first == '-') {
+                sign = -1;
+                value = value.substring(1);
+            } else if (first == '+') {
+                value = value.substring(1);
+            }
+            int base = 10;
+            if ("0".equals(value)) {
+                return Integer.valueOf(0);
+            } else if (value.startsWith("0b")) {
+                value = value.substring(2);
+                base = 2;
+            } else if (value.startsWith("0x")) {
+                value = value.substring(2);
+                base = 16;
+            } else if (value.startsWith("0")) {
+                value = value.substring(1);
+                base = 8;
+            } else if (value.indexOf(':') != -1) {
+                String[] digits = value.split(":");
+                int bes = 1;
+                int val = 0;
+                for (int i = 0, j = digits.length; i < j; i++) {
+                    val += Long.parseLong(digits[j - i - 1]) * bes;
+                    bes *= 60;
+                }
+                return createNumber(sign, String.valueOf(val), 10);
+            } else {
+                return createNumber(sign, value, 10);
+            }
+            return createNumber(sign, value, base);
+        }
+    }
+
+    private Number createNumber(int sign, String number, int radix) {
+        Number result;
+        if (sign < 0) {
+            number = "-" + number;
+        }
+        try {
+            result = Integer.valueOf(number, radix);
+        } catch (NumberFormatException e) {
+            try {
+                result = Long.valueOf(number, radix);
+            } catch (NumberFormatException e1) {
+                result = new BigInteger(number, radix);
+            }
+        }
+        return result;
+    }
+
+    public class ConstructYamlFloat extends AbstractConstruct {
+        public Object construct(Node node) {
+            String value = constructScalar((ScalarNode) node).toString().replaceAll("_", "");
+            int sign = +1;
+            char first = value.charAt(0);
+            if (first == '-') {
+                sign = -1;
+                value = value.substring(1);
+            } else if (first == '+') {
+                value = value.substring(1);
+            }
+            String valLower = value.toLowerCase();
+            if (".inf".equals(valLower)) {
+                return new Double(sign == -1 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+            } else if (".nan".equals(valLower)) {
+                return new Double(Double.NaN);
+            } else if (value.indexOf(':') != -1) {
+                String[] digits = value.split(":");
+                int bes = 1;
+                double val = 0.0;
+                for (int i = 0, j = digits.length; i < j; i++) {
+                    val += Double.parseDouble(digits[j - i - 1]) * bes;
+                    bes *= 60;
+                }
+                return new Double(sign * val);
+            } else {
+                Double d = Double.valueOf(value);
+                return new Double(d.doubleValue() * sign);
+            }
+        }
+    }
+
+    public class ConstructYamlBinary extends AbstractConstruct {
+        public Object construct(Node node) {
+            byte[] decoded = Base64Coder.decode(constructScalar((ScalarNode) node).toString()
+                    .toCharArray());
+            return decoded;
+        }
+    }
+
+    public class ConstructYamlNumber extends AbstractConstruct {
+
+        private final NumberFormat nf = NumberFormat.getInstance();
+
+        public Object construct(Node node) {
+            ScalarNode scalar = (ScalarNode) node;
+            try {
+                return nf.parse(scalar.getValue());
+            } catch (ParseException e) {
+                String lowerCaseValue = scalar.getValue().toLowerCase();
+                if (lowerCaseValue.contains("inf") || lowerCaseValue.contains("nan")) {
+                    /*
+                     * Non-finites such as (+/-)infinity and NaN are not
+                     * parseable by NumberFormat when these `Double` values are
+                     * dumped by snakeyaml. Delegate to the `Tag.FLOAT`
+                     * constructor when for this expected failure cause.
+                     */
+                    return (Number) yamlConstructors.get(Tag.FLOAT).construct(node);
+                } else {
+                    throw new IllegalArgumentException("Unable to parse as Number: "
+                            + scalar.getValue());
+                }
+            }
+        }
+    }
+
+    private final static Pattern TIMESTAMP_REGEXP = Pattern
+            .compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(?:Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?))?)?$");
+    private final static Pattern YMD_REGEXP = Pattern
+            .compile("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$");
+
+    public static class ConstructYamlTimestamp extends AbstractConstruct {
+        private Calendar calendar;
+
+        public Calendar getCalendar() {
+            return calendar;
+        }
+
+        public Object construct(Node node) {
+            ScalarNode scalar = (ScalarNode) node;
+            String nodeValue = scalar.getValue();
+            Matcher match = YMD_REGEXP.matcher(nodeValue);
+            if (match.matches()) {
+                String year_s = match.group(1);
+                String month_s = match.group(2);
+                String day_s = match.group(3);
+                calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+                calendar.clear();
+                calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
+                // Java's months are zero-based...
+                calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1); // x
+                calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
+                return calendar.getTime();
+            } else {
+                match = TIMESTAMP_REGEXP.matcher(nodeValue);
+                if (!match.matches()) {
+                    throw new YAMLException("Unexpected timestamp: " + nodeValue);
+                }
+                String year_s = match.group(1);
+                String month_s = match.group(2);
+                String day_s = match.group(3);
+                String hour_s = match.group(4);
+                String min_s = match.group(5);
+                // seconds and milliseconds
+                String seconds = match.group(6);
+                String millis = match.group(7);
+                if (millis != null) {
+                    seconds = seconds + "." + millis;
+                }
+                double fractions = Double.parseDouble(seconds);
+                int sec_s = (int) Math.round(Math.floor(fractions));
+                int usec = (int) Math.round((fractions - sec_s) * 1000);
+                // timezone
+                String timezoneh_s = match.group(8);
+                String timezonem_s = match.group(9);
+                TimeZone timeZone;
+                if (timezoneh_s != null) {
+                    String time = timezonem_s != null ? ":" + timezonem_s : "00";
+                    timeZone = TimeZone.getTimeZone("GMT" + timezoneh_s + time);
+                } else {
+                    // no time zone provided
+                    timeZone = TimeZone.getTimeZone("UTC");
+                }
+                calendar = Calendar.getInstance(timeZone);
+                calendar.set(Calendar.YEAR, Integer.parseInt(year_s));
+                // Java's months are zero-based...
+                calendar.set(Calendar.MONTH, Integer.parseInt(month_s) - 1);
+                calendar.set(Calendar.DAY_OF_MONTH, Integer.parseInt(day_s));
+                calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hour_s));
+                calendar.set(Calendar.MINUTE, Integer.parseInt(min_s));
+                calendar.set(Calendar.SECOND, sec_s);
+                calendar.set(Calendar.MILLISECOND, usec);
+                return calendar.getTime();
+            }
+        }
+    }
+
+    public class ConstructYamlOmap extends AbstractConstruct {
+        public Object construct(Node node) {
+            // Note: we do not check for duplicate keys, because it's too
+            // CPU-expensive.
+            Map<Object, Object> omap = new LinkedHashMap<Object, Object>();
+            if (!(node instanceof SequenceNode)) {
+                throw new ConstructorException("while constructing an ordered map",
+                        node.getStartMark(), "expected a sequence, but found " + node.getNodeId(),
+                        node.getStartMark());
+            }
+            SequenceNode snode = (SequenceNode) node;
+            for (Node subnode : snode.getValue()) {
+                if (!(subnode instanceof MappingNode)) {
+                    throw new ConstructorException("while constructing an ordered map",
+                            node.getStartMark(), "expected a mapping of length 1, but found "
+                                    + subnode.getNodeId(), subnode.getStartMark());
+                }
+                MappingNode mnode = (MappingNode) subnode;
+                if (mnode.getValue().size() != 1) {
+                    throw new ConstructorException("while constructing an ordered map",
+                            node.getStartMark(), "expected a single mapping item, but found "
+                                    + mnode.getValue().size() + " items", mnode.getStartMark());
+                }
+                Node keyNode = mnode.getValue().get(0).getKeyNode();
+                Node valueNode = mnode.getValue().get(0).getValueNode();
+                Object key = constructObject(keyNode);
+                Object value = constructObject(valueNode);
+                omap.put(key, value);
+            }
+            return omap;
+        }
+    }
+
+    // Note: the same code as `construct_yaml_omap`.
+    public class ConstructYamlPairs extends AbstractConstruct {
+        public Object construct(Node node) {
+            // Note: we do not check for duplicate keys, because it's too
+            // CPU-expensive.
+            if (!(node instanceof SequenceNode)) {
+                throw new ConstructorException("while constructing pairs", node.getStartMark(),
+                        "expected a sequence, but found " + node.getNodeId(), node.getStartMark());
+            }
+            SequenceNode snode = (SequenceNode) node;
+            List<Object[]> pairs = new ArrayList<Object[]>(snode.getValue().size());
+            for (Node subnode : snode.getValue()) {
+                if (!(subnode instanceof MappingNode)) {
+                    throw new ConstructorException("while constructingpairs", node.getStartMark(),
+                            "expected a mapping of length 1, but found " + subnode.getNodeId(),
+                            subnode.getStartMark());
+                }
+                MappingNode mnode = (MappingNode) subnode;
+                if (mnode.getValue().size() != 1) {
+                    throw new ConstructorException("while constructing pairs", node.getStartMark(),
+                            "expected a single mapping item, but found " + mnode.getValue().size()
+                                    + " items", mnode.getStartMark());
+                }
+                Node keyNode = mnode.getValue().get(0).getKeyNode();
+                Node valueNode = mnode.getValue().get(0).getValueNode();
+                Object key = constructObject(keyNode);
+                Object value = constructObject(valueNode);
+                pairs.add(new Object[] { key, value });
+            }
+            return pairs;
+        }
+    }
+
+    public class ConstructYamlSet implements Construct {
+        public Object construct(Node node) {
+            if (node.isTwoStepsConstruction()) {
+                return createDefaultSet();
+            } else {
+                return constructSet((MappingNode) node);
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void construct2ndStep(Node node, Object object) {
+            if (node.isTwoStepsConstruction()) {
+                constructSet2ndStep((MappingNode) node, (Set<Object>) object);
+            } else {
+                throw new YAMLException("Unexpected recursive set structure. Node: " + node);
+            }
+        }
+    }
+
+    public class ConstructYamlStr extends AbstractConstruct {
+        public Object construct(Node node) {
+            return constructScalar((ScalarNode) node);
+        }
+    }
+
+    public class ConstructYamlSeq implements Construct {
+        public Object construct(Node node) {
+            SequenceNode seqNode = (SequenceNode) node;
+            if (node.isTwoStepsConstruction()) {
+                return createDefaultList(seqNode.getValue().size());
+            } else {
+                return constructSequence(seqNode);
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void construct2ndStep(Node node, Object data) {
+            if (node.isTwoStepsConstruction()) {
+                constructSequenceStep2((SequenceNode) node, (List<Object>) data);
+            } else {
+                throw new YAMLException("Unexpected recursive sequence structure. Node: " + node);
+            }
+        }
+    }
+
+    public class ConstructYamlMap implements Construct {
+        public Object construct(Node node) {
+            if (node.isTwoStepsConstruction()) {
+                return createDefaultMap();
+            } else {
+                return constructMapping((MappingNode) node);
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        public void construct2ndStep(Node node, Object object) {
+            if (node.isTwoStepsConstruction()) {
+                constructMapping2ndStep((MappingNode) node, (Map<Object, Object>) object);
+            } else {
+                throw new YAMLException("Unexpected recursive mapping structure. Node: " + node);
+            }
+        }
+    }
+
+    public static final class ConstructUndefined extends AbstractConstruct {
+        public Object construct(Node node) {
+            throw new ConstructorException(null, null,
+                    "could not determine a constructor for the tag " + node.getTag(),
+                    node.getStartMark());
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitable.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitable.java
new file mode 100644
index 0000000..8773728
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitable.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.io.IOException;
+
+import org.yaml.snakeyaml.events.Event;
+
+public interface Emitable {
+    void emit(Event event) throws IOException;
+}
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
new file mode 100644
index 0000000..2136e21
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
@@ -0,0 +1,1479 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.CollectionEndEvent;
+import org.yaml.snakeyaml.events.CollectionStartEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.NodeEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.scanner.Constant;
+import org.yaml.snakeyaml.util.ArrayStack;
+
+/**
+ * <pre>
+ * Emitter expects events obeying the following grammar:
+ * stream ::= STREAM-START document* STREAM-END
+ * document ::= DOCUMENT-START node DOCUMENT-END
+ * node ::= SCALAR | sequence | mapping
+ * sequence ::= SEQUENCE-START node* SEQUENCE-END
+ * mapping ::= MAPPING-START (node node)* MAPPING-END
+ * </pre>
+ */
+public final class Emitter implements Emitable {
+    private static final Map<Character, String> ESCAPE_REPLACEMENTS = new HashMap<Character, String>();
+    public static final int MIN_INDENT = 1;
+    public static final int MAX_INDENT = 10;
+
+    private static final char[] SPACE = { ' ' };
+
+    static {
+        ESCAPE_REPLACEMENTS.put('\0', "0");
+        ESCAPE_REPLACEMENTS.put('\u0007', "a");
+        ESCAPE_REPLACEMENTS.put('\u0008', "b");
+        ESCAPE_REPLACEMENTS.put('\u0009', "t");
+        ESCAPE_REPLACEMENTS.put('\n', "n");
+        ESCAPE_REPLACEMENTS.put('\u000B', "v");
+        ESCAPE_REPLACEMENTS.put('\u000C', "f");
+        ESCAPE_REPLACEMENTS.put('\r', "r");
+        ESCAPE_REPLACEMENTS.put('\u001B', "e");
+        ESCAPE_REPLACEMENTS.put('"', "\"");
+        ESCAPE_REPLACEMENTS.put('\\', "\\");
+        ESCAPE_REPLACEMENTS.put('\u0085', "N");
+        ESCAPE_REPLACEMENTS.put('\u00A0', "_");
+        ESCAPE_REPLACEMENTS.put('\u2028', "L");
+        ESCAPE_REPLACEMENTS.put('\u2029', "P");
+    }
+
+    private final static Map<String, String> DEFAULT_TAG_PREFIXES = new LinkedHashMap<String, String>();
+    static {
+        DEFAULT_TAG_PREFIXES.put("!", "!");
+        DEFAULT_TAG_PREFIXES.put(Tag.PREFIX, "!!");
+    }
+    // The stream should have the methods `write` and possibly `flush`.
+    private final Writer stream;
+
+    // Encoding is defined by Writer (cannot be overriden by STREAM-START.)
+    // private Charset encoding;
+
+    // Emitter is a state machine with a stack of states to handle nested
+    // structures.
+    private final ArrayStack<EmitterState> states;
+    private EmitterState state;
+
+    // Current event and the event queue.
+    private final Queue<Event> events;
+    private Event event;
+
+    // The current indentation level and the stack of previous indents.
+    private final ArrayStack<Integer> indents;
+    private Integer indent;
+
+    // Flow level.
+    private int flowLevel;
+
+    // Contexts.
+    private boolean rootContext;
+    private boolean mappingContext;
+    private boolean simpleKeyContext;
+
+    //
+    // Characteristics of the last emitted character:
+    // - current position.
+    // - is it a whitespace?
+    // - is it an indention character
+    // (indentation space, '-', '?', or ':')?
+    // private int line; this variable is not used
+    private int column;
+    private boolean whitespace;
+    private boolean indention;
+    private boolean openEnded;
+
+    // Formatting details.
+    private Boolean canonical;
+    // pretty print flow by adding extra line breaks
+    private Boolean prettyFlow;
+
+    private boolean allowUnicode;
+    private int bestIndent;
+    private int indicatorIndent;
+    private int bestWidth;
+    private char[] bestLineBreak;
+    private boolean splitLines;
+
+    // Tag prefixes.
+    private Map<String, String> tagPrefixes;
+
+    // Prepared anchor and tag.
+    private String preparedAnchor;
+    private String preparedTag;
+
+    // Scalar analysis and style.
+    private ScalarAnalysis analysis;
+    private Character style;
+
+    public Emitter(Writer stream, DumperOptions opts) {
+        // The stream should have the methods `write` and possibly `flush`.
+        this.stream = stream;
+        // Emitter is a state machine with a stack of states to handle nested
+        // structures.
+        this.states = new ArrayStack<EmitterState>(100);
+        this.state = new ExpectStreamStart();
+        // Current event and the event queue.
+        this.events = new ArrayBlockingQueue<Event>(100);
+        this.event = null;
+        // The current indentation level and the stack of previous indents.
+        this.indents = new ArrayStack<Integer>(10);
+        this.indent = null;
+        // Flow level.
+        this.flowLevel = 0;
+        // Contexts.
+        mappingContext = false;
+        simpleKeyContext = false;
+
+        //
+        // Characteristics of the last emitted character:
+        // - current position.
+        // - is it a whitespace?
+        // - is it an indention character
+        // (indentation space, '-', '?', or ':')?
+        column = 0;
+        whitespace = true;
+        indention = true;
+
+        // Whether the document requires an explicit document indicator
+        openEnded = false;
+
+        // Formatting details.
+        this.canonical = opts.isCanonical();
+        this.prettyFlow = opts.isPrettyFlow();
+        this.allowUnicode = opts.isAllowUnicode();
+        this.bestIndent = 2;
+        if ((opts.getIndent() > MIN_INDENT) && (opts.getIndent() < MAX_INDENT)) {
+            this.bestIndent = opts.getIndent();
+        }
+        this.indicatorIndent = opts.getIndicatorIndent();
+        this.bestWidth = 80;
+        if (opts.getWidth() > this.bestIndent * 2) {
+            this.bestWidth = opts.getWidth();
+        }
+        this.bestLineBreak = opts.getLineBreak().getString().toCharArray();
+        this.splitLines = opts.getSplitLines();
+
+        // Tag prefixes.
+        this.tagPrefixes = new LinkedHashMap<String, String>();
+
+        // Prepared anchor and tag.
+        this.preparedAnchor = null;
+        this.preparedTag = null;
+
+        // Scalar analysis and style.
+        this.analysis = null;
+        this.style = null;
+    }
+
+    public void emit(Event event) throws IOException {
+        this.events.add(event);
+        while (!needMoreEvents()) {
+            this.event = this.events.poll();
+            this.state.expect();
+            this.event = null;
+        }
+    }
+
+    // In some cases, we wait for a few next events before emitting.
+
+    private boolean needMoreEvents() {
+        if (events.isEmpty()) {
+            return true;
+        }
+        Event event = events.peek();
+        if (event instanceof DocumentStartEvent) {
+            return needEvents(1);
+        } else if (event instanceof SequenceStartEvent) {
+            return needEvents(2);
+        } else if (event instanceof MappingStartEvent) {
+            return needEvents(3);
+        } else {
+            return false;
+        }
+    }
+
+    private boolean needEvents(int count) {
+        int level = 0;
+        Iterator<Event> iter = events.iterator();
+        iter.next();
+        while (iter.hasNext()) {
+            Event event = iter.next();
+            if (event instanceof DocumentStartEvent || event instanceof CollectionStartEvent) {
+                level++;
+            } else if (event instanceof DocumentEndEvent || event instanceof CollectionEndEvent) {
+                level--;
+            } else if (event instanceof StreamEndEvent) {
+                level = -1;
+            }
+            if (level < 0) {
+                return false;
+            }
+        }
+        return events.size() < count + 1;
+    }
+
+    private void increaseIndent(boolean flow, boolean indentless) {
+        indents.push(indent);
+        if (indent == null) {
+            if (flow) {
+                indent = bestIndent;
+            } else {
+                indent = 0;
+            }
+        } else if (!indentless) {
+            this.indent += bestIndent;
+        }
+    }
+
+    // States
+
+    // Stream handlers.
+
+    private class ExpectStreamStart implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof StreamStartEvent) {
+                writeStreamStart();
+                state = new ExpectFirstDocumentStart();
+            } else {
+                throw new EmitterException("expected StreamStartEvent, but got " + event);
+            }
+        }
+    }
+
+    private class ExpectNothing implements EmitterState {
+        public void expect() throws IOException {
+            throw new EmitterException("expecting nothing, but got " + event);
+        }
+    }
+
+    // Document handlers.
+
+    private class ExpectFirstDocumentStart implements EmitterState {
+        public void expect() throws IOException {
+            new ExpectDocumentStart(true).expect();
+        }
+    }
+
+    private class ExpectDocumentStart implements EmitterState {
+        private boolean first;
+
+        public ExpectDocumentStart(boolean first) {
+            this.first = first;
+        }
+
+        public void expect() throws IOException {
+            if (event instanceof DocumentStartEvent) {
+                DocumentStartEvent ev = (DocumentStartEvent) event;
+                if ((ev.getVersion() != null || ev.getTags() != null) && openEnded) {
+                    writeIndicator("...", true, false, false);
+                    writeIndent();
+                }
+                if (ev.getVersion() != null) {
+                    String versionText = prepareVersion(ev.getVersion());
+                    writeVersionDirective(versionText);
+                }
+                tagPrefixes = new LinkedHashMap<String, String>(DEFAULT_TAG_PREFIXES);
+                if (ev.getTags() != null) {
+                    Set<String> handles = new TreeSet<String>(ev.getTags().keySet());
+                    for (String handle : handles) {
+                        String prefix = ev.getTags().get(handle);
+                        tagPrefixes.put(prefix, handle);
+                        String handleText = prepareTagHandle(handle);
+                        String prefixText = prepareTagPrefix(prefix);
+                        writeTagDirective(handleText, prefixText);
+                    }
+                }
+                boolean implicit = first && !ev.getExplicit() && !canonical
+                        && ev.getVersion() == null
+                        && (ev.getTags() == null || ev.getTags().isEmpty())
+                        && !checkEmptyDocument();
+                if (!implicit) {
+                    writeIndent();
+                    writeIndicator("---", true, false, false);
+                    if (canonical) {
+                        writeIndent();
+                    }
+                }
+                state = new ExpectDocumentRoot();
+            } else if (event instanceof StreamEndEvent) {
+                // TODO fix 313 PyYAML changeset
+                // if (openEnded) {
+                // writeIndicator("...", true, false, false);
+                // writeIndent();
+                // }
+                writeStreamEnd();
+                state = new ExpectNothing();
+            } else {
+                throw new EmitterException("expected DocumentStartEvent, but got " + event);
+            }
+        }
+    }
+
+    private class ExpectDocumentEnd implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof DocumentEndEvent) {
+                writeIndent();
+                if (((DocumentEndEvent) event).getExplicit()) {
+                    writeIndicator("...", true, false, false);
+                    writeIndent();
+                }
+                flushStream();
+                state = new ExpectDocumentStart(false);
+            } else {
+                throw new EmitterException("expected DocumentEndEvent, but got " + event);
+            }
+        }
+    }
+
+    private class ExpectDocumentRoot implements EmitterState {
+        public void expect() throws IOException {
+            states.push(new ExpectDocumentEnd());
+            expectNode(true, false, false);
+        }
+    }
+
+    // Node handlers.
+
+    private void expectNode(boolean root, boolean mapping, boolean simpleKey) throws IOException {
+        rootContext = root;
+        mappingContext = mapping;
+        simpleKeyContext = simpleKey;
+        if (event instanceof AliasEvent) {
+            expectAlias();
+        } else if (event instanceof ScalarEvent || event instanceof CollectionStartEvent) {
+            processAnchor("&");
+            processTag();
+            if (event instanceof ScalarEvent) {
+                expectScalar();
+            } else if (event instanceof SequenceStartEvent) {
+                if (flowLevel != 0 || canonical || ((SequenceStartEvent) event).getFlowStyle()
+                        || checkEmptySequence()) {
+                    expectFlowSequence();
+                } else {
+                    expectBlockSequence();
+                }
+            } else {// MappingStartEvent
+                if (flowLevel != 0 || canonical || ((MappingStartEvent) event).getFlowStyle()
+                        || checkEmptyMapping()) {
+                    expectFlowMapping();
+                } else {
+                    expectBlockMapping();
+                }
+            }
+        } else {
+            throw new EmitterException("expected NodeEvent, but got " + event);
+        }
+    }
+
+    private void expectAlias() throws IOException {
+        if (((NodeEvent) event).getAnchor() == null) {
+            throw new EmitterException("anchor is not specified for alias");
+        }
+        processAnchor("*");
+        state = states.pop();
+    }
+
+    private void expectScalar() throws IOException {
+        increaseIndent(true, false);
+        processScalar();
+        indent = indents.pop();
+        state = states.pop();
+    }
+
+    // Flow sequence handlers.
+
+    private void expectFlowSequence() throws IOException {
+        writeIndicator("[", true, true, false);
+        flowLevel++;
+        increaseIndent(true, false);
+        if (prettyFlow) {
+            writeIndent();
+        }
+        state = new ExpectFirstFlowSequenceItem();
+    }
+
+    private class ExpectFirstFlowSequenceItem implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof SequenceEndEvent) {
+                indent = indents.pop();
+                flowLevel--;
+                writeIndicator("]", false, false, false);
+                state = states.pop();
+            } else {
+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
+                    writeIndent();
+                }
+                states.push(new ExpectFlowSequenceItem());
+                expectNode(false, false, false);
+            }
+        }
+    }
+
+    private class ExpectFlowSequenceItem implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof SequenceEndEvent) {
+                indent = indents.pop();
+                flowLevel--;
+                if (canonical) {
+                    writeIndicator(",", false, false, false);
+                    writeIndent();
+                }
+                writeIndicator("]", false, false, false);
+                if (prettyFlow) {
+                    writeIndent();
+                }
+                state = states.pop();
+            } else {
+                writeIndicator(",", false, false, false);
+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
+                    writeIndent();
+                }
+                states.push(new ExpectFlowSequenceItem());
+                expectNode(false, false, false);
+            }
+        }
+    }
+
+    // Flow mapping handlers.
+
+    private void expectFlowMapping() throws IOException {
+        writeIndicator("{", true, true, false);
+        flowLevel++;
+        increaseIndent(true, false);
+        if (prettyFlow) {
+            writeIndent();
+        }
+        state = new ExpectFirstFlowMappingKey();
+    }
+
+    private class ExpectFirstFlowMappingKey implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof MappingEndEvent) {
+                indent = indents.pop();
+                flowLevel--;
+                writeIndicator("}", false, false, false);
+                state = states.pop();
+            } else {
+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
+                    writeIndent();
+                }
+                if (!canonical && checkSimpleKey()) {
+                    states.push(new ExpectFlowMappingSimpleValue());
+                    expectNode(false, true, true);
+                } else {
+                    writeIndicator("?", true, false, false);
+                    states.push(new ExpectFlowMappingValue());
+                    expectNode(false, true, false);
+                }
+            }
+        }
+    }
+
+    private class ExpectFlowMappingKey implements EmitterState {
+        public void expect() throws IOException {
+            if (event instanceof MappingEndEvent) {
+                indent = indents.pop();
+                flowLevel--;
+                if (canonical) {
+                    writeIndicator(",", false, false, false);
+                    writeIndent();
+                }
+                if (prettyFlow) {
+                    writeIndent();
+                }
+                writeIndicator("}", false, false, false);
+                state = states.pop();
+            } else {
+                writeIndicator(",", false, false, false);
+                if (canonical || (column > bestWidth && splitLines) || prettyFlow) {
+                    writeIndent();
+                }
+                if (!canonical && checkSimpleKey()) {
+                    states.push(new ExpectFlowMappingSimpleValue());
+                    expectNode(false, true, true);
+                } else {
+                    writeIndicator("?", true, false, false);
+                    states.push(new ExpectFlowMappingValue());
+                    expectNode(false, true, false);
+                }
+            }
+        }
+    }
+
+    private class ExpectFlowMappingSimpleValue implements EmitterState {
+        public void expect() throws IOException {
+            writeIndicator(":", false, false, false);
+            states.push(new ExpectFlowMappingKey());
+            expectNode(false, true, false);
+        }
+    }
+
+    private class ExpectFlowMappingValue implements EmitterState {
+        public void expect() throws IOException {
+            if (canonical || (column > bestWidth) || prettyFlow) {
+                writeIndent();
+            }
+            writeIndicator(":", true, false, false);
+            states.push(new ExpectFlowMappingKey());
+            expectNode(false, true, false);
+        }
+    }
+
+    // Block sequence handlers.
+
+    private void expectBlockSequence() throws IOException {
+        boolean indentless = mappingContext && !indention;
+        increaseIndent(false, indentless);
+        state = new ExpectFirstBlockSequenceItem();
+    }
+
+    private class ExpectFirstBlockSequenceItem implements EmitterState {
+        public void expect() throws IOException {
+            new ExpectBlockSequenceItem(true).expect();
+        }
+    }
+
+    private class ExpectBlockSequenceItem implements EmitterState {
+        private boolean first;
+
+        public ExpectBlockSequenceItem(boolean first) {
+            this.first = first;
+        }
+
+        public void expect() throws IOException {
+            if (!this.first && event instanceof SequenceEndEvent) {
+                indent = indents.pop();
+                state = states.pop();
+            } else {
+                writeIndent();
+                writeWhitespace(indicatorIndent);
+                writeIndicator("-", true, false, true);
+                states.push(new ExpectBlockSequenceItem(false));
+                expectNode(false, false, false);
+            }
+        }
+    }
+
+    // Block mapping handlers.
+    private void expectBlockMapping() throws IOException {
+        increaseIndent(false, false);
+        state = new ExpectFirstBlockMappingKey();
+    }
+
+    private class ExpectFirstBlockMappingKey implements EmitterState {
+        public void expect() throws IOException {
+            new ExpectBlockMappingKey(true).expect();
+        }
+    }
+
+    private class ExpectBlockMappingKey implements EmitterState {
+        private boolean first;
+
+        public ExpectBlockMappingKey(boolean first) {
+            this.first = first;
+        }
+
+        public void expect() throws IOException {
+            if (!this.first && event instanceof MappingEndEvent) {
+                indent = indents.pop();
+                state = states.pop();
+            } else {
+                writeIndent();
+                if (checkSimpleKey()) {
+                    states.push(new ExpectBlockMappingSimpleValue());
+                    expectNode(false, true, true);
+                } else {
+                    writeIndicator("?", true, false, true);
+                    states.push(new ExpectBlockMappingValue());
+                    expectNode(false, true, false);
+                }
+            }
+        }
+    }
+
+    private class ExpectBlockMappingSimpleValue implements EmitterState {
+        public void expect() throws IOException {
+            writeIndicator(":", false, false, false);
+            states.push(new ExpectBlockMappingKey(false));
+            expectNode(false, true, false);
+        }
+    }
+
+    private class ExpectBlockMappingValue implements EmitterState {
+        public void expect() throws IOException {
+            writeIndent();
+            writeIndicator(":", true, false, true);
+            states.push(new ExpectBlockMappingKey(false));
+            expectNode(false, true, false);
+        }
+    }
+
+    // Checkers.
+
+    private boolean checkEmptySequence() {
+        return event instanceof SequenceStartEvent && !events.isEmpty() && events.peek() instanceof SequenceEndEvent;
+    }
+
+    private boolean checkEmptyMapping() {
+        return event instanceof MappingStartEvent && !events.isEmpty() && events.peek() instanceof MappingEndEvent;
+    }
+
+    private boolean checkEmptyDocument() {
+        if (!(event instanceof DocumentStartEvent) || events.isEmpty()) {
+            return false;
+        }
+        Event event = events.peek();
+        if (event instanceof ScalarEvent) {
+            ScalarEvent e = (ScalarEvent) event;
+            return e.getAnchor() == null && e.getTag() == null && e.getImplicit() != null && e
+                    .getValue().length() == 0;
+        }
+        return false;
+    }
+
+    private boolean checkSimpleKey() {
+        int length = 0;
+        if (event instanceof NodeEvent && ((NodeEvent) event).getAnchor() != null) {
+            if (preparedAnchor == null) {
+                preparedAnchor = prepareAnchor(((NodeEvent) event).getAnchor());
+            }
+            length += preparedAnchor.length();
+        }
+        String tag = null;
+        if (event instanceof ScalarEvent) {
+            tag = ((ScalarEvent) event).getTag();
+        } else if (event instanceof CollectionStartEvent) {
+            tag = ((CollectionStartEvent) event).getTag();
+        }
+        if (tag != null) {
+            if (preparedTag == null) {
+                preparedTag = prepareTag(tag);
+            }
+            length += preparedTag.length();
+        }
+        if (event instanceof ScalarEvent) {
+            if (analysis == null) {
+                analysis = analyzeScalar(((ScalarEvent) event).getValue());
+            }
+            length += analysis.scalar.length();
+        }
+        return length < 128 && (event instanceof AliasEvent
+                || (event instanceof ScalarEvent && !analysis.empty && !analysis.multiline)
+                || checkEmptySequence() || checkEmptyMapping());
+    }
+
+    // Anchor, Tag, and Scalar processors.
+
+    private void processAnchor(String indicator) throws IOException {
+        NodeEvent ev = (NodeEvent) event;
+        if (ev.getAnchor() == null) {
+            preparedAnchor = null;
+            return;
+        }
+        if (preparedAnchor == null) {
+            preparedAnchor = prepareAnchor(ev.getAnchor());
+        }
+        writeIndicator(indicator + preparedAnchor, true, false, false);
+        preparedAnchor = null;
+    }
+
+    private void processTag() throws IOException {
+        String tag = null;
+        if (event instanceof ScalarEvent) {
+            ScalarEvent ev = (ScalarEvent) event;
+            tag = ev.getTag();
+            if (style == null) {
+                style = chooseScalarStyle();
+            }
+            if ((!canonical || tag == null) && ((style == null && ev.getImplicit()
+                    .canOmitTagInPlainScalar()) || (style != null && ev.getImplicit()
+                    .canOmitTagInNonPlainScalar()))) {
+                preparedTag = null;
+                return;
+            }
+            if (ev.getImplicit().canOmitTagInPlainScalar() && tag == null) {
+                tag = "!";
+                preparedTag = null;
+            }
+        } else {
+            CollectionStartEvent ev = (CollectionStartEvent) event;
+            tag = ev.getTag();
+            if ((!canonical || tag == null) && ev.getImplicit()) {
+                preparedTag = null;
+                return;
+            }
+        }
+        if (tag == null) {
+            throw new EmitterException("tag is not specified");
+        }
+        if (preparedTag == null) {
+            preparedTag = prepareTag(tag);
+        }
+        writeIndicator(preparedTag, true, false, false);
+        preparedTag = null;
+    }
+
+    private Character chooseScalarStyle() {
+        ScalarEvent ev = (ScalarEvent) event;
+        if (analysis == null) {
+            analysis = analyzeScalar(ev.getValue());
+        }
+        if (ev.getStyle() != null && ev.getStyle() == '"' || this.canonical) {
+            return '"';
+        }
+        if (ev.getStyle() == null && ev.getImplicit().canOmitTagInPlainScalar()) {
+            if (!(simpleKeyContext && (analysis.empty || analysis.multiline))
+                    && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) {
+                return null;
+            }
+        }
+        if (ev.getStyle() != null && (ev.getStyle() == '|' || ev.getStyle() == '>')) {
+            if (flowLevel == 0 && !simpleKeyContext && analysis.allowBlock) {
+                return ev.getStyle();
+            }
+        }
+        if (ev.getStyle() == null || ev.getStyle() == '\'') {
+            if (analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline)) {
+                return '\'';
+            }
+        }
+        return '"';
+    }
+
+    private void processScalar() throws IOException {
+        ScalarEvent ev = (ScalarEvent) event;
+        if (analysis == null) {
+            analysis = analyzeScalar(ev.getValue());
+        }
+        if (style == null) {
+            style = chooseScalarStyle();
+        }
+        boolean split = !simpleKeyContext && splitLines;
+        if (style == null) {
+            writePlain(analysis.scalar, split);
+        } else {
+            switch (style) {
+            case '"':
+                writeDoubleQuoted(analysis.scalar, split);
+                break;
+            case '\'':
+                writeSingleQuoted(analysis.scalar, split);
+                break;
+            case '>':
+                writeFolded(analysis.scalar, split);
+                break;
+            case '|':
+                writeLiteral(analysis.scalar);
+                break;
+            default:
+                throw new YAMLException("Unexpected style: " + style);
+            }
+        }
+        analysis = null;
+        style = null;
+    }
+
+    // Analyzers.
+
+    private String prepareVersion(Version version) {
+        if (version.major() != 1) {
+            throw new EmitterException("unsupported YAML version: " + version);
+        }
+        return version.getRepresentation();
+    }
+
+    private final static Pattern HANDLE_FORMAT = Pattern.compile("^![-_\\w]*!$");
+
+    private String prepareTagHandle(String handle) {
+        if (handle.length() == 0) {
+            throw new EmitterException("tag handle must not be empty");
+        } else if (handle.charAt(0) != '!' || handle.charAt(handle.length() - 1) != '!') {
+            throw new EmitterException("tag handle must start and end with '!': " + handle);
+        } else if (!"!".equals(handle) && !HANDLE_FORMAT.matcher(handle).matches()) {
+            throw new EmitterException("invalid character in the tag handle: " + handle);
+        }
+        return handle;
+    }
+
+    private String prepareTagPrefix(String prefix) {
+        if (prefix.length() == 0) {
+            throw new EmitterException("tag prefix must not be empty");
+        }
+        StringBuilder chunks = new StringBuilder();
+        int start = 0;
+        int end = 0;
+        if (prefix.charAt(0) == '!') {
+            end = 1;
+        }
+        while (end < prefix.length()) {
+            end++;
+        }
+        if (start < end) {
+            chunks.append(prefix.substring(start, end));
+        }
+        return chunks.toString();
+    }
+
+    private String prepareTag(String tag) {
+        if (tag.length() == 0) {
+            throw new EmitterException("tag must not be empty");
+        }
+        if ("!".equals(tag)) {
+            return tag;
+        }
+        String handle = null;
+        String suffix = tag;
+        // shall the tag prefixes be sorted as in PyYAML?
+        for (String prefix : tagPrefixes.keySet()) {
+            if (tag.startsWith(prefix) && ("!".equals(prefix) || prefix.length() < tag.length())) {
+                handle = prefix;
+            }
+        }
+        if (handle != null) {
+            suffix = tag.substring(handle.length());
+            handle = tagPrefixes.get(handle);
+        }
+
+        int end = suffix.length();
+        String suffixText = end > 0 ? suffix.substring(0, end) : "";
+
+        if (handle != null) {
+            return handle + suffixText;
+        }
+        return "!<" + suffixText + ">";
+    }
+
+    private final static Pattern ANCHOR_FORMAT = Pattern.compile("^[-_\\w]*$");
+
+    static String prepareAnchor(String anchor) {
+        if (anchor.length() == 0) {
+            throw new EmitterException("anchor must not be empty");
+        }
+        if (!ANCHOR_FORMAT.matcher(anchor).matches()) {
+            throw new EmitterException("invalid character in the anchor: " + anchor);
+        }
+        return anchor;
+    }
+
+    private ScalarAnalysis analyzeScalar(String scalar) {
+        // Empty scalar is a special case.
+        if (scalar.length() == 0) {
+            return new ScalarAnalysis(scalar, true, false, false, true, true, false);
+        }
+        // Indicators and special characters.
+        boolean blockIndicators = false;
+        boolean flowIndicators = false;
+        boolean lineBreaks = false;
+        boolean specialCharacters = false;
+
+        // Important whitespace combinations.
+        boolean leadingSpace = false;
+        boolean leadingBreak = false;
+        boolean trailingSpace = false;
+        boolean trailingBreak = false;
+        boolean breakSpace = false;
+        boolean spaceBreak = false;
+
+        // Check document indicators.
+        if (scalar.startsWith("---") || scalar.startsWith("...")) {
+            blockIndicators = true;
+            flowIndicators = true;
+        }
+        // First character or preceded by a whitespace.
+        boolean preceededByWhitespace = true;
+        boolean followedByWhitespace = scalar.length() == 1 || Constant.NULL_BL_T_LINEBR.has(scalar.charAt(1));
+        // The previous character is a space.
+        boolean previousSpace = false;
+
+        // The previous character is a break.
+        boolean previousBreak = false;
+
+        int index = 0;
+
+        while (index < scalar.length()) {
+            char ch = scalar.charAt(index);
+            // Check for indicators.
+            if (index == 0) {
+                // Leading indicators are special characters.
+                if ("#,[]{}&*!|>\'\"%@`".indexOf(ch) != -1) {
+                    flowIndicators = true;
+                    blockIndicators = true;
+                }
+                if (ch == '?' || ch == ':') {
+                    flowIndicators = true;
+                    if (followedByWhitespace) {
+                        blockIndicators = true;
+                    }
+                }
+                if (ch == '-' && followedByWhitespace) {
+                    flowIndicators = true;
+                    blockIndicators = true;
+                }
+            } else {
+                // Some indicators cannot appear within a scalar as well.
+                if (",?[]{}".indexOf(ch) != -1) {
+                    flowIndicators = true;
+                }
+                if (ch == ':') {
+                    flowIndicators = true;
+                    if (followedByWhitespace) {
+                        blockIndicators = true;
+                    }
+                }
+                if (ch == '#' && preceededByWhitespace) {
+                    flowIndicators = true;
+                    blockIndicators = true;
+                }
+            }
+            // Check for line breaks, special, and unicode characters.
+            boolean isLineBreak = Constant.LINEBR.has(ch);
+            if (isLineBreak) {
+                lineBreaks = true;
+            }
+            if (!(ch == '\n' || ('\u0020' <= ch && ch <= '\u007E'))) {
+                if ((ch == '\u0085' || ('\u00A0' <= ch && ch <= '\uD7FF') || ('\uE000' <= ch && ch <= '\uFFFD'))
+                        && (ch != '\uFEFF')) {
+                    // unicode is used
+                    if (!this.allowUnicode) {
+                        specialCharacters = true;
+                    }
+                } else {
+                    specialCharacters = true;
+                }
+            }
+            // Detect important whitespace combinations.
+            if (ch == ' ') {
+                if (index == 0) {
+                    leadingSpace = true;
+                }
+                if (index == scalar.length() - 1) {
+                    trailingSpace = true;
+                }
+                if (previousBreak) {
+                    breakSpace = true;
+                }
+                previousSpace = true;
+                previousBreak = false;
+            } else if (isLineBreak) {
+                if (index == 0) {
+                    leadingBreak = true;
+                }
+                if (index == scalar.length() - 1) {
+                    trailingBreak = true;
+                }
+                if (previousSpace) {
+                    spaceBreak = true;
+                }
+                previousSpace = false;
+                previousBreak = true;
+            } else {
+                previousSpace = false;
+                previousBreak = false;
+            }
+
+            // Prepare for the next character.
+            index++;
+            preceededByWhitespace = Constant.NULL_BL_T.has(ch) || isLineBreak;
+            followedByWhitespace = index + 1 >= scalar.length()
+                    || (Constant.NULL_BL_T.has(scalar.charAt(index + 1))) || isLineBreak;
+        }
+        // Let's decide what styles are allowed.
+        boolean allowFlowPlain = true;
+        boolean allowBlockPlain = true;
+        boolean allowSingleQuoted = true;
+        boolean allowBlock = true;
+        // Leading and trailing whitespaces are bad for plain scalars.
+        if (leadingSpace || leadingBreak || trailingSpace || trailingBreak) {
+            allowFlowPlain = allowBlockPlain = false;
+        }
+        // We do not permit trailing spaces for block scalars.
+        if (trailingSpace) {
+            allowBlock = false;
+        }
+        // Spaces at the beginning of a new line are only acceptable for block
+        // scalars.
+        if (breakSpace) {
+            allowFlowPlain = allowBlockPlain = allowSingleQuoted = false;
+        }
+        // Spaces followed by breaks, as well as special character are only
+        // allowed for double quoted scalars.
+        if (spaceBreak || specialCharacters) {
+            allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false;
+        }
+        // Although the plain scalar writer supports breaks, we never emit
+        // multiline plain scalars in the flow context.
+        if (lineBreaks) {
+            allowFlowPlain = false;
+        }
+        // Flow indicators are forbidden for flow plain scalars.
+        if (flowIndicators) {
+            allowFlowPlain = false;
+        }
+        // Block indicators are forbidden for block plain scalars.
+        if (blockIndicators) {
+            allowBlockPlain = false;
+        }
+
+        return new ScalarAnalysis(scalar, false, lineBreaks, allowFlowPlain, allowBlockPlain,
+                allowSingleQuoted, allowBlock);
+    }
+
+    // Writers.
+
+    void flushStream() throws IOException {
+        stream.flush();
+    }
+
+    void writeStreamStart() {
+        // BOM is written by Writer.
+    }
+
+    void writeStreamEnd() throws IOException {
+        flushStream();
+    }
+
+    void writeIndicator(String indicator, boolean needWhitespace, boolean whitespace,
+            boolean indentation) throws IOException {
+        if (!this.whitespace && needWhitespace) {
+            this.column++;
+            stream.write(SPACE);
+        }
+        this.whitespace = whitespace;
+        this.indention = this.indention && indentation;
+        this.column += indicator.length();
+        openEnded = false;
+        stream.write(indicator);
+    }
+
+    void writeIndent() throws IOException {
+        int indent;
+        if (this.indent != null) {
+            indent = this.indent;
+        } else {
+            indent = 0;
+        }
+
+        if (!this.indention || this.column > indent || (this.column == indent && !this.whitespace)) {
+            writeLineBreak(null);
+        }
+
+        writeWhitespace(indent - this.column);
+    }
+
+    private void writeWhitespace(int length) throws IOException {
+        if (length <= 0) {
+            return;
+        }
+        this.whitespace = true;
+        char[] data = new char[length];
+        for (int i = 0; i < data.length; i++) {
+            data[i] = ' ';
+        }
+        this.column += length;
+        stream.write(data);
+    }
+
+    private void writeLineBreak(String data) throws IOException {
+        this.whitespace = true;
+        this.indention = true;
+        this.column = 0;
+        if (data == null) {
+            stream.write(this.bestLineBreak);
+        } else {
+            stream.write(data);
+        }
+    }
+
+    void writeVersionDirective(String versionText) throws IOException {
+        stream.write("%YAML ");
+        stream.write(versionText);
+        writeLineBreak(null);
+    }
+
+    void writeTagDirective(String handleText, String prefixText) throws IOException {
+        // XXX: not sure 4 invocations better then StringBuilders created by str
+        // + str
+        stream.write("%TAG ");
+        stream.write(handleText);
+        stream.write(SPACE);
+        stream.write(prefixText);
+        writeLineBreak(null);
+    }
+
+    // Scalar streams.
+    private void writeSingleQuoted(String text, boolean split) throws IOException {
+        writeIndicator("'", true, false, false);
+        boolean spaces = false;
+        boolean breaks = false;
+        int start = 0, end = 0;
+        char ch;
+        while (end <= text.length()) {
+            ch = 0;
+            if (end < text.length()) {
+                ch = text.charAt(end);
+            }
+            if (spaces) {
+                if (ch == 0 || ch != ' ') {
+                    if (start + 1 == end && this.column > this.bestWidth && split && start != 0
+                            && end != text.length()) {
+                        writeIndent();
+                    } else {
+                        int len = end - start;
+                        this.column += len;
+                        stream.write(text, start, len);
+                    }
+                    start = end;
+                }
+            } else if (breaks) {
+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
+                    if (text.charAt(start) == '\n') {
+                        writeLineBreak(null);
+                    }
+                    String data = text.substring(start, end);
+                    for (char br : data.toCharArray()) {
+                        if (br == '\n') {
+                            writeLineBreak(null);
+                        } else {
+                            writeLineBreak(String.valueOf(br));
+                        }
+                    }
+                    writeIndent();
+                    start = end;
+                }
+            } else {
+                if (Constant.LINEBR.has(ch, "\0 \'")) {
+                    if (start < end) {
+                        int len = end - start;
+                        this.column += len;
+                        stream.write(text, start, len);
+                        start = end;
+                    }
+                }
+            }
+            if (ch == '\'') {
+                this.column += 2;
+                stream.write("''");
+                start = end + 1;
+            }
+            if (ch != 0) {
+                spaces = ch == ' ';
+                breaks = Constant.LINEBR.has(ch);
+            }
+            end++;
+        }
+        writeIndicator("'", false, false, false);
+    }
+
+    private void writeDoubleQuoted(String text, boolean split) throws IOException {
+        writeIndicator("\"", true, false, false);
+        int start = 0;
+        int end = 0;
+        while (end <= text.length()) {
+            Character ch = null;
+            if (end < text.length()) {
+                ch = text.charAt(end);
+            }
+            if (ch == null || "\"\\\u0085\u2028\u2029\uFEFF".indexOf(ch) != -1
+                    || !('\u0020' <= ch && ch <= '\u007E')) {
+                if (start < end) {
+                    int len = end - start;
+                    this.column += len;
+                    stream.write(text, start, len);
+                    start = end;
+                }
+                if (ch != null) {
+                    String data;
+                    if (ESCAPE_REPLACEMENTS.containsKey(ch)) {
+                        data = "\\" + ESCAPE_REPLACEMENTS.get(ch);
+                    } else if (!this.allowUnicode || !StreamReader.isPrintable(ch)) {
+                        // if !allowUnicode or the character is not printable,
+                        // we must encode it
+                        if (ch <= '\u00FF') {
+                            String s = "0" + Integer.toString(ch, 16);
+                            data = "\\x" + s.substring(s.length() - 2);
+                        } else if (ch >= '\uD800' && ch <= '\uDBFF') {
+                            if (end + 1 < text.length()) {
+                                Character ch2 = text.charAt(++end);
+                                String s = "000" + Long.toHexString(Character.toCodePoint(ch, ch2));
+                                data = "\\U" + s.substring(s.length() - 8);
+                            } else {
+                                String s = "000" + Integer.toString(ch, 16);
+                                data = "\\u" + s.substring(s.length() - 4);
+                            }
+                        } else {
+                            String s = "000" + Integer.toString(ch, 16);
+                            data = "\\u" + s.substring(s.length() - 4);
+                        }
+                    } else {
+                        data = String.valueOf(ch);
+                    }
+                    this.column += data.length();
+                    stream.write(data);
+                    start = end + 1;
+                }
+            }
+            if ((0 < end && end < (text.length() - 1)) && (ch == ' ' || start >= end)
+                    && (this.column + (end - start)) > this.bestWidth && split) {
+                String data;
+                if (start >= end) {
+                    data = "\\";
+                } else {
+                    data = text.substring(start, end) + "\\";
+                }
+                if (start < end) {
+                    start = end;
+                }
+                this.column += data.length();
+                stream.write(data);
+                writeIndent();
+                this.whitespace = false;
+                this.indention = false;
+                if (text.charAt(start) == ' ') {
+                    data = "\\";
+                    this.column += data.length();
+                    stream.write(data);
+                }
+            }
+            end += 1;
+        }
+        writeIndicator("\"", false, false, false);
+    }
+
+    private String determineBlockHints(String text) {
+        StringBuilder hints = new StringBuilder();
+        if (Constant.LINEBR.has(text.charAt(0), " ")) {
+            hints.append(bestIndent);
+        }
+        char ch1 = text.charAt(text.length() - 1);
+        if (Constant.LINEBR.hasNo(ch1)) {
+            hints.append("-");
+        } else if (text.length() == 1 || Constant.LINEBR.has(text.charAt(text.length() - 2))) {
+            hints.append("+");
+        }
+        return hints.toString();
+    }
+
+    void writeFolded(String text, boolean split) throws IOException {
+        String hints = determineBlockHints(text);
+        writeIndicator(">" + hints, true, false, false);
+        if (hints.length() > 0 && (hints.charAt(hints.length() - 1) == '+')) {
+            openEnded = true;
+        }
+        writeLineBreak(null);
+        boolean leadingSpace = true;
+        boolean spaces = false;
+        boolean breaks = true;
+        int start = 0, end = 0;
+        while (end <= text.length()) {
+            char ch = 0;
+            if (end < text.length()) {
+                ch = text.charAt(end);
+            }
+            if (breaks) {
+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
+                    if (!leadingSpace && ch != 0 && ch != ' ' && text.charAt(start) == '\n') {
+                        writeLineBreak(null);
+                    }
+                    leadingSpace = ch == ' ';
+                    String data = text.substring(start, end);
+                    for (char br : data.toCharArray()) {
+                        if (br == '\n') {
+                            writeLineBreak(null);
+                        } else {
+                            writeLineBreak(String.valueOf(br));
+                        }
+                    }
+                    if (ch != 0) {
+                        writeIndent();
+                    }
+                    start = end;
+                }
+            } else if (spaces) {
+                if (ch != ' ') {
+                    if (start + 1 == end && this.column > this.bestWidth && split) {
+                        writeIndent();
+                    } else {
+                        int len = end - start;
+                        this.column += len;
+                        stream.write(text, start, len);
+                    }
+                    start = end;
+                }
+            } else {
+                if (Constant.LINEBR.has(ch, "\0 ")) {
+                    int len = end - start;
+                    this.column += len;
+                    stream.write(text, start, len);
+                    if (ch == 0) {
+                        writeLineBreak(null);
+                    }
+                    start = end;
+                }
+            }
+            if (ch != 0) {
+                breaks = Constant.LINEBR.has(ch);
+                spaces = ch == ' ';
+            }
+            end++;
+        }
+    }
+
+    void writeLiteral(String text) throws IOException {
+        String hints = determineBlockHints(text);
+        writeIndicator("|" + hints, true, false, false);
+        if (hints.length() > 0 && (hints.charAt(hints.length() - 1)) == '+') {
+            openEnded = true;
+        }
+        writeLineBreak(null);
+        boolean breaks = true;
+        int start = 0, end = 0;
+        while (end <= text.length()) {
+            char ch = 0;
+            if (end < text.length()) {
+                ch = text.charAt(end);
+            }
+            if (breaks) {
+                if (ch == 0 || Constant.LINEBR.hasNo(ch)) {
+                    String data = text.substring(start, end);
+                    for (char br : data.toCharArray()) {
+                        if (br == '\n') {
+                            writeLineBreak(null);
+                        } else {
+                            writeLineBreak(String.valueOf(br));
+                        }
+                    }
+                    if (ch != 0) {
+                        writeIndent();
+                    }
+                    start = end;
+                }
+            } else {
+                if (ch == 0 || Constant.LINEBR.has(ch)) {
+                    stream.write(text, start, end - start);
+                    if (ch == 0) {
+                        writeLineBreak(null);
+                    }
+                    start = end;
+                }
+            }
+            if (ch != 0) {
+                breaks = Constant.LINEBR.has(ch);
+            }
+            end++;
+        }
+    }
+
+    void writePlain(String text, boolean split) throws IOException {
+        if (rootContext) {
+            openEnded = true;
+        }
+        if (text.length() == 0) {
+            return;
+        }
+        if (!this.whitespace) {
+            this.column++;
+            stream.write(SPACE);
+        }
+        this.whitespace = false;
+        this.indention = false;
+        boolean spaces = false;
+        boolean breaks = false;
+        int start = 0, end = 0;
+        while (end <= text.length()) {
+            char ch = 0;
+            if (end < text.length()) {
+                ch = text.charAt(end);
+            }
+            if (spaces) {
+                if (ch != ' ') {
+                    if (start + 1 == end && this.column > this.bestWidth && split) {
+                        writeIndent();
+                        this.whitespace = false;
+                        this.indention = false;
+                    } else {
+                        int len = end - start;
+                        this.column += len;
+                        stream.write(text, start, len);
+                    }
+                    start = end;
+                }
+            } else if (breaks) {
+                if (Constant.LINEBR.hasNo(ch)) {
+                    if (text.charAt(start) == '\n') {
+                        writeLineBreak(null);
+                    }
+                    String data = text.substring(start, end);
+                    for (char br : data.toCharArray()) {
+                        if (br == '\n') {
+                            writeLineBreak(null);
+                        } else {
+                            writeLineBreak(String.valueOf(br));
+                        }
+                    }
+                    writeIndent();
+                    this.whitespace = false;
+                    this.indention = false;
+                    start = end;
+                }
+            } else {
+                if (ch == 0 || Constant.LINEBR.has(ch)) {
+                    int len = end - start;
+                    this.column += len;
+                    stream.write(text, start, len);
+                    start = end;
+                }
+            }
+            if (ch != 0) {
+                spaces = ch == ' ';
+                breaks = Constant.LINEBR.has(ch);
+            }
+            end++;
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/EmitterException.java b/src/main/java/org/yaml/snakeyaml/emitter/EmitterException.java
new file mode 100644
index 0000000..ed83fee
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/emitter/EmitterException.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class EmitterException extends YAMLException {
+    private static final long serialVersionUID = -8280070025452995908L;
+
+    public EmitterException(String msg) {
+        super(msg);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/EmitterState.java b/src/main/java/org/yaml/snakeyaml/emitter/EmitterState.java
new file mode 100755
index 0000000..8e6622e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/emitter/EmitterState.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.io.IOException;
+
+/**
+ * Python's methods are first class object. Java needs a class.
+ */
+interface EmitterState {
+    void expect() throws IOException;
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/ScalarAnalysis.java b/src/main/java/org/yaml/snakeyaml/emitter/ScalarAnalysis.java
new file mode 100644
index 0000000..99a0829
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/emitter/ScalarAnalysis.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+public final class ScalarAnalysis {
+    public String scalar;
+    public boolean empty;
+    public boolean multiline;
+    public boolean allowFlowPlain;
+    public boolean allowBlockPlain;
+    public boolean allowSingleQuoted;
+    public boolean allowBlock;
+
+    public ScalarAnalysis(String scalar, boolean empty, boolean multiline, boolean allowFlowPlain,
+            boolean allowBlockPlain, boolean allowSingleQuoted, boolean allowBlock) {
+        this.scalar = scalar;
+        this.empty = empty;
+        this.multiline = multiline;
+        this.allowFlowPlain = allowFlowPlain;
+        this.allowBlockPlain = allowBlockPlain;
+        this.allowSingleQuoted = allowSingleQuoted;
+        this.allowBlock = allowBlock;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/error/Mark.java b/src/main/java/org/yaml/snakeyaml/error/Mark.java
new file mode 100644
index 0000000..7e1d546
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/error/Mark.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.error;
+
+import org.yaml.snakeyaml.scanner.Constant;
+
+/**
+ * It's just a record and its only use is producing nice error messages. Parser
+ * does not use it for any other purposes.
+ */
+public final class Mark {
+    private String name;
+    private int index;
+    private int line;
+    private int column;
+    private String buffer;
+    private int pointer;
+
+    public Mark(String name, int index, int line, int column, String buffer, int pointer) {
+        super();
+        this.name = name;
+        this.index = index;
+        this.line = line;
+        this.column = column;
+        this.buffer = buffer;
+        this.pointer = pointer;
+    }
+
+    private boolean isLineBreak(char ch) {
+        return Constant.NULL_OR_LINEBR.has(ch);
+    }
+
+    public String get_snippet(int indent, int max_length) {
+        if (buffer == null) {
+            return null;
+        }
+        float half = max_length / 2 - 1;
+        int start = pointer;
+        String head = "";
+        while ((start > 0) && !isLineBreak(buffer.charAt(start - 1))) {
+            start -= 1;
+            if (pointer - start > half) {
+                head = " ... ";
+                start += 5;
+                break;
+            }
+        }
+        String tail = "";
+        int end = pointer;
+        while ((end < buffer.length()) && !isLineBreak(buffer.charAt(end))) {
+            end += 1;
+            if (end - pointer > half) {
+                tail = " ... ";
+                end -= 5;
+                break;
+            }
+        }
+        String snippet = buffer.substring(start, end);
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < indent; i++) {
+            result.append(" ");
+        }
+        result.append(head);
+        result.append(snippet);
+        result.append(tail);
+        result.append("\n");
+        for (int i = 0; i < indent + pointer - start + head.length(); i++) {
+            result.append(" ");
+        }
+        result.append("^");
+        return result.toString();
+    }
+
+    public String get_snippet() {
+        return get_snippet(4, 75);
+    }
+
+    @Override
+    public String toString() {
+        String snippet = get_snippet();
+        StringBuilder where = new StringBuilder(" in ");
+        where.append(name);
+        where.append(", line ");
+        where.append(line + 1);
+        where.append(", column ");
+        where.append(column + 1);
+        if (snippet != null) {
+            where.append(":\n");
+            where.append(snippet);
+        }
+        return where.toString();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * starts with 0
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /**
+     * starts with 0
+     */
+    public int getColumn() {
+        return column;
+    }
+
+    /**
+     * starts with 0
+     */
+    public int getIndex() {
+        return index;
+    }
+
+}
diff --git a/src/main/java/org/yaml/snakeyaml/error/MarkedYAMLException.java b/src/main/java/org/yaml/snakeyaml/error/MarkedYAMLException.java
new file mode 100644
index 0000000..4e44ab9
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/error/MarkedYAMLException.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.error;
+
+public class MarkedYAMLException extends YAMLException {
+
+    private static final long serialVersionUID = -9119388488683035101L;
+    private String context;
+    private Mark contextMark;
+    private String problem;
+    private Mark problemMark;
+    private String note;
+
+    protected MarkedYAMLException(String context, Mark contextMark, String problem,
+            Mark problemMark, String note) {
+        this(context, contextMark, problem, problemMark, note, null);
+    }
+
+    protected MarkedYAMLException(String context, Mark contextMark, String problem,
+            Mark problemMark, String note, Throwable cause) {
+        super(context + "; " + problem + "; " + problemMark, cause);
+        this.context = context;
+        this.contextMark = contextMark;
+        this.problem = problem;
+        this.problemMark = problemMark;
+        this.note = note;
+    }
+
+    protected MarkedYAMLException(String context, Mark contextMark, String problem, Mark problemMark) {
+        this(context, contextMark, problem, problemMark, null, null);
+    }
+
+    protected MarkedYAMLException(String context, Mark contextMark, String problem,
+            Mark problemMark, Throwable cause) {
+        this(context, contextMark, problem, problemMark, null, cause);
+    }
+
+    @Override
+    public String getMessage() {
+        return toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder lines = new StringBuilder();
+        if (context != null) {
+            lines.append(context);
+            lines.append("\n");
+        }
+        if (contextMark != null
+                && (problem == null || problemMark == null
+                        || contextMark.getName().equals(problemMark.getName())
+                        || (contextMark.getLine() != problemMark.getLine()) || (contextMark
+                        .getColumn() != problemMark.getColumn()))) {
+            lines.append(contextMark.toString());
+            lines.append("\n");
+        }
+        if (problem != null) {
+            lines.append(problem);
+            lines.append("\n");
+        }
+        if (problemMark != null) {
+            lines.append(problemMark.toString());
+            lines.append("\n");
+        }
+        if (note != null) {
+            lines.append(note);
+            lines.append("\n");
+        }
+        return lines.toString();
+    }
+
+    public String getContext() {
+        return context;
+    }
+
+    public Mark getContextMark() {
+        return contextMark;
+    }
+
+    public String getProblem() {
+        return problem;
+    }
+
+    public Mark getProblemMark() {
+        return problemMark;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/error/YAMLException.java b/src/main/java/org/yaml/snakeyaml/error/YAMLException.java
new file mode 100644
index 0000000..af7189b
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/error/YAMLException.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.error;
+
+public class YAMLException extends RuntimeException {
+    private static final long serialVersionUID = -4738336175050337570L;
+
+    public YAMLException(String message) {
+        super(message);
+    }
+
+    public YAMLException(Throwable cause) {
+        super(cause);
+    }
+
+    public YAMLException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/AliasEvent.java b/src/main/java/org/yaml/snakeyaml/events/AliasEvent.java
new file mode 100644
index 0000000..e0dcf7f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/AliasEvent.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the inclusion of a previously anchored node.
+ */
+public final class AliasEvent extends NodeEvent {
+    public AliasEvent(String anchor, Mark startMark, Mark endMark) {
+        super(anchor, startMark, endMark);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.Alias == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/CollectionEndEvent.java b/src/main/java/org/yaml/snakeyaml/events/CollectionEndEvent.java
new file mode 100644
index 0000000..b36f32c
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/CollectionEndEvent.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Base class for the end events of the collection nodes.
+ */
+public abstract class CollectionEndEvent extends Event {
+
+    public CollectionEndEvent(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java
new file mode 100644
index 0000000..9a77299
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/CollectionStartEvent.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Base class for the start events of the collection nodes.
+ */
+public abstract class CollectionStartEvent extends NodeEvent {
+    private final String tag;
+    // The implicit flag of a collection start event indicates if the tag may be
+    // omitted when the collection is emitted
+    private final boolean implicit;
+    // flag indicates if a collection is block or flow
+    private final Boolean flowStyle;
+
+    public CollectionStartEvent(String anchor, String tag, boolean implicit, Mark startMark,
+            Mark endMark, Boolean flowStyle) {
+        super(anchor, startMark, endMark);
+        this.tag = tag;
+        this.implicit = implicit;
+        this.flowStyle = flowStyle;
+    }
+
+    /**
+     * Tag of this collection.
+     * 
+     * @return The tag of this collection, or <code>null</code> if no explicit
+     *         tag is available.
+     */
+    public String getTag() {
+        return this.tag;
+    }
+
+    /**
+     * <code>true</code> if the tag can be omitted while this collection is
+     * emitted.
+     * 
+     * @return True if the tag can be omitted while this collection is emitted.
+     */
+    public boolean getImplicit() {
+        return this.implicit;
+    }
+
+    /**
+     * <code>true</code> if this collection is in flow style, <code>false</code>
+     * for block style.
+     * 
+     * @return If this collection is in flow style.
+     */
+    public Boolean getFlowStyle() {
+        return this.flowStyle;
+    }
+
+    @Override
+    protected String getArguments() {
+        return super.getArguments() + ", tag=" + tag + ", implicit=" + implicit;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/DocumentEndEvent.java b/src/main/java/org/yaml/snakeyaml/events/DocumentEndEvent.java
new file mode 100644
index 0000000..30fe439
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/DocumentEndEvent.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the end of a document.
+ * <p>
+ * This event follows the document's content.
+ * </p>
+ */
+public final class DocumentEndEvent extends Event {
+    private final boolean explicit;
+
+    public DocumentEndEvent(Mark startMark, Mark endMark, boolean explicit) {
+        super(startMark, endMark);
+        this.explicit = explicit;
+    }
+
+    public boolean getExplicit() {
+        return explicit;
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.DocumentEnd == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/DocumentStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/DocumentStartEvent.java
new file mode 100644
index 0000000..fa24cdf
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/DocumentStartEvent.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the beginning of a document.
+ * <p>
+ * This event followed by the document's content and a {@link DocumentEndEvent}.
+ * </p>
+ */
+public final class DocumentStartEvent extends Event {
+    private final boolean explicit;
+    private final Version version;
+    private final Map<String, String> tags;
+
+    public DocumentStartEvent(Mark startMark, Mark endMark, boolean explicit, Version version,
+            Map<String, String> tags) {
+        super(startMark, endMark);
+        this.explicit = explicit;
+        this.version = version;
+        // TODO enforce not null
+        // if (tags == null) {
+        // throw new NullPointerException("Tags must be provided.");
+        // }
+        this.tags = tags;
+    }
+
+    public boolean getExplicit() {
+        return explicit;
+    }
+
+    /**
+     * YAML version the document conforms to.
+     * 
+     * @return <code>null</code>if the document has no explicit
+     *         <code>%YAML</code> directive. Otherwise an array with two
+     *         components, the major and minor part of the version (in this
+     *         order).
+     */
+    public Version getVersion() {
+        return version;
+    }
+
+    /**
+     * Tag shorthands as defined by the <code>%TAG</code> directive.
+     * 
+     * @return Mapping of 'handles' to 'prefixes' (the handles include the '!'
+     *         characters).
+     */
+    public Map<String, String> getTags() {
+        return tags;
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.DocumentStart == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/Event.java b/src/main/java/org/yaml/snakeyaml/events/Event.java
new file mode 100644
index 0000000..4ebfa92
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/Event.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Basic unit of output from a {@link org.yaml.snakeyaml.parser.Parser} or input
+ * of a {@link org.yaml.snakeyaml.emitter.Emitter}.
+ */
+public abstract class Event {
+    public enum ID {
+        Alias, DocumentEnd, DocumentStart, MappingEnd, MappingStart, Scalar, SequenceEnd, SequenceStart, StreamEnd, StreamStart
+    }
+
+    private final Mark startMark;
+    private final Mark endMark;
+
+    public Event(Mark startMark, Mark endMark) {
+        this.startMark = startMark;
+        this.endMark = endMark;
+    }
+
+    public String toString() {
+        return "<" + this.getClass().getName() + "(" + getArguments() + ")>";
+    }
+
+    public Mark getStartMark() {
+        return startMark;
+    }
+
+    public Mark getEndMark() {
+        return endMark;
+    }
+
+    /**
+     * @see "__repr__ for Event in PyYAML"
+     */
+    protected String getArguments() {
+        return "";
+    }
+
+    public abstract boolean is(Event.ID id);
+
+    /*
+     * for tests only
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Event) {
+            return toString().equals(obj.toString());
+        } else {
+            return false;
+        }
+    }
+
+    /*
+     * for tests only
+     */
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/ImplicitTuple.java b/src/main/java/org/yaml/snakeyaml/events/ImplicitTuple.java
new file mode 100644
index 0000000..86a99ac
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/ImplicitTuple.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+/**
+ * The implicit flag of a scalar event is a pair of boolean values that indicate
+ * if the tag may be omitted when the scalar is emitted in a plain and non-plain
+ * style correspondingly.
+ * 
+ * @see <a href="http://pyyaml.org/wiki/PyYAMLDocumentation#Events">Events</a>
+ */
+public class ImplicitTuple {
+    private final boolean plain;
+    private final boolean nonPlain;
+
+    public ImplicitTuple(boolean plain, boolean nonplain) {
+        this.plain = plain;
+        this.nonPlain = nonplain;
+    }
+
+    /**
+     * @return true when tag may be omitted when the scalar is emitted in a
+     *         plain style.
+     */
+    public boolean canOmitTagInPlainScalar() {
+        return plain;
+    }
+
+    /**
+     * @return true when tag may be omitted when the scalar is emitted in a
+     *         non-plain style.
+     */
+    public boolean canOmitTagInNonPlainScalar() {
+        return nonPlain;
+    }
+
+    public boolean bothFalse() {
+        return !plain && !nonPlain;
+    }
+
+    @Override
+    public String toString() {
+        return "implicit=[" + plain + ", " + nonPlain + "]";
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/MappingEndEvent.java b/src/main/java/org/yaml/snakeyaml/events/MappingEndEvent.java
new file mode 100644
index 0000000..618c916
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/MappingEndEvent.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the end of a mapping node.
+ * 
+ * @see MappingStartEvent
+ */
+public final class MappingEndEvent extends CollectionEndEvent {
+
+    public MappingEndEvent(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.MappingEnd == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/MappingStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/MappingStartEvent.java
new file mode 100644
index 0000000..412e4d5
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/MappingStartEvent.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the beginning of a mapping node.
+ * <p>
+ * This event is followed by a number of key value pairs. <br>
+ * The pairs are not in any particular order. However, the value always directly
+ * follows the corresponding key. <br>
+ * After the key value pairs follows a {@link MappingEndEvent}.
+ * </p>
+ * <p>
+ * There must be an even number of node events between the start and end event.
+ * </p>
+ * 
+ * @see MappingEndEvent
+ */
+public final class MappingStartEvent extends CollectionStartEvent {
+    public MappingStartEvent(String anchor, String tag, boolean implicit, Mark startMark,
+            Mark endMark, Boolean flowStyle) {
+        super(anchor, tag, implicit, startMark, endMark, flowStyle);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.MappingStart == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/NodeEvent.java b/src/main/java/org/yaml/snakeyaml/events/NodeEvent.java
new file mode 100644
index 0000000..f0af48b
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/NodeEvent.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Base class for all events that mark the beginning of a node.
+ */
+public abstract class NodeEvent extends Event {
+
+    private final String anchor;
+
+    public NodeEvent(String anchor, Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+        this.anchor = anchor;
+    }
+
+    /**
+     * Node anchor by which this node might later be referenced by a
+     * {@link AliasEvent}.
+     * <p>
+     * Note that {@link AliasEvent}s are by it self <code>NodeEvent</code>s and
+     * use this property to indicate the referenced anchor.
+     * 
+     * @return Anchor of this node or <code>null</code> if no anchor is defined.
+     */
+    public String getAnchor() {
+        return this.anchor;
+    }
+
+    @Override
+    protected String getArguments() {
+        return "anchor=" + anchor;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
new file mode 100644
index 0000000..7f07a62
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks a scalar value.
+ */
+public final class ScalarEvent extends NodeEvent {
+    private final String tag;
+    // style flag of a scalar event indicates the style of the scalar. Possible
+    // values are None, '', '\'', '"', '|', '>'
+    private final Character style;
+    private final String value;
+    // The implicit flag of a scalar event is a pair of boolean values that
+    // indicate if the tag may be omitted when the scalar is emitted in a plain
+    // and non-plain style correspondingly.
+    private final ImplicitTuple implicit;
+
+    public ScalarEvent(String anchor, String tag, ImplicitTuple implicit, String value,
+            Mark startMark, Mark endMark, Character style) {
+        super(anchor, startMark, endMark);
+        this.tag = tag;
+        this.implicit = implicit;
+        this.value = value;
+        this.style = style;
+    }
+
+    /**
+     * Tag of this scalar.
+     * 
+     * @return The tag of this scalar, or <code>null</code> if no explicit tag
+     *         is available.
+     */
+    public String getTag() {
+        return this.tag;
+    }
+
+    /**
+     * Style of the scalar.
+     * <dl>
+     * <dt>null</dt>
+     * <dd>Flow Style - Plain</dd>
+     * <dt>'\''</dt>
+     * <dd>Flow Style - Single-Quoted</dd>
+     * <dt>'"'</dt>
+     * <dd>Flow Style - Double-Quoted</dd>
+     * <dt>'|'</dt>
+     * <dd>Block Style - Literal</dd>
+     * <dt>'>'</dt>
+     * <dd>Block Style - Folded</dd>
+     * </dl>
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/#id864487">Kind/Style
+     *      Combinations</a>
+     * @return Style of the scalar.
+     */
+    public Character getStyle() {
+        return this.style;
+    }
+
+    /**
+     * String representation of the value.
+     * <p>
+     * Without quotes and escaping.
+     * </p>
+     * 
+     * @return Value as Unicode string.
+     */
+    public String getValue() {
+        return this.value;
+    }
+
+    public ImplicitTuple getImplicit() {
+        return this.implicit;
+    }
+
+    @Override
+    protected String getArguments() {
+        return super.getArguments() + ", tag=" + tag + ", " + implicit + ", value=" + value;
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.Scalar == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/SequenceEndEvent.java b/src/main/java/org/yaml/snakeyaml/events/SequenceEndEvent.java
new file mode 100644
index 0000000..a6a6127
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/SequenceEndEvent.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the end of a sequence.
+ * 
+ * @see SequenceStartEvent
+ */
+public final class SequenceEndEvent extends CollectionEndEvent {
+
+    public SequenceEndEvent(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.SequenceEnd == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/SequenceStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/SequenceStartEvent.java
new file mode 100644
index 0000000..eb7b910
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/SequenceStartEvent.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the beginning of a sequence node.
+ * <p>
+ * This event is followed by the elements contained in the sequence, and a
+ * {@link SequenceEndEvent}.
+ * </p>
+ * 
+ * @see SequenceEndEvent
+ */
+public final class SequenceStartEvent extends CollectionStartEvent {
+    public SequenceStartEvent(String anchor, String tag, boolean implicit, Mark startMark,
+            Mark endMark, Boolean flowStyle) {
+        super(anchor, tag, implicit, startMark, endMark, flowStyle);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.SequenceStart == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/StreamEndEvent.java b/src/main/java/org/yaml/snakeyaml/events/StreamEndEvent.java
new file mode 100644
index 0000000..1389c6b
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/StreamEndEvent.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the end of a stream that might have contained multiple documents.
+ * <p>
+ * This event is the last event that a parser emits. Together with
+ * {@link StreamStartEvent} (which is the first event a parser emits) they mark
+ * the beginning and the end of a stream of documents.
+ * </p>
+ * <p>
+ * See {@link Event} for an exemplary output.
+ * </p>
+ */
+public final class StreamEndEvent extends Event {
+    public StreamEndEvent(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.StreamEnd == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/events/StreamStartEvent.java b/src/main/java/org/yaml/snakeyaml/events/StreamStartEvent.java
new file mode 100644
index 0000000..42e6c76
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/events/StreamStartEvent.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Marks the start of a stream that might contain multiple documents.
+ * <p>
+ * This event is the first event that a parser emits. Together with
+ * {@link StreamEndEvent} (which is the last event a parser emits) they mark the
+ * beginning and the end of a stream of documents.
+ * </p>
+ * <p>
+ * See {@link Event} for an exemplary output.
+ * </p>
+ */
+public final class StreamStartEvent extends Event {
+
+    public StreamStartEvent(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public boolean is(Event.ID id) {
+        return ID.StreamStart == id;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java
new file mode 100644
index 0000000..ec8f1b6
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java
@@ -0,0 +1,236 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.beans.IntrospectionException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+
+/**
+ * Construct a custom Java instance out of a compact object notation format.
+ */
+public class CompactConstructor extends Constructor {
+    private static final Pattern GUESS_COMPACT = Pattern
+            .compile("\\p{Alpha}.*\\s*\\((?:,?\\s*(?:(?:\\w*)|(?:\\p{Alpha}\\w*\\s*=.+))\\s*)+\\)");
+    private static final Pattern FIRST_PATTERN = Pattern.compile("(\\p{Alpha}.*)(\\s*)\\((.*?)\\)");
+    private static final Pattern PROPERTY_NAME_PATTERN = Pattern
+            .compile("\\s*(\\p{Alpha}\\w*)\\s*=(.+)");
+    private Construct compactConstruct;
+
+    protected Object constructCompactFormat(ScalarNode node, CompactData data) {
+        try {
+            Object obj = createInstance(node, data);
+            Map<String, Object> properties = new HashMap<String, Object>(data.getProperties());
+            setProperties(obj, properties);
+            return obj;
+        } catch (Exception e) {
+            throw new YAMLException(e);
+        }
+    }
+
+    protected Object createInstance(ScalarNode node, CompactData data) throws Exception {
+        Class<?> clazz = getClassForName(data.getPrefix());
+        Class<?>[] args = new Class[data.getArguments().size()];
+        for (int i = 0; i < args.length; i++) {
+            // assume all the arguments are Strings
+            args[i] = String.class;
+        }
+        java.lang.reflect.Constructor<?> c = clazz.getDeclaredConstructor(args);
+        c.setAccessible(true);
+        return c.newInstance(data.getArguments().toArray());
+
+    }
+
+    protected void setProperties(Object bean, Map<String, Object> data) throws Exception {
+        if (data == null) {
+            throw new NullPointerException("Data for Compact Object Notation cannot be null.");
+        }
+        for (Map.Entry<String, Object> entry : data.entrySet()) {
+            String key = entry.getKey();
+            Property property = getPropertyUtils().getProperty(bean.getClass(), key);
+            try {
+                property.set(bean, entry.getValue());
+            } catch (IllegalArgumentException e) {
+                throw new YAMLException("Cannot set property='" + key + "' with value='"
+                        + data.get(key) + "' (" + data.get(key).getClass() + ") in " + bean);
+            }
+        }
+    }
+
+    public CompactData getCompactData(String scalar) {
+        if (!scalar.endsWith(")")) {
+            return null;
+        }
+        if (scalar.indexOf('(') < 0) {
+            return null;
+        }
+        Matcher m = FIRST_PATTERN.matcher(scalar);
+        if (m.matches()) {
+            String tag = m.group(1).trim();
+            String content = m.group(3);
+            CompactData data = new CompactData(tag);
+            if (content.length() == 0)
+                return data;
+            String[] names = content.split("\\s*,\\s*");
+            for (int i = 0; i < names.length; i++) {
+                String section = names[i];
+                if (section.indexOf('=') < 0) {
+                    data.getArguments().add(section);
+                } else {
+                    Matcher sm = PROPERTY_NAME_PATTERN.matcher(section);
+                    if (sm.matches()) {
+                        String name = sm.group(1);
+                        String value = sm.group(2).trim();
+                        data.getProperties().put(name, value);
+                    } else {
+                        return null;
+                    }
+                }
+            }
+            return data;
+        }
+        return null;
+    }
+
+    private Construct getCompactConstruct() {
+        if (compactConstruct == null) {
+            compactConstruct = createCompactConstruct();
+        }
+        return compactConstruct;
+    }
+
+    protected Construct createCompactConstruct() {
+        return new ConstructCompactObject();
+    }
+
+    @Override
+    protected Construct getConstructor(Node node) {
+        if (node instanceof MappingNode) {
+            MappingNode mnode = (MappingNode) node;
+            List<NodeTuple> list = mnode.getValue();
+            if (list.size() == 1) {
+                NodeTuple tuple = list.get(0);
+                Node key = tuple.getKeyNode();
+                if (key instanceof ScalarNode) {
+                    ScalarNode scalar = (ScalarNode) key;
+                    if (GUESS_COMPACT.matcher(scalar.getValue()).matches()) {
+                        return getCompactConstruct();
+                    }
+                }
+            }
+        } else if (node instanceof ScalarNode) {
+            ScalarNode scalar = (ScalarNode) node;
+            if (GUESS_COMPACT.matcher(scalar.getValue()).matches()) {
+                return getCompactConstruct();
+            }
+        }
+        return super.getConstructor(node);
+    }
+
+    public class ConstructCompactObject extends ConstructMapping {
+
+        @Override
+        public void construct2ndStep(Node node, Object object) {
+            // Compact Object Notation may contain only one entry
+            MappingNode mnode = (MappingNode) node;
+            NodeTuple nodeTuple = mnode.getValue().iterator().next();
+
+            Node valueNode = nodeTuple.getValueNode();
+
+            if (valueNode instanceof MappingNode) {
+                valueNode.setType(object.getClass());
+                constructJavaBean2ndStep((MappingNode) valueNode, object);
+            } else {
+                // value is a list
+                applySequence(object, constructSequence((SequenceNode) valueNode));
+            }
+        }
+
+        /*
+         * MappingNode and ScalarNode end up here only they assumed to be a
+         * compact object's representation (@see getConstructor(Node) above)
+         */
+        public Object construct(Node node) {
+            ScalarNode tmpNode = null;
+            if (node instanceof MappingNode) {
+                // Compact Object Notation may contain only one entry
+                MappingNode mnode = (MappingNode) node;
+                NodeTuple nodeTuple = mnode.getValue().iterator().next();
+                node.setTwoStepsConstruction(true);
+                tmpNode = (ScalarNode) nodeTuple.getKeyNode();
+                // return constructScalar((ScalarNode) keyNode);
+            } else {
+                tmpNode = (ScalarNode) node;
+            }
+
+            CompactData data = getCompactData(tmpNode.getValue());
+            if (data == null) { // TODO: Should we throw an exception here ?
+                return constructScalar(tmpNode);
+            }
+            return constructCompactFormat(tmpNode, data);
+        }
+    }
+
+    protected void applySequence(Object bean, List<?> value) {
+        try {
+            Property property = getPropertyUtils().getProperty(bean.getClass(),
+                    getSequencePropertyName(bean.getClass()));
+            property.set(bean, value);
+        } catch (Exception e) {
+            throw new YAMLException(e);
+        }
+    }
+
+    /**
+     * Provide the name of the property which is used when the entries form a
+     * sequence. The property must be a List.
+     * 
+     * @throws IntrospectionException
+     */
+    protected String getSequencePropertyName(Class<?> bean) throws IntrospectionException {
+        Set<Property> properties = getPropertyUtils().getProperties(bean);
+        for (Iterator<Property> iterator = properties.iterator(); iterator.hasNext();) {
+            Property property = iterator.next();
+            if (!List.class.isAssignableFrom(property.getType())) {
+                iterator.remove();
+            }
+        }
+        if (properties.size() == 0) {
+            throw new YAMLException("No list property found in " + bean);
+        } else if (properties.size() > 1) {
+            throw new YAMLException(
+                    "Many list properties found in "
+                            + bean
+                            + "; Please override getSequencePropertyName() to specify which property to use.");
+        }
+        return properties.iterator().next().getName();
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactData.java b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactData.java
new file mode 100644
index 0000000..c2da7c4
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactData.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CompactData {
+    private String prefix;
+    private List<String> arguments = new ArrayList<String>();
+    private Map<String, String> properties = new HashMap<String, String>();
+
+    public CompactData(String prefix) {
+        this.prefix = prefix;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    public List<String> getArguments() {
+        return arguments;
+    }
+
+    @Override
+    public String toString() {
+        return "CompactData: " + prefix + " " + properties;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructor.java b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructor.java
new file mode 100644
index 0000000..e58c3ad
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructor.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class PackageCompactConstructor extends CompactConstructor {
+    private String packageName;
+
+    public PackageCompactConstructor(String packageName) {
+        this.packageName = packageName;
+    }
+
+    @Override
+    protected Class<?> getClassForName(String name) throws ClassNotFoundException {
+        if (name.indexOf('.') < 0) {
+            try {
+                Class<?> clazz = Class.forName(packageName + "." + name);
+                return clazz;
+            } catch (ClassNotFoundException e) {
+                // use super implementation
+            }
+        }
+        return super.getClassForName(name);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/external/biz/base64Coder/Base64Coder.java b/src/main/java/org/yaml/snakeyaml/external/biz/base64Coder/Base64Coder.java
new file mode 100644
index 0000000..65923b6
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/external/biz/base64Coder/Base64Coder.java
@@ -0,0 +1,305 @@
+// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
+// www.source-code.biz, www.inventec.ch/chdh
+//
+// This module is multi-licensed and may be used under the terms
+// of any of the following licenses:
+//
+//  EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal
+//  LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html
+//  GPL, GNU General Public License, V2 or later, http://www.gnu.org/licenses/gpl.html
+//  AL, Apache License, V2.0 or later, http://www.apache.org/licenses
+//  BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php
+//
+// Please contact the author if you need another license.
+// This module is provided "as is", without warranties of any kind.
+
+package org.yaml.snakeyaml.external.biz.base64Coder;
+
+/**
+ * A Base64 encoder/decoder.
+ * 
+ * <p>
+ * This class is used to encode and decode data in Base64 format as described in
+ * RFC 1521.
+ * 
+ * <p>
+ * Project home page: <a
+ * href="http://www.source-code.biz/base64coder/java/">www.
+ * source-code.biz/base64coder/java</a><br>
+ * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland<br>
+ * Multi-licensed: EPL / LGPL / GPL / AL / BSD.
+ */
+public class Base64Coder {
+
+    // The line separator string of the operating system.
+    private static final String systemLineSeparator = System.getProperty("line.separator");
+
+    // Mapping table from 6-bit nibbles to Base64 characters.
+    private static char[] map1 = new char[64];
+    static {
+        int i = 0;
+        for (char c = 'A'; c <= 'Z'; c++)
+            map1[i++] = c;
+        for (char c = 'a'; c <= 'z'; c++)
+            map1[i++] = c;
+        for (char c = '0'; c <= '9'; c++)
+            map1[i++] = c;
+        map1[i++] = '+';
+        map1[i++] = '/';
+    }
+
+    // Mapping table from Base64 characters to 6-bit nibbles.
+    private static byte[] map2 = new byte[128];
+    static {
+        for (int i = 0; i < map2.length; i++)
+            map2[i] = -1;
+        for (int i = 0; i < 64; i++)
+            map2[map1[i]] = (byte) i;
+    }
+
+    /**
+     * Encodes a string into Base64 format. No blanks or line breaks are
+     * inserted.
+     * 
+     * @param s
+     *            A String to be encoded.
+     * @return A String containing the Base64 encoded data.
+     */
+    public static String encodeString(String s) {
+        return new String(encode(s.getBytes()));
+    }
+
+    /**
+     * Encodes a byte array into Base 64 format and breaks the output into lines
+     * of 76 characters. This method is compatible with
+     * <code>sun.misc.BASE64Encoder.encodeBuffer(byte[])</code>.
+     * 
+     * @param in
+     *            An array containing the data bytes to be encoded.
+     * @return A String containing the Base64 encoded data, broken into lines.
+     */
+    public static String encodeLines(byte[] in) {
+        return encodeLines(in, 0, in.length, 76, systemLineSeparator);
+    }
+
+    /**
+     * Encodes a byte array into Base 64 format and breaks the output into
+     * lines.
+     * 
+     * @param in
+     *            An array containing the data bytes to be encoded.
+     * @param iOff
+     *            Offset of the first byte in <code>in</code> to be processed.
+     * @param iLen
+     *            Number of bytes to be processed in <code>in</code>, starting
+     *            at <code>iOff</code>.
+     * @param lineLen
+     *            Line length for the output data. Should be a multiple of 4.
+     * @param lineSeparator
+     *            The line separator to be used to separate the output lines.
+     * @return A String containing the Base64 encoded data, broken into lines.
+     */
+    public static String encodeLines(byte[] in, int iOff, int iLen, int lineLen,
+            String lineSeparator) {
+        int blockLen = (lineLen * 3) / 4;
+        if (blockLen <= 0)
+            throw new IllegalArgumentException();
+        int lines = (iLen + blockLen - 1) / blockLen;
+        int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length();
+        StringBuilder buf = new StringBuilder(bufLen);
+        int ip = 0;
+        while (ip < iLen) {
+            int l = Math.min(iLen - ip, blockLen);
+            buf.append(encode(in, iOff + ip, l));
+            buf.append(lineSeparator);
+            ip += l;
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Encodes a byte array into Base64 format. No blanks or line breaks are
+     * inserted in the output.
+     * 
+     * @param in
+     *            An array containing the data bytes to be encoded.
+     * @return A character array containing the Base64 encoded data.
+     */
+    public static char[] encode(byte[] in) {
+        return encode(in, 0, in.length);
+    }
+
+    /**
+     * Encodes a byte array into Base64 format. No blanks or line breaks are
+     * inserted in the output.
+     * 
+     * @param in
+     *            An array containing the data bytes to be encoded.
+     * @param iLen
+     *            Number of bytes to process in <code>in</code>.
+     * @return A character array containing the Base64 encoded data.
+     */
+    public static char[] encode(byte[] in, int iLen) {
+        return encode(in, 0, iLen);
+    }
+
+    /**
+     * Encodes a byte array into Base64 format. No blanks or line breaks are
+     * inserted in the output.
+     * 
+     * @param in
+     *            An array containing the data bytes to be encoded.
+     * @param iOff
+     *            Offset of the first byte in <code>in</code> to be processed.
+     * @param iLen
+     *            Number of bytes to process in <code>in</code>, starting at
+     *            <code>iOff</code>.
+     * @return A character array containing the Base64 encoded data.
+     */
+    public static char[] encode(byte[] in, int iOff, int iLen) {
+        int oDataLen = (iLen * 4 + 2) / 3; // output length without padding
+        int oLen = ((iLen + 2) / 3) * 4; // output length including padding
+        char[] out = new char[oLen];
+        int ip = iOff;
+        int iEnd = iOff + iLen;
+        int op = 0;
+        while (ip < iEnd) {
+            int i0 = in[ip++] & 0xff;
+            int i1 = ip < iEnd ? in[ip++] & 0xff : 0;
+            int i2 = ip < iEnd ? in[ip++] & 0xff : 0;
+            int o0 = i0 >>> 2;
+            int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
+            int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
+            int o3 = i2 & 0x3F;
+            out[op++] = map1[o0];
+            out[op++] = map1[o1];
+            out[op] = op < oDataLen ? map1[o2] : '=';
+            op++;
+            out[op] = op < oDataLen ? map1[o3] : '=';
+            op++;
+        }
+        return out;
+    }
+
+    /**
+     * Decodes a string from Base64 format. No blanks or line breaks are allowed
+     * within the Base64 encoded input data.
+     * 
+     * @param s
+     *            A Base64 String to be decoded.
+     * @return A String containing the decoded data.
+     * @throws IllegalArgumentException
+     *             If the input is not valid Base64 encoded data.
+     */
+    public static String decodeString(String s) {
+        return new String(decode(s));
+    }
+
+    /**
+     * Decodes a byte array from Base64 format and ignores line separators, tabs
+     * and blanks. CR, LF, Tab and Space characters are ignored in the input
+     * data. This method is compatible with
+     * <code>sun.misc.BASE64Decoder.decodeBuffer(String)</code>.
+     * 
+     * @param s
+     *            A Base64 String to be decoded.
+     * @return An array containing the decoded data bytes.
+     * @throws IllegalArgumentException
+     *             If the input is not valid Base64 encoded data.
+     */
+    public static byte[] decodeLines(String s) {
+        char[] buf = new char[s.length()];
+        int p = 0;
+        for (int ip = 0; ip < s.length(); ip++) {
+            char c = s.charAt(ip);
+            if (c != ' ' && c != '\r' && c != '\n' && c != '\t')
+                buf[p++] = c;
+        }
+        return decode(buf, 0, p);
+    }
+
+    /**
+     * Decodes a byte array from Base64 format. No blanks or line breaks are
+     * allowed within the Base64 encoded input data.
+     * 
+     * @param s
+     *            A Base64 String to be decoded.
+     * @return An array containing the decoded data bytes.
+     * @throws IllegalArgumentException
+     *             If the input is not valid Base64 encoded data.
+     */
+    public static byte[] decode(String s) {
+        return decode(s.toCharArray());
+    }
+
+    /**
+     * Decodes a byte array from Base64 format. No blanks or line breaks are
+     * allowed within the Base64 encoded input data.
+     * 
+     * @param in
+     *            A character array containing the Base64 encoded data.
+     * @return An array containing the decoded data bytes.
+     * @throws IllegalArgumentException
+     *             If the input is not valid Base64 encoded data.
+     */
+    public static byte[] decode(char[] in) {
+        return decode(in, 0, in.length);
+    }
+
+    /**
+     * Decodes a byte array from Base64 format. No blanks or line breaks are
+     * allowed within the Base64 encoded input data.
+     * 
+     * @param in
+     *            A character array containing the Base64 encoded data.
+     * @param iOff
+     *            Offset of the first character in <code>in</code> to be
+     *            processed.
+     * @param iLen
+     *            Number of characters to process in <code>in</code>, starting
+     *            at <code>iOff</code>.
+     * @return An array containing the decoded data bytes.
+     * @throws IllegalArgumentException
+     *             If the input is not valid Base64 encoded data.
+     */
+    public static byte[] decode(char[] in, int iOff, int iLen) {
+        if (iLen % 4 != 0)
+            throw new IllegalArgumentException(
+                    "Length of Base64 encoded input string is not a multiple of 4.");
+        while (iLen > 0 && in[iOff + iLen - 1] == '=')
+            iLen--;
+        int oLen = (iLen * 3) / 4;
+        byte[] out = new byte[oLen];
+        int ip = iOff;
+        int iEnd = iOff + iLen;
+        int op = 0;
+        while (ip < iEnd) {
+            int i0 = in[ip++];
+            int i1 = in[ip++];
+            int i2 = ip < iEnd ? in[ip++] : 'A';
+            int i3 = ip < iEnd ? in[ip++] : 'A';
+            if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
+                throw new IllegalArgumentException("Illegal character in Base64 encoded data.");
+            int b0 = map2[i0];
+            int b1 = map2[i1];
+            int b2 = map2[i2];
+            int b3 = map2[i3];
+            if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
+                throw new IllegalArgumentException("Illegal character in Base64 encoded data.");
+            int o0 = (b0 << 2) | (b1 >>> 4);
+            int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2);
+            int o2 = ((b2 & 3) << 6) | b3;
+            out[op++] = (byte) o0;
+            if (op < oLen)
+                out[op++] = (byte) o1;
+            if (op < oLen)
+                out[op++] = (byte) o2;
+        }
+        return out;
+    }
+
+    // Dummy constructor.
+    private Base64Coder() {
+    }
+
+} // end class Base64Coder
diff --git a/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/Escaper.java b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/Escaper.java
new file mode 100644
index 0000000..c26e3cb
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/Escaper.java
@@ -0,0 +1,97 @@
+/* Copyright (c) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.external.com.google.gdata.util.common.base;
+
+/**
+ * An object that converts literal text into a format safe for inclusion in a
+ * particular context (such as an XML document). Typically (but not always), the
+ * inverse process of "unescaping" the text is performed automatically by the
+ * relevant parser.
+ * 
+ * <p>
+ * For example, an XML escaper would convert the literal string
+ * {@code "Foo<Bar>"} into {@code "Foo&lt;Bar&gt;"} to prevent {@code "<Bar>"}
+ * from being confused with an XML tag. When the resulting XML document is
+ * parsed, the parser API will return this text as the original literal string
+ * {@code "Foo<Bar>"}.
+ * 
+ * <p>
+ * An {@code Escaper} instance is required to be stateless, and safe when used
+ * concurrently by multiple threads.
+ * 
+ * <p>
+ * Several popular escapers are defined as constants in the class
+ * {@link CharEscapers}. To create your own escapers, use
+ * {@link CharEscaperBuilder}, or extend {@link CharEscaper} or
+ * {@code UnicodeEscaper}.
+ * 
+ * 
+ */
+public interface Escaper {
+    /**
+     * Returns the escaped form of a given literal string.
+     * 
+     * <p>
+     * Note that this method may treat input characters differently depending on
+     * the specific escaper implementation.
+     * <ul>
+     * <li>{@link UnicodeEscaper} handles <a
+     * href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> correctly,
+     * including surrogate character pairs. If the input is badly formed the
+     * escaper should throw {@link IllegalArgumentException}.
+     * <li>{@link CharEscaper} handles Java characters independently and does
+     * not verify the input for well formed characters. A CharEscaper should not
+     * be used in situations where input is not guaranteed to be restricted to
+     * the Basic Multilingual Plane (BMP).
+     * </ul>
+     * 
+     * @param string
+     *            the literal string to be escaped
+     * @return the escaped form of {@code string}
+     * @throws NullPointerException
+     *             if {@code string} is null
+     * @throws IllegalArgumentException
+     *             if {@code string} contains badly formed UTF-16 or cannot be
+     *             escaped for any other reason
+     */
+    public String escape(String string);
+
+    /**
+     * Returns an {@code Appendable} instance which automatically escapes all
+     * text appended to it before passing the resulting text to an underlying
+     * {@code Appendable}.
+     * 
+     * <p>
+     * Note that this method may treat input characters differently depending on
+     * the specific escaper implementation.
+     * <ul>
+     * <li>{@link UnicodeEscaper} handles <a
+     * href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> correctly,
+     * including surrogate character pairs. If the input is badly formed the
+     * escaper should throw {@link IllegalArgumentException}.
+     * <li>{@link CharEscaper} handles Java characters independently and does
+     * not verify the input for well formed characters. A CharEscaper should not
+     * be used in situations where input is not guaranteed to be restricted to
+     * the Basic Multilingual Plane (BMP).
+     * </ul>
+     * 
+     * @param out
+     *            the underlying {@code Appendable} to append escaped output to
+     * @return an {@code Appendable} which passes text to {@code out} after
+     *         escaping it.
+     */
+    public Appendable escape(Appendable out);
+}
diff --git a/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/PercentEscaper.java b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/PercentEscaper.java
new file mode 100644
index 0000000..5e2f902
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/PercentEscaper.java
@@ -0,0 +1,281 @@
+/* Copyright (c) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.external.com.google.gdata.util.common.base;
+
+/**
+ * A {@code UnicodeEscaper} that escapes some set of Java characters using the
+ * URI percent encoding scheme. The set of safe characters (those which remain
+ * unescaped) can be specified on construction.
+ * 
+ * <p>
+ * For details on escaping URIs for use in web pages, see section 2.4 of <a
+ * href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
+ * 
+ * <p>
+ * In most cases this class should not need to be used directly. If you have no
+ * special requirements for escaping your URIs, you should use either
+ * {@link CharEscapers#uriEscaper()} or {@link CharEscapers#uriEscaper(boolean)}.
+ * 
+ * <p>
+ * When encoding a String, the following rules apply:
+ * <ul>
+ * <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0"
+ * through "9" remain the same.
+ * <li>Any additionally specified safe characters remain the same.
+ * <li>If {@code plusForSpace} was specified, the space character " " is
+ * converted into a plus sign "+".
+ * <li>All other characters are converted into one or more bytes using UTF-8
+ * encoding and each byte is then represented by the 3-character string "%XY",
+ * where "XY" is the two-digit, uppercase, hexadecimal representation of the
+ * byte value.
+ * </ul>
+ * 
+ * <p>
+ * RFC 2396 specifies the set of unreserved characters as "-", "_", ".", "!",
+ * "~", "*", "'", "(" and ")". It goes on to state:
+ * 
+ * <p>
+ * <i>Unreserved characters can be escaped without changing the semantics of the
+ * URI, but this should not be done unless the URI is being used in a context
+ * that does not allow the unescaped character to appear.</i>
+ * 
+ * <p>
+ * For performance reasons the only currently supported character encoding of
+ * this class is UTF-8.
+ * 
+ * <p>
+ * <b>Note</b>: This escaper produces uppercase hexidecimal sequences. From <a
+ * href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>:<br>
+ * <i>"URI producers and normalizers should use uppercase hexadecimal digits for
+ * all percent-encodings."</i>
+ * 
+ * 
+ */
+public class PercentEscaper extends UnicodeEscaper {
+    /**
+     * A string of safe characters that mimics the behavior of
+     * {@link java.net.URLEncoder}.
+     * 
+     */
+    public static final String SAFECHARS_URLENCODER = "-_.*";
+
+    /**
+     * A string of characters that do not need to be encoded when used in URI
+     * path segments, as specified in RFC 3986. Note that some of these
+     * characters do need to be escaped when used in other parts of the URI.
+     */
+    public static final String SAFEPATHCHARS_URLENCODER = "-_.!~*'()@:$&,;=";
+
+    /**
+     * A string of characters that do not need to be encoded when used in URI
+     * query strings, as specified in RFC 3986. Note that some of these
+     * characters do need to be escaped when used in other parts of the URI.
+     */
+    public static final String SAFEQUERYSTRINGCHARS_URLENCODER = "-_.!~*'()@:$,;/?:";
+
+    // In some uri escapers spaces are escaped to '+'
+    private static final char[] URI_ESCAPED_SPACE = { '+' };
+
+    private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+
+    /**
+     * If true we should convert space to the {@code +} character.
+     */
+    private final boolean plusForSpace;
+
+    /**
+     * An array of flags where for any {@code char c} if {@code safeOctets[c]}
+     * is true then {@code c} should remain unmodified in the output. If
+     * {@code c > safeOctets.length} then it should be escaped.
+     */
+    private final boolean[] safeOctets;
+
+    /**
+     * Constructs a URI escaper with the specified safe characters and optional
+     * handling of the space character.
+     * 
+     * @param safeChars
+     *            a non null string specifying additional safe characters for
+     *            this escaper (the ranges 0..9, a..z and A..Z are always safe
+     *            and should not be specified here)
+     * @param plusForSpace
+     *            true if ASCII space should be escaped to {@code +} rather than
+     *            {@code %20}
+     * @throws IllegalArgumentException
+     *             if any of the parameters were invalid
+     */
+    public PercentEscaper(String safeChars, boolean plusForSpace) {
+        // Avoid any misunderstandings about the behavior of this escaper
+        if (safeChars.matches(".*[0-9A-Za-z].*")) {
+            throw new IllegalArgumentException(
+                    "Alphanumeric characters are always 'safe' and should not be "
+                            + "explicitly specified");
+        }
+        // Avoid ambiguous parameters. Safe characters are never modified so if
+        // space is a safe character then setting plusForSpace is meaningless.
+        if (plusForSpace && safeChars.contains(" ")) {
+            throw new IllegalArgumentException(
+                    "plusForSpace cannot be specified when space is a 'safe' character");
+        }
+        if (safeChars.contains("%")) {
+            throw new IllegalArgumentException("The '%' character cannot be specified as 'safe'");
+        }
+        this.plusForSpace = plusForSpace;
+        this.safeOctets = createSafeOctets(safeChars);
+    }
+
+    /**
+     * Creates a boolean[] with entries corresponding to the character values
+     * for 0-9, A-Z, a-z and those specified in safeChars set to true. The array
+     * is as small as is required to hold the given character information.
+     */
+    private static boolean[] createSafeOctets(String safeChars) {
+        int maxChar = 'z';
+        char[] safeCharArray = safeChars.toCharArray();
+        for (char c : safeCharArray) {
+            maxChar = Math.max(c, maxChar);
+        }
+        boolean[] octets = new boolean[maxChar + 1];
+        for (int c = '0'; c <= '9'; c++) {
+            octets[c] = true;
+        }
+        for (int c = 'A'; c <= 'Z'; c++) {
+            octets[c] = true;
+        }
+        for (int c = 'a'; c <= 'z'; c++) {
+            octets[c] = true;
+        }
+        for (char c : safeCharArray) {
+            octets[c] = true;
+        }
+        return octets;
+    }
+
+    /*
+     * Overridden for performance. For unescaped strings this improved the
+     * performance of the uri escaper from ~760ns to ~400ns as measured by
+     * {@link CharEscapersBenchmark}.
+     */
+    @Override
+    protected int nextEscapeIndex(CharSequence csq, int index, int end) {
+        for (; index < end; index++) {
+            char c = csq.charAt(index);
+            if (c >= safeOctets.length || !safeOctets[c]) {
+                break;
+            }
+        }
+        return index;
+    }
+
+    /*
+     * Overridden for performance. For unescaped strings this improved the
+     * performance of the uri escaper from ~400ns to ~170ns as measured by
+     * {@link CharEscapersBenchmark}.
+     */
+    @Override
+    public String escape(String s) {
+        int slen = s.length();
+        for (int index = 0; index < slen; index++) {
+            char c = s.charAt(index);
+            if (c >= safeOctets.length || !safeOctets[c]) {
+                return escapeSlow(s, index);
+            }
+        }
+        return s;
+    }
+
+    /**
+     * Escapes the given Unicode code point in UTF-8.
+     */
+    @Override
+    protected char[] escape(int cp) {
+        // We should never get negative values here but if we do it will throw
+        // an
+        // IndexOutOfBoundsException, so at least it will get spotted.
+        if (cp < safeOctets.length && safeOctets[cp]) {
+            return null;
+        } else if (cp == ' ' && plusForSpace) {
+            return URI_ESCAPED_SPACE;
+        } else if (cp <= 0x7F) {
+            // Single byte UTF-8 characters
+            // Start with "%--" and fill in the blanks
+            char[] dest = new char[3];
+            dest[0] = '%';
+            dest[2] = UPPER_HEX_DIGITS[cp & 0xF];
+            dest[1] = UPPER_HEX_DIGITS[cp >>> 4];
+            return dest;
+        } else if (cp <= 0x7ff) {
+            // Two byte UTF-8 characters [cp >= 0x80 && cp <= 0x7ff]
+            // Start with "%--%--" and fill in the blanks
+            char[] dest = new char[6];
+            dest[0] = '%';
+            dest[3] = '%';
+            dest[5] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[2] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[1] = UPPER_HEX_DIGITS[0xC | cp];
+            return dest;
+        } else if (cp <= 0xffff) {
+            // Three byte UTF-8 characters [cp >= 0x800 && cp <= 0xffff]
+            // Start with "%E-%--%--" and fill in the blanks
+            char[] dest = new char[9];
+            dest[0] = '%';
+            dest[1] = 'E';
+            dest[3] = '%';
+            dest[6] = '%';
+            dest[8] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[5] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[2] = UPPER_HEX_DIGITS[cp];
+            return dest;
+        } else if (cp <= 0x10ffff) {
+            char[] dest = new char[12];
+            // Four byte UTF-8 characters [cp >= 0xffff && cp <= 0x10ffff]
+            // Start with "%F-%--%--%--" and fill in the blanks
+            dest[0] = '%';
+            dest[1] = 'F';
+            dest[3] = '%';
+            dest[6] = '%';
+            dest[9] = '%';
+            dest[11] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[10] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[8] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[7] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[5] = UPPER_HEX_DIGITS[cp & 0xF];
+            cp >>>= 4;
+            dest[4] = UPPER_HEX_DIGITS[0x8 | (cp & 0x3)];
+            cp >>>= 2;
+            dest[2] = UPPER_HEX_DIGITS[cp & 0x7];
+            return dest;
+        } else {
+            // If this ever happens it is due to bug in UnicodeEscaper, not bad
+            // input.
+            throw new IllegalArgumentException("Invalid unicode character value " + cp);
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/UnicodeEscaper.java b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/UnicodeEscaper.java
new file mode 100644
index 0000000..5403185
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/external/com/google/gdata/util/common/base/UnicodeEscaper.java
@@ -0,0 +1,506 @@
+/* Copyright (c) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.yaml.snakeyaml.external.com.google.gdata.util.common.base;
+
+import java.io.IOException;
+
+/**
+ * An {@link Escaper} that converts literal text into a format safe for
+ * inclusion in a particular context (such as an XML document). Typically (but
+ * not always), the inverse process of "unescaping" the text is performed
+ * automatically by the relevant parser.
+ * 
+ * <p>
+ * For example, an XML escaper would convert the literal string
+ * {@code "Foo<Bar>"} into {@code "Foo&lt;Bar&gt;"} to prevent {@code "<Bar>"}
+ * from being confused with an XML tag. When the resulting XML document is
+ * parsed, the parser API will return this text as the original literal string
+ * {@code "Foo<Bar>"}.
+ * 
+ * <p>
+ * <b>Note:</b> This class is similar to {@link CharEscaper} but with one very
+ * important difference. A CharEscaper can only process Java <a
+ * href="http://en.wikipedia.org/wiki/UTF-16">UTF16</a> characters in isolation
+ * and may not cope when it encounters surrogate pairs. This class facilitates
+ * the correct escaping of all Unicode characters.
+ * 
+ * <p>
+ * As there are important reasons, including potential security issues, to
+ * handle Unicode correctly if you are considering implementing a new escaper
+ * you should favor using UnicodeEscaper wherever possible.
+ * 
+ * <p>
+ * A {@code UnicodeEscaper} instance is required to be stateless, and safe when
+ * used concurrently by multiple threads.
+ * 
+ * <p>
+ * Several popular escapers are defined as constants in the class
+ * {@link CharEscapers}. To create your own escapers extend this class and
+ * implement the {@link #escape(int)} method.
+ * 
+ * 
+ */
+public abstract class UnicodeEscaper implements Escaper {
+    /** The amount of padding (chars) to use when growing the escape buffer. */
+    private static final int DEST_PAD = 32;
+
+    /**
+     * Returns the escaped form of the given Unicode code point, or {@code null}
+     * if this code point does not need to be escaped. When called as part of an
+     * escaping operation, the given code point is guaranteed to be in the range
+     * {@code 0 <= cp <= Character#MAX_CODE_POINT}.
+     * 
+     * <p>
+     * If an empty array is returned, this effectively strips the input
+     * character from the resulting text.
+     * 
+     * <p>
+     * If the character does not need to be escaped, this method should return
+     * {@code null}, rather than an array containing the character
+     * representation of the code point. This enables the escaping algorithm to
+     * perform more efficiently.
+     * 
+     * <p>
+     * If the implementation of this method cannot correctly handle a particular
+     * code point then it should either throw an appropriate runtime exception
+     * or return a suitable replacement character. It must never silently
+     * discard invalid input as this may constitute a security risk.
+     * 
+     * @param cp
+     *            the Unicode code point to escape if necessary
+     * @return the replacement characters, or {@code null} if no escaping was
+     *         needed
+     */
+    protected abstract char[] escape(int cp);
+
+    /**
+     * Scans a sub-sequence of characters from a given {@link CharSequence},
+     * returning the index of the next character that requires escaping.
+     * 
+     * <p>
+     * <b>Note:</b> When implementing an escaper, it is a good idea to override
+     * this method for efficiency. The base class implementation determines
+     * successive Unicode code points and invokes {@link #escape(int)} for each
+     * of them. If the semantics of your escaper are such that code points in
+     * the supplementary range are either all escaped or all unescaped, this
+     * method can be implemented more efficiently using
+     * {@link CharSequence#charAt(int)}.
+     * 
+     * <p>
+     * Note however that if your escaper does not escape characters in the
+     * supplementary range, you should either continue to validate the
+     * correctness of any surrogate characters encountered or provide a clear
+     * warning to users that your escaper does not validate its input.
+     * 
+     * <p>
+     * See {@link PercentEscaper} for an example.
+     * 
+     * @param csq
+     *            a sequence of characters
+     * @param start
+     *            the index of the first character to be scanned
+     * @param end
+     *            the index immediately after the last character to be scanned
+     * @throws IllegalArgumentException
+     *             if the scanned sub-sequence of {@code csq} contains invalid
+     *             surrogate pairs
+     */
+    protected int nextEscapeIndex(CharSequence csq, int start, int end) {
+        int index = start;
+        while (index < end) {
+            int cp = codePointAt(csq, index, end);
+            if (cp < 0 || escape(cp) != null) {
+                break;
+            }
+            index += Character.isSupplementaryCodePoint(cp) ? 2 : 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns the escaped form of a given literal string.
+     * 
+     * <p>
+     * If you are escaping input in arbitrary successive chunks, then it is not
+     * generally safe to use this method. If an input string ends with an
+     * unmatched high surrogate character, then this method will throw
+     * {@link IllegalArgumentException}. You should either ensure your input is
+     * valid <a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a> before
+     * calling this method or use an escaped {@link Appendable} (as returned by
+     * {@link #escape(Appendable)}) which can cope with arbitrarily split input.
+     * 
+     * <p>
+     * <b>Note:</b> When implementing an escaper it is a good idea to override
+     * this method for efficiency by inlining the implementation of
+     * {@link #nextEscapeIndex(CharSequence, int, int)} directly. Doing this for
+     * {@link PercentEscaper} more than doubled the performance for unescaped
+     * strings (as measured by {@link CharEscapersBenchmark}).
+     * 
+     * @param string
+     *            the literal string to be escaped
+     * @return the escaped form of {@code string}
+     * @throws NullPointerException
+     *             if {@code string} is null
+     * @throws IllegalArgumentException
+     *             if invalid surrogate characters are encountered
+     */
+    public String escape(String string) {
+        int end = string.length();
+        int index = nextEscapeIndex(string, 0, end);
+        return index == end ? string : escapeSlow(string, index);
+    }
+
+    /**
+     * Returns the escaped form of a given literal string, starting at the given
+     * index. This method is called by the {@link #escape(String)} method when
+     * it discovers that escaping is required. It is protected to allow
+     * subclasses to override the fastpath escaping function to inline their
+     * escaping test. See {@link CharEscaperBuilder} for an example usage.
+     * 
+     * <p>
+     * This method is not reentrant and may only be invoked by the top level
+     * {@link #escape(String)} method.
+     * 
+     * @param s
+     *            the literal string to be escaped
+     * @param index
+     *            the index to start escaping from
+     * @return the escaped form of {@code string}
+     * @throws NullPointerException
+     *             if {@code string} is null
+     * @throws IllegalArgumentException
+     *             if invalid surrogate characters are encountered
+     */
+    protected final String escapeSlow(String s, int index) {
+        int end = s.length();
+
+        // Get a destination buffer and setup some loop variables.
+        char[] dest = DEST_TL.get();
+        int destIndex = 0;
+        int unescapedChunkStart = 0;
+
+        while (index < end) {
+            int cp = codePointAt(s, index, end);
+            if (cp < 0) {
+                throw new IllegalArgumentException("Trailing high surrogate at end of input");
+            }
+            char[] escaped = escape(cp);
+            if (escaped != null) {
+                int charsSkipped = index - unescapedChunkStart;
+
+                // This is the size needed to add the replacement, not the full
+                // size needed by the string. We only regrow when we absolutely
+                // must.
+                int sizeNeeded = destIndex + charsSkipped + escaped.length;
+                if (dest.length < sizeNeeded) {
+                    int destLength = sizeNeeded + (end - index) + DEST_PAD;
+                    dest = growBuffer(dest, destIndex, destLength);
+                }
+                // If we have skipped any characters, we need to copy them now.
+                if (charsSkipped > 0) {
+                    s.getChars(unescapedChunkStart, index, dest, destIndex);
+                    destIndex += charsSkipped;
+                }
+                if (escaped.length > 0) {
+                    System.arraycopy(escaped, 0, dest, destIndex, escaped.length);
+                    destIndex += escaped.length;
+                }
+            }
+            unescapedChunkStart = index + (Character.isSupplementaryCodePoint(cp) ? 2 : 1);
+            index = nextEscapeIndex(s, unescapedChunkStart, end);
+        }
+
+        // Process trailing unescaped characters - no need to account for
+        // escaped
+        // length or padding the allocation.
+        int charsSkipped = end - unescapedChunkStart;
+        if (charsSkipped > 0) {
+            int endIndex = destIndex + charsSkipped;
+            if (dest.length < endIndex) {
+                dest = growBuffer(dest, destIndex, endIndex);
+            }
+            s.getChars(unescapedChunkStart, end, dest, destIndex);
+            destIndex = endIndex;
+        }
+        return new String(dest, 0, destIndex);
+    }
+
+    /**
+     * Returns an {@code Appendable} instance which automatically escapes all
+     * text appended to it before passing the resulting text to an underlying
+     * {@code Appendable}.
+     * 
+     * <p>
+     * Unlike {@link #escape(String)} it is permitted to append arbitrarily
+     * split input to this Appendable, including input that is split over a
+     * surrogate pair. In this case the pending high surrogate character will
+     * not be processed until the corresponding low surrogate is appended. This
+     * means that a trailing high surrogate character at the end of the input
+     * cannot be detected and will be silently ignored. This is unavoidable
+     * since the Appendable interface has no {@code close()} method, and it is
+     * impossible to determine when the last characters have been appended.
+     * 
+     * <p>
+     * The methods of the returned object will propagate any exceptions thrown
+     * by the underlying {@code Appendable}.
+     * 
+     * <p>
+     * For well formed <a href="http://en.wikipedia.org/wiki/UTF-16">UTF-16</a>
+     * the escaping behavior is identical to that of {@link #escape(String)} and
+     * the following code is equivalent to (but much slower than)
+     * {@code escaper.escape(string)}:
+     * 
+     * <pre>
+     * {
+     *     &#064;code
+     *     StringBuilder sb = new StringBuilder();
+     *     escaper.escape(sb).append(string);
+     *     return sb.toString();
+     * }
+     * </pre>
+     * 
+     * @param out
+     *            the underlying {@code Appendable} to append escaped output to
+     * @return an {@code Appendable} which passes text to {@code out} after
+     *         escaping it
+     * @throws NullPointerException
+     *             if {@code out} is null
+     * @throws IllegalArgumentException
+     *             if invalid surrogate characters are encountered
+     * 
+     */
+    public Appendable escape(final Appendable out) {
+        assert out != null;
+
+        return new Appendable() {
+            int pendingHighSurrogate = -1;
+            char[] decodedChars = new char[2];
+
+            public Appendable append(CharSequence csq) throws IOException {
+                return append(csq, 0, csq.length());
+            }
+
+            public Appendable append(CharSequence csq, int start, int end) throws IOException {
+                int index = start;
+                if (index < end) {
+                    // This is a little subtle: index must never reference the
+                    // middle of a
+                    // surrogate pair but unescapedChunkStart can. The first
+                    // time we enter
+                    // the loop below it is possible that index !=
+                    // unescapedChunkStart.
+                    int unescapedChunkStart = index;
+                    if (pendingHighSurrogate != -1) {
+                        // Our last append operation ended halfway through a
+                        // surrogate pair
+                        // so we have to do some extra work first.
+                        char c = csq.charAt(index++);
+                        if (!Character.isLowSurrogate(c)) {
+                            throw new IllegalArgumentException(
+                                    "Expected low surrogate character but got " + c);
+                        }
+                        char[] escaped = escape(Character.toCodePoint((char) pendingHighSurrogate,
+                                c));
+                        if (escaped != null) {
+                            // Emit the escaped character and adjust
+                            // unescapedChunkStart to
+                            // skip the low surrogate we have consumed.
+                            outputChars(escaped, escaped.length);
+                            unescapedChunkStart += 1;
+                        } else {
+                            // Emit pending high surrogate (unescaped) but do
+                            // not modify
+                            // unescapedChunkStart as we must still emit the low
+                            // surrogate.
+                            out.append((char) pendingHighSurrogate);
+                        }
+                        pendingHighSurrogate = -1;
+                    }
+                    while (true) {
+                        // Find and append the next subsequence of unescaped
+                        // characters.
+                        index = nextEscapeIndex(csq, index, end);
+                        if (index > unescapedChunkStart) {
+                            out.append(csq, unescapedChunkStart, index);
+                        }
+                        if (index == end) {
+                            break;
+                        }
+                        // If we are not finished, calculate the next code
+                        // point.
+                        int cp = codePointAt(csq, index, end);
+                        if (cp < 0) {
+                            // Our sequence ended half way through a surrogate
+                            // pair so just
+                            // record the state and exit.
+                            pendingHighSurrogate = -cp;
+                            break;
+                        }
+                        // Escape the code point and output the characters.
+                        char[] escaped = escape(cp);
+                        if (escaped != null) {
+                            outputChars(escaped, escaped.length);
+                        } else {
+                            // This shouldn't really happen if nextEscapeIndex
+                            // is correct but
+                            // we should cope with false positives.
+                            int len = Character.toChars(cp, decodedChars, 0);
+                            outputChars(decodedChars, len);
+                        }
+                        // Update our index past the escaped character and
+                        // continue.
+                        index += (Character.isSupplementaryCodePoint(cp) ? 2 : 1);
+                        unescapedChunkStart = index;
+                    }
+                }
+                return this;
+            }
+
+            public Appendable append(char c) throws IOException {
+                if (pendingHighSurrogate != -1) {
+                    // Our last append operation ended halfway through a
+                    // surrogate pair
+                    // so we have to do some extra work first.
+                    if (!Character.isLowSurrogate(c)) {
+                        throw new IllegalArgumentException(
+                                "Expected low surrogate character but got '" + c + "' with value "
+                                        + (int) c);
+                    }
+                    char[] escaped = escape(Character.toCodePoint((char) pendingHighSurrogate, c));
+                    if (escaped != null) {
+                        outputChars(escaped, escaped.length);
+                    } else {
+                        out.append((char) pendingHighSurrogate);
+                        out.append(c);
+                    }
+                    pendingHighSurrogate = -1;
+                } else if (Character.isHighSurrogate(c)) {
+                    // This is the start of a (split) surrogate pair.
+                    pendingHighSurrogate = c;
+                } else {
+                    if (Character.isLowSurrogate(c)) {
+                        throw new IllegalArgumentException("Unexpected low surrogate character '"
+                                + c + "' with value " + (int) c);
+                    }
+                    // This is a normal (non surrogate) char.
+                    char[] escaped = escape(c);
+                    if (escaped != null) {
+                        outputChars(escaped, escaped.length);
+                    } else {
+                        out.append(c);
+                    }
+                }
+                return this;
+            }
+
+            private void outputChars(char[] chars, int len) throws IOException {
+                for (int n = 0; n < len; n++) {
+                    out.append(chars[n]);
+                }
+            }
+        };
+    }
+
+    /**
+     * Returns the Unicode code point of the character at the given index.
+     * 
+     * <p>
+     * Unlike {@link Character#codePointAt(CharSequence, int)} or
+     * {@link String#codePointAt(int)} this method will never fail silently when
+     * encountering an invalid surrogate pair.
+     * 
+     * <p>
+     * The behaviour of this method is as follows:
+     * <ol>
+     * <li>If {@code index >= end}, {@link IndexOutOfBoundsException} is thrown.
+     * <li><b>If the character at the specified index is not a surrogate, it is
+     * returned.</b>
+     * <li>If the first character was a high surrogate value, then an attempt is
+     * made to read the next character.
+     * <ol>
+     * <li><b>If the end of the sequence was reached, the negated value of the
+     * trailing high surrogate is returned.</b>
+     * <li><b>If the next character was a valid low surrogate, the code point
+     * value of the high/low surrogate pair is returned.</b>
+     * <li>If the next character was not a low surrogate value, then
+     * {@link IllegalArgumentException} is thrown.
+     * </ol>
+     * <li>If the first character was a low surrogate value,
+     * {@link IllegalArgumentException} is thrown.
+     * </ol>
+     * 
+     * @param seq
+     *            the sequence of characters from which to decode the code point
+     * @param index
+     *            the index of the first character to decode
+     * @param end
+     *            the index beyond the last valid character to decode
+     * @return the Unicode code point for the given index or the negated value
+     *         of the trailing high surrogate character at the end of the
+     *         sequence
+     */
+    protected static final int codePointAt(CharSequence seq, int index, int end) {
+        if (index < end) {
+            char c1 = seq.charAt(index++);
+            if (c1 < Character.MIN_HIGH_SURROGATE || c1 > Character.MAX_LOW_SURROGATE) {
+                // Fast path (first test is probably all we need to do)
+                return c1;
+            } else if (c1 <= Character.MAX_HIGH_SURROGATE) {
+                // If the high surrogate was the last character, return its
+                // inverse
+                if (index == end) {
+                    return -c1;
+                }
+                // Otherwise look for the low surrogate following it
+                char c2 = seq.charAt(index);
+                if (Character.isLowSurrogate(c2)) {
+                    return Character.toCodePoint(c1, c2);
+                }
+                throw new IllegalArgumentException("Expected low surrogate but got char '" + c2
+                        + "' with value " + (int) c2 + " at index " + index);
+            } else {
+                throw new IllegalArgumentException("Unexpected low surrogate character '" + c1
+                        + "' with value " + (int) c1 + " at index " + (index - 1));
+            }
+        }
+        throw new IndexOutOfBoundsException("Index exceeds specified range");
+    }
+
+    /**
+     * Helper method to grow the character buffer as needed, this only happens
+     * once in a while so it's ok if it's in a method call. If the index passed
+     * in is 0 then no copying will be done.
+     */
+    private static final char[] growBuffer(char[] dest, int index, int size) {
+        char[] copy = new char[size];
+        if (index > 0) {
+            System.arraycopy(dest, 0, copy, 0, index);
+        }
+        return copy;
+    }
+
+    /**
+     * A thread-local destination buffer to keep us from creating new buffers.
+     * The starting size is 1024 characters. If we grow past this we don't put
+     * it back in the threadlocal, we just keep going and grow as needed.
+     */
+    private static final ThreadLocal<char[]> DEST_TL = new ThreadLocal<char[]>() {
+        @Override
+        protected char[] initialValue() {
+            return new char[1024];
+        }
+    };
+}
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/BeanAccess.java b/src/main/java/org/yaml/snakeyaml/introspector/BeanAccess.java
new file mode 100644
index 0000000..696fec7
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/BeanAccess.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+/**
+ * Control instance variables.
+ */
+public enum BeanAccess {
+    /** use JavaBean properties and public fields */
+    DEFAULT,
+
+    /** use all declared fields (including inherited) */
+    FIELD,
+
+    /** reserved */
+    PROPERTY;
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/FieldProperty.java b/src/main/java/org/yaml/snakeyaml/introspector/FieldProperty.java
new file mode 100644
index 0000000..9bc38aa
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/FieldProperty.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+import java.lang.reflect.Field;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+/**
+ * <p>
+ * A <code>FieldProperty</code> is a <code>Property</code> which is accessed as
+ * a field, without going through accessor methods (setX, getX). The field may
+ * have any scope (public, package, protected, private).
+ * </p>
+ */
+public class FieldProperty extends GenericProperty {
+    private final Field field;
+
+    public FieldProperty(Field field) {
+        super(field.getName(), field.getType(), field.getGenericType());
+        this.field = field;
+        field.setAccessible(true);
+    }
+
+    @Override
+    public void set(Object object, Object value) throws Exception {
+        field.set(object, value);
+    }
+
+    @Override
+    public Object get(Object object) {
+        try {
+            return field.get(object);
+        } catch (Exception e) {
+            throw new YAMLException("Unable to access field " + field.getName() + " on object "
+                    + object + " : " + e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/GenericProperty.java b/src/main/java/org/yaml/snakeyaml/introspector/GenericProperty.java
new file mode 100644
index 0000000..3098160
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/GenericProperty.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+abstract public class GenericProperty extends Property {
+
+    private Type genType;
+
+    public GenericProperty(String name, Class<?> aClass, Type aType) {
+        super(name, aClass);
+        genType = aType;
+        actualClassesChecked = aType == null;
+    }
+
+    private boolean actualClassesChecked;
+    private Class<?>[] actualClasses;
+
+    public Class<?>[] getActualTypeArguments() { // should we synchronize here ?
+        if (!actualClassesChecked) {
+            if (genType instanceof ParameterizedType) {
+                ParameterizedType parameterizedType = (ParameterizedType) genType;
+                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+                if (actualTypeArguments.length > 0) {
+                    actualClasses = new Class<?>[actualTypeArguments.length];
+                    for (int i = 0; i < actualTypeArguments.length; i++) {
+                        if (actualTypeArguments[i] instanceof Class<?>) {
+                            actualClasses[i] = (Class<?>) actualTypeArguments[i];
+                        } else if (actualTypeArguments[i] instanceof ParameterizedType) {
+                            actualClasses[i] = (Class<?>) ((ParameterizedType) actualTypeArguments[i])
+                                    .getRawType();
+                        } else if (actualTypeArguments[i] instanceof GenericArrayType) {
+                            Type componentType = ((GenericArrayType) actualTypeArguments[i])
+                                    .getGenericComponentType();
+                            if (componentType instanceof Class<?>) {
+                                actualClasses[i] = Array.newInstance((Class<?>) componentType, 0)
+                                        .getClass();
+                            } else {
+                                actualClasses = null;
+                                break;
+                            }
+                        } else {
+                            actualClasses = null;
+                            break;
+                        }
+                    }
+                }
+            } else if (genType instanceof GenericArrayType) {
+                Type componentType = ((GenericArrayType) genType).getGenericComponentType();
+                if (componentType instanceof Class<?>) {
+                    actualClasses = new Class<?>[] { (Class<?>) componentType };
+                }
+            } else if (genType instanceof Class<?>) {// XXX this check is only
+                                                     // required for IcedTea6
+                Class<?> classType = (Class<?>) genType;
+                if (classType.isArray()) {
+                    actualClasses = new Class<?>[1];
+                    actualClasses[0] = getType().getComponentType();
+                }
+            }
+            actualClassesChecked = true;
+        }
+        return actualClasses;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java b/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
new file mode 100644
index 0000000..fc6e00d
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/MethodProperty.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+import java.beans.PropertyDescriptor;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+/**
+ * <p>
+ * A <code>MethodProperty</code> is a <code>Property</code> which is accessed
+ * through accessor methods (setX, getX). It is possible to have a
+ * <code>MethodProperty</code> which has only setter, only getter, or both. It
+ * is not possible to have a <code>MethodProperty</code> which has neither
+ * setter nor getter.
+ * </p>
+ */
+public class MethodProperty extends GenericProperty {
+
+    private final PropertyDescriptor property;
+    private final boolean readable;
+    private final boolean writable;
+
+    public MethodProperty(PropertyDescriptor property) {
+        super(property.getName(), property.getPropertyType(),
+                property.getReadMethod() == null ? null : property.getReadMethod()
+                        .getGenericReturnType());
+        this.property = property;
+        this.readable = property.getReadMethod() != null;
+        this.writable = property.getWriteMethod() != null;
+    }
+
+    @Override
+    public void set(Object object, Object value) throws Exception {
+        property.getWriteMethod().invoke(object, value);
+    }
+
+    @Override
+    public Object get(Object object) {
+        try {
+            property.getReadMethod().setAccessible(true);// issue 50
+            return property.getReadMethod().invoke(object);
+        } catch (Exception e) {
+            throw new YAMLException("Unable to find getter for property '" + property.getName()
+                    + "' on object " + object + ":" + e);
+        }
+    }
+
+    @Override
+    public boolean isWritable() {
+        return writable;
+    }
+
+    @Override
+    public boolean isReadable() {
+        return readable;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/MissingProperty.java b/src/main/java/org/yaml/snakeyaml/introspector/MissingProperty.java
new file mode 100644
index 0000000..b16a1c2
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/MissingProperty.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+/**
+ * A property that does not map to a real property; this is used when {@link
+ * PropertyUtils.setSkipMissingProperties(boolean)} is set to true.
+ */
+public class MissingProperty extends Property {
+
+    public MissingProperty(String name) {
+        super(name, Object.class);
+    }
+
+    @Override
+    public Class<?>[] getActualTypeArguments() {
+        return new Class[0];
+    }
+
+    /**
+     * Setter does nothing.
+     */
+    @Override
+    public void set(Object object, Object value) throws Exception {
+    }
+
+    @Override
+    public Object get(Object object) {
+        return object;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/Property.java b/src/main/java/org/yaml/snakeyaml/introspector/Property.java
new file mode 100644
index 0000000..286ca0e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/Property.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+/**
+ * <p>
+ * A <code>Property</code> represents a single member variable of a class,
+ * possibly including its accessor methods (getX, setX). The name stored in this
+ * class is the actual name of the property as given for the class, not an
+ * alias.
+ * </p>
+ * 
+ * <p>
+ * Objects of this class have a total ordering which defaults to ordering based
+ * on the name of the property.
+ * </p>
+ */
+public abstract class Property implements Comparable<Property> {
+
+    private final String name;
+    private final Class<?> type;
+
+    public Property(String name, Class<?> type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    public Class<?> getType() {
+        return type;
+    }
+
+    abstract public Class<?>[] getActualTypeArguments();
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return getName() + " of " + getType();
+    }
+
+    public int compareTo(Property o) {
+        return name.compareTo(o.name);
+    }
+
+    public boolean isWritable() {
+        return true;
+    }
+
+    public boolean isReadable() {
+        return true;
+    }
+
+    abstract public void set(Object object, Object value) throws Exception;
+
+    abstract public Object get(Object object);
+
+    @Override
+    public int hashCode() {
+        return name.hashCode() + type.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof Property) {
+            Property p = (Property) other;
+            return name.equals(p.getName()) && type.equals(p.getType());
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
new file mode 100644
index 0000000..92519d9
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class PropertyUtils {
+
+    private final Map<Class<?>, Map<String, Property>> propertiesCache = new HashMap<Class<?>, Map<String, Property>>();
+    private final Map<Class<?>, Set<Property>> readableProperties = new HashMap<Class<?>, Set<Property>>();
+    private BeanAccess beanAccess = BeanAccess.DEFAULT;
+    private boolean allowReadOnlyProperties = false;
+    private boolean skipMissingProperties = false;
+
+    protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess)
+            throws IntrospectionException {
+        if (propertiesCache.containsKey(type)) {
+            return propertiesCache.get(type);
+        }
+
+        Map<String, Property> properties = new LinkedHashMap<String, Property>();
+        boolean inaccessableFieldsExist = false;
+        switch (bAccess) {
+        case FIELD:
+            for (Class<?> c = type; c != null; c = c.getSuperclass()) {
+                for (Field field : c.getDeclaredFields()) {
+                    int modifiers = field.getModifiers();
+                    if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
+                            && !properties.containsKey(field.getName())) {
+                        properties.put(field.getName(), new FieldProperty(field));
+                    }
+                }
+            }
+            break;
+        default:
+            // add JavaBean properties
+            for (PropertyDescriptor property : Introspector.getBeanInfo(type)
+                    .getPropertyDescriptors()) {
+                Method readMethod = property.getReadMethod();
+                if (readMethod == null || !readMethod.getName().equals("getClass")) {
+                    properties.put(property.getName(), new MethodProperty(property));
+                }
+            }
+
+            // add public fields
+            for (Class<?> c = type; c != null; c = c.getSuperclass()) {
+                for (Field field : c.getDeclaredFields()) {
+                    int modifiers = field.getModifiers();
+                    if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
+                        if (Modifier.isPublic(modifiers)) {
+                            properties.put(field.getName(), new FieldProperty(field));
+                        } else {
+                            inaccessableFieldsExist = true;
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        if (properties.isEmpty() && inaccessableFieldsExist) {
+            throw new YAMLException("No JavaBean properties found in " + type.getName());
+        }
+        propertiesCache.put(type, properties);
+        return properties;
+    }
+
+    public Set<Property> getProperties(Class<? extends Object> type) throws IntrospectionException {
+        return getProperties(type, beanAccess);
+    }
+
+    public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess)
+            throws IntrospectionException {
+        if (readableProperties.containsKey(type)) {
+            return readableProperties.get(type);
+        }
+        Set<Property> properties = createPropertySet(type, bAccess);
+        readableProperties.put(type, properties);
+        return properties;
+    }
+
+    protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+            throws IntrospectionException {
+        Set<Property> properties = new TreeSet<Property>();
+        Collection<Property> props = getPropertiesMap(type, bAccess).values();
+        for (Property property : props) {
+            if (property.isReadable() && (allowReadOnlyProperties || property.isWritable())) {
+                properties.add(property);
+            }
+        }
+        return properties;
+    }
+
+    public Property getProperty(Class<? extends Object> type, String name)
+            throws IntrospectionException {
+        return getProperty(type, name, beanAccess);
+    }
+
+    public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess)
+            throws IntrospectionException {
+        Map<String, Property> properties = getPropertiesMap(type, bAccess);
+        Property property = properties.get(name);
+        if (property == null && skipMissingProperties) {
+            property = new MissingProperty(name);
+        }
+        if (property == null || !property.isWritable()) {
+            throw new YAMLException("Unable to find property '" + name + "' on class: "
+                    + type.getName());
+        }
+        return property;
+    }
+
+    public void setBeanAccess(BeanAccess beanAccess) {
+        if (this.beanAccess != beanAccess) {
+            this.beanAccess = beanAccess;
+            propertiesCache.clear();
+            readableProperties.clear();
+        }
+    }
+
+    public void setAllowReadOnlyProperties(boolean allowReadOnlyProperties) {
+        if (this.allowReadOnlyProperties != allowReadOnlyProperties) {
+            this.allowReadOnlyProperties = allowReadOnlyProperties;
+            readableProperties.clear();
+        }
+    }
+
+    /**
+     * Skip properties that are missing during deserialization of YAML to a Java
+     * object. The default is false.
+     * 
+     * @param skipMissingProperties
+     *            true if missing properties should be skipped, false otherwise.
+     */
+    public void setSkipMissingProperties(boolean skipMissingProperties) {
+        if (this.skipMissingProperties != skipMissingProperties) {
+            this.skipMissingProperties = skipMissingProperties;
+            readableProperties.clear();
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/AnchorNode.java b/src/main/java/org/yaml/snakeyaml/nodes/AnchorNode.java
new file mode 100644
index 0000000..8a90131
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/AnchorNode.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+public class AnchorNode extends Node {
+
+    private Node realNode;
+
+    public AnchorNode(Node realNode) {
+        super(realNode.getTag(), realNode.getStartMark(), realNode.getEndMark());
+        this.realNode = realNode;
+    }
+
+    @Override
+    public NodeId getNodeId() {
+        return NodeId.anchor;
+    }
+
+    public Node getRealNode() {
+        return realNode;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/CollectionNode.java b/src/main/java/org/yaml/snakeyaml/nodes/CollectionNode.java
new file mode 100644
index 0000000..aa44368
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/CollectionNode.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Base class for the two collection types {@link MappingNode mapping} and
+ * {@link SequenceNode collection}.
+ */
+public abstract class CollectionNode extends Node {
+    private Boolean flowStyle;
+
+    public CollectionNode(Tag tag, Mark startMark, Mark endMark, Boolean flowStyle) {
+        super(tag, startMark, endMark);
+        this.flowStyle = flowStyle;
+    }
+
+    /**
+     * Serialization style of this collection.
+     * 
+     * @return <code>true</code> for flow style, <code>false</code> for block
+     *         style.
+     */
+    public Boolean getFlowStyle() {
+        return flowStyle;
+    }
+
+    public void setFlowStyle(Boolean flowStyle) {
+        this.flowStyle = flowStyle;
+    }
+
+    public void setEndMark(Mark endMark) {
+        this.endMark = endMark;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/MappingNode.java b/src/main/java/org/yaml/snakeyaml/nodes/MappingNode.java
new file mode 100644
index 0000000..ed3dcff
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/MappingNode.java
@@ -0,0 +1,111 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import java.util.List;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Represents a map.
+ * <p>
+ * A map is a collection of unsorted key-value pairs.
+ * </p>
+ */
+public class MappingNode extends CollectionNode {
+    private List<NodeTuple> value;
+    private boolean merged = false;
+
+    public MappingNode(Tag tag, boolean resolved, List<NodeTuple> value, Mark startMark,
+            Mark endMark, Boolean flowStyle) {
+        super(tag, startMark, endMark, flowStyle);
+        if (value == null) {
+            throw new NullPointerException("value in a Node is required.");
+        }
+        this.value = value;
+        this.resolved = resolved;
+    }
+
+    public MappingNode(Tag tag, List<NodeTuple> value, Boolean flowStyle) {
+        this(tag, true, value, null, null, flowStyle);
+    }
+
+    @Override
+    public NodeId getNodeId() {
+        return NodeId.mapping;
+    }
+
+    /**
+     * Returns the entries of this map.
+     * 
+     * @return List of entries.
+     */
+    public List<NodeTuple> getValue() {
+        return value;
+    }
+
+    public void setValue(List<NodeTuple> merge) {
+        value = merge;
+    }
+
+    public void setOnlyKeyType(Class<? extends Object> keyType) {
+        for (NodeTuple nodes : value) {
+            nodes.getKeyNode().setType(keyType);
+        }
+    }
+
+    public void setTypes(Class<? extends Object> keyType, Class<? extends Object> valueType) {
+        for (NodeTuple nodes : value) {
+            nodes.getValueNode().setType(valueType);
+            nodes.getKeyNode().setType(keyType);
+        }
+    }
+
+    @Override
+    public String toString() {
+        String values;
+        StringBuilder buf = new StringBuilder();
+        for (NodeTuple node : getValue()) {
+            buf.append("{ key=");
+            buf.append(node.getKeyNode());
+            buf.append("; value=");
+            if (node.getValueNode() instanceof CollectionNode) {
+                // to avoid overflow in case of recursive structures
+                buf.append(System.identityHashCode(node.getValueNode()));
+            } else {
+                buf.append(node.toString());
+            }
+            buf.append(" }");
+        }
+        values = buf.toString();
+        return "<" + this.getClass().getName() + " (tag=" + getTag() + ", values=" + values + ")>";
+    }
+
+    /**
+     * @param merged
+     *            - true if map contains merge node
+     */
+    public void setMerged(boolean merged) {
+        this.merged = merged;
+    }
+
+    /**
+     * @return true if map contains merge node
+     */
+    public boolean isMerged() {
+        return merged;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Node.java b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
new file mode 100644
index 0000000..1037e41
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Node.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Base class for all nodes.
+ * <p>
+ * The nodes form the node-graph described in the <a
+ * href="http://yaml.org/spec/1.1/">YAML Specification</a>.
+ * </p>
+ * <p>
+ * While loading, the node graph is usually created by the
+ * {@link org.yaml.snakeyaml.composer.Composer}, and later transformed into
+ * application specific Java classes by the classes from the
+ * {@link org.yaml.snakeyaml.constructor} package.
+ * </p>
+ */
+public abstract class Node {
+    private Tag tag;
+    private Mark startMark;
+    protected Mark endMark;
+    private Class<? extends Object> type;
+    private boolean twoStepsConstruction;
+    /**
+     * true when the tag is assigned by the resolver
+     */
+    protected boolean resolved;
+    protected Boolean useClassConstructor;
+
+    public Node(Tag tag, Mark startMark, Mark endMark) {
+        setTag(tag);
+        this.startMark = startMark;
+        this.endMark = endMark;
+        this.type = Object.class;
+        this.twoStepsConstruction = false;
+        this.resolved = true;
+        this.useClassConstructor = null;
+    }
+
+    /**
+     * Tag of this node.
+     * <p>
+     * Every node has a tag assigned. The tag is either local or global.
+     * 
+     * @return Tag of this node.
+     */
+    public Tag getTag() {
+        return this.tag;
+    }
+
+    public Mark getEndMark() {
+        return endMark;
+    }
+
+    /**
+     * For error reporting.
+     * 
+     * @see "class variable 'id' in PyYAML"
+     * @return scalar, sequence, mapping
+     */
+    public abstract NodeId getNodeId();
+
+    public Mark getStartMark() {
+        return startMark;
+    }
+
+    public void setTag(Tag tag) {
+        if (tag == null) {
+            throw new NullPointerException("tag in a Node is required.");
+        }
+        this.tag = tag;
+    }
+
+    /**
+     * Two Nodes are never equal.
+     */
+    @Override
+    public final boolean equals(Object obj) {
+        return super.equals(obj);
+    }
+
+    public Class<? extends Object> getType() {
+        return type;
+    }
+
+    public void setType(Class<? extends Object> type) {
+        if (!type.isAssignableFrom(this.type)) {
+            this.type = type;
+        }
+    }
+
+    public void setTwoStepsConstruction(boolean twoStepsConstruction) {
+        this.twoStepsConstruction = twoStepsConstruction;
+    }
+
+    /**
+     * Indicates if this node must be constructed in two steps.
+     * <p>
+     * Two-step construction is required whenever a node is a child (direct or
+     * indirect) of it self. That is, if a recursive structure is build using
+     * anchors and aliases.
+     * </p>
+     * <p>
+     * Set by {@link org.yaml.snakeyaml.composer.Composer}, used during the
+     * construction process.
+     * </p>
+     * <p>
+     * Only relevant during loading.
+     * </p>
+     * 
+     * @return <code>true</code> if the node is self referenced.
+     */
+    public boolean isTwoStepsConstruction() {
+        return twoStepsConstruction;
+    }
+
+    @Override
+    public final int hashCode() {
+        return super.hashCode();
+    }
+
+    public boolean useClassConstructor() {
+        if (useClassConstructor == null) {
+            if (!tag.isSecondary() && isResolved() && !Object.class.equals(type)
+                    && !tag.equals(Tag.NULL)) {
+                return true;
+            } else if (tag.isCompatible(getType())) {
+                // the tag is compatible with the runtime class
+                // the tag will be ignored
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return useClassConstructor.booleanValue();
+    }
+
+    public void setUseClassConstructor(Boolean useClassConstructor) {
+        this.useClassConstructor = useClassConstructor;
+    }
+
+    /**
+     * Indicates if the tag was added by
+     * {@link org.yaml.snakeyaml.resolver.Resolver}.
+     * 
+     * @return <code>true</code> if the tag of this node was resolved</code>
+     */
+    public boolean isResolved() {
+        return resolved;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/NodeId.java b/src/main/java/org/yaml/snakeyaml/nodes/NodeId.java
new file mode 100644
index 0000000..178e26d
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/NodeId.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+/**
+ * Enum for the three basic YAML types: scalar, sequence and mapping.
+ */
+public enum NodeId {
+    scalar, sequence, mapping, anchor;
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/NodeTuple.java b/src/main/java/org/yaml/snakeyaml/nodes/NodeTuple.java
new file mode 100644
index 0000000..3e3cb36
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/NodeTuple.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+/**
+ * Stores one key value pair used in a map.
+ */
+public final class NodeTuple {
+
+    private Node keyNode;
+    private Node valueNode;
+
+    public NodeTuple(Node keyNode, Node valueNode) {
+        if (keyNode == null || valueNode == null) {
+            throw new NullPointerException("Nodes must be provided.");
+        }
+        this.keyNode = keyNode;
+        this.valueNode = valueNode;
+    }
+
+    /**
+     * Key node.
+     */
+    public Node getKeyNode() {
+        return keyNode;
+    }
+
+    /**
+     * Value node.
+     * 
+     * @return value
+     */
+    public Node getValueNode() {
+        return valueNode;
+    }
+
+    @Override
+    public String toString() {
+        return "<NodeTuple keyNode=" + keyNode.toString() + "; valueNode=" + valueNode.toString()
+                + ">";
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
new file mode 100644
index 0000000..9120d0e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Represents a scalar node.
+ * <p>
+ * Scalar nodes form the leaves in the node graph.
+ * </p>
+ */
+public class ScalarNode extends Node {
+    private Character style;
+    private String value;
+
+    public ScalarNode(Tag tag, String value, Mark startMark, Mark endMark, Character style) {
+        this(tag, true, value, startMark, endMark, style);
+    }
+
+    public ScalarNode(Tag tag, boolean resolved, String value, Mark startMark, Mark endMark,
+            Character style) {
+        super(tag, startMark, endMark);
+        if (value == null) {
+            throw new NullPointerException("value in a Node is required.");
+        }
+        this.value = value;
+        this.style = style;
+        this.resolved = resolved;
+    }
+
+    /**
+     * Get scalar style of this node.
+     * 
+     * @see org.yaml.snakeyaml.events.ScalarEvent
+     * @see <a href="http://yaml.org/spec/1.1/#id903915">Chapter 9. Scalar
+     *      Styles</a>
+     */
+    public Character getStyle() {
+        return style;
+    }
+
+    @Override
+    public NodeId getNodeId() {
+        return NodeId.scalar;
+    }
+
+    /**
+     * Value of this scalar.
+     * 
+     * @return Scalar's value.
+     */
+    public String getValue() {
+        return value;
+    }
+
+    public String toString() {
+        return "<" + this.getClass().getName() + " (tag=" + getTag() + ", value=" + getValue()
+                + ")>";
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/SequenceNode.java b/src/main/java/org/yaml/snakeyaml/nodes/SequenceNode.java
new file mode 100644
index 0000000..5e2f6b4
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/SequenceNode.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import java.util.List;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Represents a sequence.
+ * <p>
+ * A sequence is a ordered collection of nodes.
+ * </p>
+ */
+public class SequenceNode extends CollectionNode {
+    final private List<Node> value;
+
+    public SequenceNode(Tag tag, boolean resolved, List<Node> value, Mark startMark, Mark endMark,
+            Boolean flowStyle) {
+        super(tag, startMark, endMark, flowStyle);
+        if (value == null) {
+            throw new NullPointerException("value in a Node is required.");
+        }
+        this.value = value;
+        this.resolved = resolved;
+    }
+
+    public SequenceNode(Tag tag, List<Node> value, Boolean flowStyle) {
+        this(tag, true, value, null, null, flowStyle);
+    }
+
+    @Override
+    public NodeId getNodeId() {
+        return NodeId.sequence;
+    }
+
+    /**
+     * Returns the elements in this sequence.
+     * 
+     * @return Nodes in the specified order.
+     */
+    public List<Node> getValue() {
+        return value;
+    }
+
+    public void setListType(Class<? extends Object> listType) {
+        for (Node node : value) {
+            node.setType(listType);
+        }
+    }
+
+    public String toString() {
+        return "<" + this.getClass().getName() + " (tag=" + getTag() + ", value=" + getValue()
+                + ")>";
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/Tag.java b/src/main/java/org/yaml/snakeyaml/nodes/Tag.java
new file mode 100644
index 0000000..913e1f5
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/nodes/Tag.java
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.util.UriEncoder;
+
+public final class Tag implements Comparable<Tag> {
+    public static final String PREFIX = "tag:yaml.org,2002:";
+    public static final Tag YAML = new Tag(PREFIX + "yaml");
+    public static final Tag MERGE = new Tag(PREFIX + "merge");
+    public static final Tag SET = new Tag(PREFIX + "set");
+    public static final Tag PAIRS = new Tag(PREFIX + "pairs");
+    public static final Tag OMAP = new Tag(PREFIX + "omap");
+    public static final Tag BINARY = new Tag(PREFIX + "binary");
+    public static final Tag INT = new Tag(PREFIX + "int");
+    public static final Tag FLOAT = new Tag(PREFIX + "float");
+    public static final Tag TIMESTAMP = new Tag(PREFIX + "timestamp");
+    public static final Tag BOOL = new Tag(PREFIX + "bool");
+    public static final Tag NULL = new Tag(PREFIX + "null");
+    public static final Tag STR = new Tag(PREFIX + "str");
+    public static final Tag SEQ = new Tag(PREFIX + "seq");
+    public static final Tag MAP = new Tag(PREFIX + "map");
+    public static final Map<Tag, Set<Class<?>>> COMPATIBILITY_MAP;
+    static {
+        COMPATIBILITY_MAP = new HashMap<Tag, Set<Class<?>>>();
+        Set<Class<?>> floatSet = new HashSet<Class<?>>();
+        floatSet.add(Double.class);
+        floatSet.add(Float.class);
+        floatSet.add(BigDecimal.class);
+        COMPATIBILITY_MAP.put(FLOAT, floatSet);
+        //
+        Set<Class<?>> intSet = new HashSet<Class<?>>();
+        intSet.add(Integer.class);
+        intSet.add(Long.class);
+        intSet.add(BigInteger.class);
+        COMPATIBILITY_MAP.put(INT, intSet);
+        //
+        Set<Class<?>> timestampSet = new HashSet<Class<?>>();
+        timestampSet.add(Date.class);
+        timestampSet.add(java.sql.Date.class);
+        timestampSet.add(Timestamp.class);
+        COMPATIBILITY_MAP.put(TIMESTAMP, timestampSet);
+    }
+
+    private final String value;
+    private boolean secondary = false; // see http://www.yaml.org/refcard.html
+
+    public Tag(String tag) {
+        if (tag == null) {
+            throw new NullPointerException("Tag must be provided.");
+        } else if (tag.length() == 0) {
+            throw new IllegalArgumentException("Tag must not be empty.");
+        } else if (tag.trim().length() != tag.length()) {
+            throw new IllegalArgumentException("Tag must not contain leading or trailing spaces.");
+        }
+        this.value = UriEncoder.encode(tag);
+        this.secondary = !tag.startsWith(PREFIX);
+    }
+
+    public Tag(Class<? extends Object> clazz) {
+        if (clazz == null) {
+            throw new NullPointerException("Class for tag must be provided.");
+        }
+        this.value = Tag.PREFIX + UriEncoder.encode(clazz.getName());
+    }
+
+    //TODO to be removed ?
+    public Tag(URI uri) {
+        if (uri == null) {
+            throw new NullPointerException("URI for tag must be provided.");
+        }
+        this.value = uri.toASCIIString();
+    }
+
+    public boolean isSecondary() {
+        return secondary;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public boolean startsWith(String prefix) {
+        return value.startsWith(prefix);
+    }
+
+    public String getClassName() {
+        if (!value.startsWith(Tag.PREFIX)) {
+            throw new YAMLException("Invalid tag: " + value);
+        }
+        return UriEncoder.decode(value.substring(Tag.PREFIX.length()));
+    }
+
+    public int getLength() {
+        return value.length();
+    }
+
+    @Override
+    public String toString() {
+        return value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Tag) {
+            return value.equals(((Tag) obj).getValue());
+        } else
+            return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return value.hashCode();
+    }
+
+    /**
+     * Java has more then 1 class compatible with a language-independent tag
+     * (!!int, !!float, !!timestamp etc)
+     * 
+     * @param clazz
+     *            - Class to check compatibility
+     * @return true when the Class can be represented by this
+     *         language-independent tag
+     */
+    public boolean isCompatible(Class<?> clazz) {
+        Set<Class<?>> set = COMPATIBILITY_MAP.get(this);
+        if (set != null) {
+            return set.contains(clazz);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Check whether this tag matches the global tag for the Class
+     * 
+     * @param clazz
+     *            - Class to check
+     * @return true when the this tag can be used as a global tag for the Class
+     */
+    public boolean matches(Class<? extends Object> clazz) {
+        return value.equals(Tag.PREFIX + clazz.getName());
+    }
+
+    public int compareTo(Tag o) {
+        return value.compareTo(o.getValue());
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/parser/Parser.java b/src/main/java/org/yaml/snakeyaml/parser/Parser.java
new file mode 100644
index 0000000..8c1bf16
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/parser/Parser.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import org.yaml.snakeyaml.events.Event;
+
+/**
+ * This interface represents an input stream of {@link Event Events}.
+ * <p>
+ * The parser and the scanner form together the 'Parse' step in the loading
+ * process (see chapter 3.1 of the <a href="http://yaml.org/spec/1.1/">YAML
+ * Specification</a>).
+ * </p>
+ * 
+ * @see org.yaml.snakeyaml.events.Event
+ */
+public interface Parser {
+
+    /**
+     * Check if the next event is one of the given type.
+     * 
+     * @param choice
+     *            Event ID.
+     * @return <code>true</code> if the next event can be assigned to a variable
+     *         of the given type. Returns <code>false</code> if no more events
+     *         are available.
+     * @throws ParserException
+     *             Thrown in case of malformed input.
+     */
+    public boolean checkEvent(Event.ID choice);
+
+    /**
+     * Return the next event, but do not delete it from the stream.
+     * 
+     * @return The event that will be returned on the next call to
+     *         {@link #getEvent}
+     * @throws ParserException
+     *             Thrown in case of malformed input.
+     */
+    public Event peekEvent();
+
+    /**
+     * Returns the next event.
+     * <p>
+     * The event will be removed from the stream.
+     * </p>
+     * 
+     * @throws ParserException
+     *             Thrown in case of malformed input.
+     */
+    public Event getEvent();
+}
diff --git a/src/main/java/org/yaml/snakeyaml/parser/ParserException.java b/src/main/java/org/yaml/snakeyaml/parser/ParserException.java
new file mode 100644
index 0000000..fd4b1f1
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/parser/ParserException.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.MarkedYAMLException;
+
+/**
+ * Exception thrown by the {@link Parser} implementations in case of malformed
+ * input.
+ */
+public class ParserException extends MarkedYAMLException {
+    private static final long serialVersionUID = -2349253802798398038L;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param context
+     *            Part of the input document in which vicinity the problem
+     *            occurred.
+     * @param contextMark
+     *            Position of the <code>context</code> within the document.
+     * @param problem
+     *            Part of the input document that caused the problem.
+     * @param problemMark
+     *            Position of the <code>problem</code>. within the document.
+     */
+    public ParserException(String context, Mark contextMark, String problem, Mark problemMark) {
+        super(context, contextMark, problem, problemMark, null, null);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java b/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java
new file mode 100644
index 0000000..7fea2ee
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/parser/ParserImpl.java
@@ -0,0 +1,794 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.scanner.Scanner;
+import org.yaml.snakeyaml.scanner.ScannerImpl;
+import org.yaml.snakeyaml.tokens.AliasToken;
+import org.yaml.snakeyaml.tokens.AnchorToken;
+import org.yaml.snakeyaml.tokens.BlockEntryToken;
+import org.yaml.snakeyaml.tokens.DirectiveToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.StreamEndToken;
+import org.yaml.snakeyaml.tokens.StreamStartToken;
+import org.yaml.snakeyaml.tokens.TagToken;
+import org.yaml.snakeyaml.tokens.TagTuple;
+import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.util.ArrayStack;
+
+/**
+ * <pre>
+ * # The following YAML grammar is LL(1) and is parsed by a recursive descent
+ * parser.
+ * stream            ::= STREAM-START implicit_document? explicit_document* STREAM-END
+ * implicit_document ::= block_node DOCUMENT-END*
+ * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+ * block_node_or_indentless_sequence ::=
+ *                       ALIAS
+ *                       | properties (block_content | indentless_block_sequence)?
+ *                       | block_content
+ *                       | indentless_block_sequence
+ * block_node        ::= ALIAS
+ *                       | properties block_content?
+ *                       | block_content
+ * flow_node         ::= ALIAS
+ *                       | properties flow_content?
+ *                       | flow_content
+ * properties        ::= TAG ANCHOR? | ANCHOR TAG?
+ * block_content     ::= block_collection | flow_collection | SCALAR
+ * flow_content      ::= flow_collection | SCALAR
+ * block_collection  ::= block_sequence | block_mapping
+ * flow_collection   ::= flow_sequence | flow_mapping
+ * block_sequence    ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+ * indentless_sequence   ::= (BLOCK-ENTRY block_node?)+
+ * block_mapping     ::= BLOCK-MAPPING_START
+ *                       ((KEY block_node_or_indentless_sequence?)?
+ *                       (VALUE block_node_or_indentless_sequence?)?)*
+ *                       BLOCK-END
+ * flow_sequence     ::= FLOW-SEQUENCE-START
+ *                       (flow_sequence_entry FLOW-ENTRY)*
+ *                       flow_sequence_entry?
+ *                       FLOW-SEQUENCE-END
+ * flow_sequence_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * flow_mapping      ::= FLOW-MAPPING-START
+ *                       (flow_mapping_entry FLOW-ENTRY)*
+ *                       flow_mapping_entry?
+ *                       FLOW-MAPPING-END
+ * flow_mapping_entry    ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+ * FIRST sets:
+ * stream: { STREAM-START }
+ * explicit_document: { DIRECTIVE DOCUMENT-START }
+ * implicit_document: FIRST(block_node)
+ * block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START }
+ * flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START }
+ * block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
+ * flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR }
+ * block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START }
+ * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
+ * block_sequence: { BLOCK-SEQUENCE-START }
+ * block_mapping: { BLOCK-MAPPING-START }
+ * block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY }
+ * indentless_sequence: { ENTRY }
+ * flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START }
+ * flow_sequence: { FLOW-SEQUENCE-START }
+ * flow_mapping: { FLOW-MAPPING-START }
+ * flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
+ * flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY }
+ * </pre>
+ * 
+ * Since writing a recursive-descendant parser is a straightforward task, we do
+ * not give many comments here.
+ */
+public class ParserImpl implements Parser {
+    private static final Map<String, String> DEFAULT_TAGS = new HashMap<String, String>();
+    static {
+        DEFAULT_TAGS.put("!", "!");
+        DEFAULT_TAGS.put("!!", Tag.PREFIX);
+    }
+
+    protected final Scanner scanner;
+    private Event currentEvent;
+    private final ArrayStack<Production> states;
+    private final ArrayStack<Mark> marks;
+    private Production state;
+    private VersionTagsTuple directives;
+
+    public ParserImpl(StreamReader reader) {
+        this(new ScannerImpl(reader));
+    }
+
+    public ParserImpl(Scanner scanner) {
+        this.scanner = scanner;
+        currentEvent = null;
+        directives = new VersionTagsTuple(null, new HashMap<String, String>(DEFAULT_TAGS));
+        states = new ArrayStack<Production>(100);
+        marks = new ArrayStack<Mark>(10);
+        state = new ParseStreamStart();
+    }
+
+    /**
+     * Check the type of the next event.
+     */
+    public boolean checkEvent(Event.ID choice) {
+        peekEvent();
+        return currentEvent != null && currentEvent.is(choice);
+    }
+
+    /**
+     * Get the next event.
+     */
+    public Event peekEvent() {
+        if (currentEvent == null) {
+            if (state != null) {
+                currentEvent = state.produce();
+            }
+        }
+        return currentEvent;
+    }
+
+    /**
+     * Get the next event and proceed further.
+     */
+    public Event getEvent() {
+        peekEvent();
+        Event value = currentEvent;
+        currentEvent = null;
+        return value;
+    }
+
+    /**
+     * <pre>
+     * stream    ::= STREAM-START implicit_document? explicit_document* STREAM-END
+     * implicit_document ::= block_node DOCUMENT-END*
+     * explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+     * </pre>
+     */
+    private class ParseStreamStart implements Production {
+        public Event produce() {
+            // Parse the stream start.
+            StreamStartToken token = (StreamStartToken) scanner.getToken();
+            Event event = new StreamStartEvent(token.getStartMark(), token.getEndMark());
+            // Prepare the next state.
+            state = new ParseImplicitDocumentStart();
+            return event;
+        }
+    }
+
+    private class ParseImplicitDocumentStart implements Production {
+        public Event produce() {
+            // Parse an implicit document.
+            if (!scanner.checkToken(Token.ID.Directive, Token.ID.DocumentStart, Token.ID.StreamEnd)) {
+                directives = new VersionTagsTuple(null, DEFAULT_TAGS);
+                Token token = scanner.peekToken();
+                Mark startMark = token.getStartMark();
+                Mark endMark = startMark;
+                Event event = new DocumentStartEvent(startMark, endMark, false, null, null);
+                // Prepare the next state.
+                states.push(new ParseDocumentEnd());
+                state = new ParseBlockNode();
+                return event;
+            } else {
+                Production p = new ParseDocumentStart();
+                return p.produce();
+            }
+        }
+    }
+
+    private class ParseDocumentStart implements Production {
+        public Event produce() {
+            // Parse any extra document end indicators.
+            while (scanner.checkToken(Token.ID.DocumentEnd)) {
+                scanner.getToken();
+            }
+            // Parse an explicit document.
+            Event event;
+            if (!scanner.checkToken(Token.ID.StreamEnd)) {
+                Token token = scanner.peekToken();
+                Mark startMark = token.getStartMark();
+                VersionTagsTuple tuple = processDirectives();
+                if (!scanner.checkToken(Token.ID.DocumentStart)) {
+                    throw new ParserException(null, null, "expected '<document start>', but found "
+                            + scanner.peekToken().getTokenId(), scanner.peekToken().getStartMark());
+                }
+                token = scanner.getToken();
+                Mark endMark = token.getEndMark();
+                event = new DocumentStartEvent(startMark, endMark, true, tuple.getVersion(),
+                        tuple.getTags());
+                states.push(new ParseDocumentEnd());
+                state = new ParseDocumentContent();
+            } else {
+                // Parse the end of the stream.
+                StreamEndToken token = (StreamEndToken) scanner.getToken();
+                event = new StreamEndEvent(token.getStartMark(), token.getEndMark());
+                if (!states.isEmpty()) {
+                    throw new YAMLException("Unexpected end of stream. States left: " + states);
+                }
+                if (!marks.isEmpty()) {
+                    throw new YAMLException("Unexpected end of stream. Marks left: " + marks);
+                }
+                state = null;
+            }
+            return event;
+        }
+    }
+
+    private class ParseDocumentEnd implements Production {
+        public Event produce() {
+            // Parse the document end.
+            Token token = scanner.peekToken();
+            Mark startMark = token.getStartMark();
+            Mark endMark = startMark;
+            boolean explicit = false;
+            if (scanner.checkToken(Token.ID.DocumentEnd)) {
+                token = scanner.getToken();
+                endMark = token.getEndMark();
+                explicit = true;
+            }
+            Event event = new DocumentEndEvent(startMark, endMark, explicit);
+            // Prepare the next state.
+            state = new ParseDocumentStart();
+            return event;
+        }
+    }
+
+    private class ParseDocumentContent implements Production {
+        public Event produce() {
+            Event event;
+            if (scanner.checkToken(Token.ID.Directive, Token.ID.DocumentStart,
+                    Token.ID.DocumentEnd, Token.ID.StreamEnd)) {
+                event = processEmptyScalar(scanner.peekToken().getStartMark());
+                state = states.pop();
+                return event;
+            } else {
+                Production p = new ParseBlockNode();
+                return p.produce();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private VersionTagsTuple processDirectives() {
+        Version yamlVersion = null;
+        HashMap<String, String> tagHandles = new HashMap<String, String>();
+        while (scanner.checkToken(Token.ID.Directive)) {
+            @SuppressWarnings("rawtypes")
+            DirectiveToken token = (DirectiveToken) scanner.getToken();
+            if (token.getName().equals("YAML")) {
+                if (yamlVersion != null) {
+                    throw new ParserException(null, null, "found duplicate YAML directive",
+                            token.getStartMark());
+                }
+                List<Integer> value = (List<Integer>) token.getValue();
+                Integer major = value.get(0);
+                if (major != 1) {
+                    throw new ParserException(null, null,
+                            "found incompatible YAML document (version 1.* is required)",
+                            token.getStartMark());
+                }
+                Integer minor = value.get(1);
+                switch (minor) {
+                case 0:
+                    yamlVersion = Version.V1_0;
+                    break;
+
+                default:
+                    yamlVersion = Version.V1_1;
+                    break;
+                }
+            } else if (token.getName().equals("TAG")) {
+                List<String> value = (List<String>) token.getValue();
+                String handle = value.get(0);
+                String prefix = value.get(1);
+                if (tagHandles.containsKey(handle)) {
+                    throw new ParserException(null, null, "duplicate tag handle " + handle,
+                            token.getStartMark());
+                }
+                tagHandles.put(handle, prefix);
+            }
+        }
+        if (yamlVersion != null || !tagHandles.isEmpty()) {
+            // directives in the document found - drop the previous
+            for (String key : DEFAULT_TAGS.keySet()) {
+                // do not overwrite re-defined tags
+                if (!tagHandles.containsKey(key)) {
+                    tagHandles.put(key, DEFAULT_TAGS.get(key));
+                }
+            }
+            directives = new VersionTagsTuple(yamlVersion, tagHandles);
+        }
+        return directives;
+    }
+
+    /**
+     * <pre>
+     *  block_node_or_indentless_sequence ::= ALIAS
+     *                | properties (block_content | indentless_block_sequence)?
+     *                | block_content
+     *                | indentless_block_sequence
+     *  block_node    ::= ALIAS
+     *                    | properties block_content?
+     *                    | block_content
+     *  flow_node     ::= ALIAS
+     *                    | properties flow_content?
+     *                    | flow_content
+     *  properties    ::= TAG ANCHOR? | ANCHOR TAG?
+     *  block_content     ::= block_collection | flow_collection | SCALAR
+     *  flow_content      ::= flow_collection | SCALAR
+     *  block_collection  ::= block_sequence | block_mapping
+     *  flow_collection   ::= flow_sequence | flow_mapping
+     * </pre>
+     */
+
+    private class ParseBlockNode implements Production {
+        public Event produce() {
+            return parseNode(true, false);
+        }
+    }
+
+    private Event parseFlowNode() {
+        return parseNode(false, false);
+    }
+
+    private Event parseBlockNodeOrIndentlessSequence() {
+        return parseNode(true, true);
+    }
+
+    private Event parseNode(boolean block, boolean indentlessSequence) {
+        Event event;
+        Mark startMark = null;
+        Mark endMark = null;
+        Mark tagMark = null;
+        if (scanner.checkToken(Token.ID.Alias)) {
+            AliasToken token = (AliasToken) scanner.getToken();
+            event = new AliasEvent(token.getValue(), token.getStartMark(), token.getEndMark());
+            state = states.pop();
+        } else {
+            String anchor = null;
+            TagTuple tagTokenTag = null;
+            if (scanner.checkToken(Token.ID.Anchor)) {
+                AnchorToken token = (AnchorToken) scanner.getToken();
+                startMark = token.getStartMark();
+                endMark = token.getEndMark();
+                anchor = token.getValue();
+                if (scanner.checkToken(Token.ID.Tag)) {
+                    TagToken tagToken = (TagToken) scanner.getToken();
+                    tagMark = tagToken.getStartMark();
+                    endMark = tagToken.getEndMark();
+                    tagTokenTag = tagToken.getValue();
+                }
+            } else if (scanner.checkToken(Token.ID.Tag)) {
+                TagToken tagToken = (TagToken) scanner.getToken();
+                startMark = tagToken.getStartMark();
+                tagMark = startMark;
+                endMark = tagToken.getEndMark();
+                tagTokenTag = tagToken.getValue();
+                if (scanner.checkToken(Token.ID.Anchor)) {
+                    AnchorToken token = (AnchorToken) scanner.getToken();
+                    endMark = token.getEndMark();
+                    anchor = token.getValue();
+                }
+            }
+            String tag = null;
+            if (tagTokenTag != null) {
+                String handle = tagTokenTag.getHandle();
+                String suffix = tagTokenTag.getSuffix();
+                if (handle != null) {
+                    if (!directives.getTags().containsKey(handle)) {
+                        throw new ParserException("while parsing a node", startMark,
+                                "found undefined tag handle " + handle, tagMark);
+                    }
+                    tag = directives.getTags().get(handle) + suffix;
+                } else {
+                    tag = suffix;
+                }
+            }
+            if (startMark == null) {
+                startMark = scanner.peekToken().getStartMark();
+                endMark = startMark;
+            }
+            event = null;
+            boolean implicit = tag == null || tag.equals("!");
+            if (indentlessSequence && scanner.checkToken(Token.ID.BlockEntry)) {
+                endMark = scanner.peekToken().getEndMark();
+                event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark,
+                        Boolean.FALSE);
+                state = new ParseIndentlessSequenceEntry();
+            } else {
+                if (scanner.checkToken(Token.ID.Scalar)) {
+                    ScalarToken token = (ScalarToken) scanner.getToken();
+                    endMark = token.getEndMark();
+                    ImplicitTuple implicitValues;
+                    if ((token.getPlain() && tag == null) || "!".equals(tag)) {
+                        implicitValues = new ImplicitTuple(true, false);
+                    } else if (tag == null) {
+                        implicitValues = new ImplicitTuple(false, true);
+                    } else {
+                        implicitValues = new ImplicitTuple(false, false);
+                    }
+                    event = new ScalarEvent(anchor, tag, implicitValues, token.getValue(),
+                            startMark, endMark, token.getStyle());
+                    state = states.pop();
+                } else if (scanner.checkToken(Token.ID.FlowSequenceStart)) {
+                    endMark = scanner.peekToken().getEndMark();
+                    event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark,
+                            Boolean.TRUE);
+                    state = new ParseFlowSequenceFirstEntry();
+                } else if (scanner.checkToken(Token.ID.FlowMappingStart)) {
+                    endMark = scanner.peekToken().getEndMark();
+                    event = new MappingStartEvent(anchor, tag, implicit, startMark, endMark,
+                            Boolean.TRUE);
+                    state = new ParseFlowMappingFirstKey();
+                } else if (block && scanner.checkToken(Token.ID.BlockSequenceStart)) {
+                    endMark = scanner.peekToken().getStartMark();
+                    event = new SequenceStartEvent(anchor, tag, implicit, startMark, endMark,
+                            Boolean.FALSE);
+                    state = new ParseBlockSequenceFirstEntry();
+                } else if (block && scanner.checkToken(Token.ID.BlockMappingStart)) {
+                    endMark = scanner.peekToken().getStartMark();
+                    event = new MappingStartEvent(anchor, tag, implicit, startMark, endMark,
+                            Boolean.FALSE);
+                    state = new ParseBlockMappingFirstKey();
+                } else if (anchor != null || tag != null) {
+                    // Empty scalars are allowed even if a tag or an anchor is
+                    // specified.
+                    event = new ScalarEvent(anchor, tag, new ImplicitTuple(implicit, false), "",
+                            startMark, endMark, (char) 0);
+                    state = states.pop();
+                } else {
+                    String node;
+                    if (block) {
+                        node = "block";
+                    } else {
+                        node = "flow";
+                    }
+                    Token token = scanner.peekToken();
+                    throw new ParserException("while parsing a " + node + " node", startMark,
+                            "expected the node content, but found " + token.getTokenId(),
+                            token.getStartMark());
+                }
+            }
+        }
+        return event;
+    }
+
+    // block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)*
+    // BLOCK-END
+
+    private class ParseBlockSequenceFirstEntry implements Production {
+        public Event produce() {
+            Token token = scanner.getToken();
+            marks.push(token.getStartMark());
+            return new ParseBlockSequenceEntry().produce();
+        }
+    }
+
+    private class ParseBlockSequenceEntry implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.BlockEntry)) {
+                BlockEntryToken token = (BlockEntryToken) scanner.getToken();
+                if (!scanner.checkToken(Token.ID.BlockEntry, Token.ID.BlockEnd)) {
+                    states.push(new ParseBlockSequenceEntry());
+                    return new ParseBlockNode().produce();
+                } else {
+                    state = new ParseBlockSequenceEntry();
+                    return processEmptyScalar(token.getEndMark());
+                }
+            }
+            if (!scanner.checkToken(Token.ID.BlockEnd)) {
+                Token token = scanner.peekToken();
+                throw new ParserException("while parsing a block collection", marks.pop(),
+                        "expected <block end>, but found " + token.getTokenId(),
+                        token.getStartMark());
+            }
+            Token token = scanner.getToken();
+            Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark());
+            state = states.pop();
+            marks.pop();
+            return event;
+        }
+    }
+
+    // indentless_sequence ::= (BLOCK-ENTRY block_node?)+
+
+    private class ParseIndentlessSequenceEntry implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.BlockEntry)) {
+                Token token = scanner.getToken();
+                if (!scanner.checkToken(Token.ID.BlockEntry, Token.ID.Key, Token.ID.Value,
+                        Token.ID.BlockEnd)) {
+                    states.push(new ParseIndentlessSequenceEntry());
+                    return new ParseBlockNode().produce();
+                } else {
+                    state = new ParseIndentlessSequenceEntry();
+                    return processEmptyScalar(token.getEndMark());
+                }
+            }
+            Token token = scanner.peekToken();
+            Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark());
+            state = states.pop();
+            return event;
+        }
+    }
+
+    private class ParseBlockMappingFirstKey implements Production {
+        public Event produce() {
+            Token token = scanner.getToken();
+            marks.push(token.getStartMark());
+            return new ParseBlockMappingKey().produce();
+        }
+    }
+
+    private class ParseBlockMappingKey implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.Key)) {
+                Token token = scanner.getToken();
+                if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) {
+                    states.push(new ParseBlockMappingValue());
+                    return parseBlockNodeOrIndentlessSequence();
+                } else {
+                    state = new ParseBlockMappingValue();
+                    return processEmptyScalar(token.getEndMark());
+                }
+            }
+            if (!scanner.checkToken(Token.ID.BlockEnd)) {
+                Token token = scanner.peekToken();
+                throw new ParserException("while parsing a block mapping", marks.pop(),
+                        "expected <block end>, but found " + token.getTokenId(),
+                        token.getStartMark());
+            }
+            Token token = scanner.getToken();
+            Event event = new MappingEndEvent(token.getStartMark(), token.getEndMark());
+            state = states.pop();
+            marks.pop();
+            return event;
+        }
+    }
+
+    private class ParseBlockMappingValue implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.Value)) {
+                Token token = scanner.getToken();
+                if (!scanner.checkToken(Token.ID.Key, Token.ID.Value, Token.ID.BlockEnd)) {
+                    states.push(new ParseBlockMappingKey());
+                    return parseBlockNodeOrIndentlessSequence();
+                } else {
+                    state = new ParseBlockMappingKey();
+                    return processEmptyScalar(token.getEndMark());
+                }
+            }
+            state = new ParseBlockMappingKey();
+            Token token = scanner.peekToken();
+            return processEmptyScalar(token.getStartMark());
+        }
+    }
+
+    /**
+     * <pre>
+     * flow_sequence     ::= FLOW-SEQUENCE-START
+     *                       (flow_sequence_entry FLOW-ENTRY)*
+     *                       flow_sequence_entry?
+     *                       FLOW-SEQUENCE-END
+     * flow_sequence_entry   ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+     * Note that while production rules for both flow_sequence_entry and
+     * flow_mapping_entry are equal, their interpretations are different.
+     * For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?`
+     * generate an inline mapping (set syntax).
+     * </pre>
+     */
+    private class ParseFlowSequenceFirstEntry implements Production {
+        public Event produce() {
+            Token token = scanner.getToken();
+            marks.push(token.getStartMark());
+            return new ParseFlowSequenceEntry(true).produce();
+        }
+    }
+
+    private class ParseFlowSequenceEntry implements Production {
+        private boolean first = false;
+
+        public ParseFlowSequenceEntry(boolean first) {
+            this.first = first;
+        }
+
+        public Event produce() {
+            if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
+                if (!first) {
+                    if (scanner.checkToken(Token.ID.FlowEntry)) {
+                        scanner.getToken();
+                    } else {
+                        Token token = scanner.peekToken();
+                        throw new ParserException("while parsing a flow sequence", marks.pop(),
+                                "expected ',' or ']', but got " + token.getTokenId(),
+                                token.getStartMark());
+                    }
+                }
+                if (scanner.checkToken(Token.ID.Key)) {
+                    Token token = scanner.peekToken();
+                    Event event = new MappingStartEvent(null, null, true, token.getStartMark(),
+                            token.getEndMark(), Boolean.TRUE);
+                    state = new ParseFlowSequenceEntryMappingKey();
+                    return event;
+                } else if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
+                    states.push(new ParseFlowSequenceEntry(false));
+                    return parseFlowNode();
+                }
+            }
+            Token token = scanner.getToken();
+            Event event = new SequenceEndEvent(token.getStartMark(), token.getEndMark());
+            state = states.pop();
+            marks.pop();
+            return event;
+        }
+    }
+
+    private class ParseFlowSequenceEntryMappingKey implements Production {
+        public Event produce() {
+            Token token = scanner.getToken();
+            if (!scanner.checkToken(Token.ID.Value, Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) {
+                states.push(new ParseFlowSequenceEntryMappingValue());
+                return parseFlowNode();
+            } else {
+                state = new ParseFlowSequenceEntryMappingValue();
+                return processEmptyScalar(token.getEndMark());
+            }
+        }
+    }
+
+    private class ParseFlowSequenceEntryMappingValue implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.Value)) {
+                Token token = scanner.getToken();
+                if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowSequenceEnd)) {
+                    states.push(new ParseFlowSequenceEntryMappingEnd());
+                    return parseFlowNode();
+                } else {
+                    state = new ParseFlowSequenceEntryMappingEnd();
+                    return processEmptyScalar(token.getEndMark());
+                }
+            } else {
+                state = new ParseFlowSequenceEntryMappingEnd();
+                Token token = scanner.peekToken();
+                return processEmptyScalar(token.getStartMark());
+            }
+        }
+    }
+
+    private class ParseFlowSequenceEntryMappingEnd implements Production {
+        public Event produce() {
+            state = new ParseFlowSequenceEntry(false);
+            Token token = scanner.peekToken();
+            return new MappingEndEvent(token.getStartMark(), token.getEndMark());
+        }
+    }
+
+    /**
+     * <pre>
+     *   flow_mapping  ::= FLOW-MAPPING-START
+     *          (flow_mapping_entry FLOW-ENTRY)*
+     *          flow_mapping_entry?
+     *          FLOW-MAPPING-END
+     *   flow_mapping_entry    ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+     * </pre>
+     */
+    private class ParseFlowMappingFirstKey implements Production {
+        public Event produce() {
+            Token token = scanner.getToken();
+            marks.push(token.getStartMark());
+            return new ParseFlowMappingKey(true).produce();
+        }
+    }
+
+    private class ParseFlowMappingKey implements Production {
+        private boolean first = false;
+
+        public ParseFlowMappingKey(boolean first) {
+            this.first = first;
+        }
+
+        public Event produce() {
+            if (!scanner.checkToken(Token.ID.FlowMappingEnd)) {
+                if (!first) {
+                    if (scanner.checkToken(Token.ID.FlowEntry)) {
+                        scanner.getToken();
+                    } else {
+                        Token token = scanner.peekToken();
+                        throw new ParserException("while parsing a flow mapping", marks.pop(),
+                                "expected ',' or '}', but got " + token.getTokenId(),
+                                token.getStartMark());
+                    }
+                }
+                if (scanner.checkToken(Token.ID.Key)) {
+                    Token token = scanner.getToken();
+                    if (!scanner.checkToken(Token.ID.Value, Token.ID.FlowEntry,
+                            Token.ID.FlowMappingEnd)) {
+                        states.push(new ParseFlowMappingValue());
+                        return parseFlowNode();
+                    } else {
+                        state = new ParseFlowMappingValue();
+                        return processEmptyScalar(token.getEndMark());
+                    }
+                } else if (!scanner.checkToken(Token.ID.FlowMappingEnd)) {
+                    states.push(new ParseFlowMappingEmptyValue());
+                    return parseFlowNode();
+                }
+            }
+            Token token = scanner.getToken();
+            Event event = new MappingEndEvent(token.getStartMark(), token.getEndMark());
+            state = states.pop();
+            marks.pop();
+            return event;
+        }
+    }
+
+    private class ParseFlowMappingValue implements Production {
+        public Event produce() {
+            if (scanner.checkToken(Token.ID.Value)) {
+                Token token = scanner.getToken();
+                if (!scanner.checkToken(Token.ID.FlowEntry, Token.ID.FlowMappingEnd)) {
+                    states.push(new ParseFlowMappingKey(false));
+                    return parseFlowNode();
+                } else {
+                    state = new ParseFlowMappingKey(false);
+                    return processEmptyScalar(token.getEndMark());
+                }
+            } else {
+                state = new ParseFlowMappingKey(false);
+                Token token = scanner.peekToken();
+                return processEmptyScalar(token.getStartMark());
+            }
+        }
+    }
+
+    private class ParseFlowMappingEmptyValue implements Production {
+        public Event produce() {
+            state = new ParseFlowMappingKey(false);
+            return processEmptyScalar(scanner.peekToken().getStartMark());
+        }
+    }
+
+    /**
+     * <pre>
+     * block_mapping     ::= BLOCK-MAPPING_START
+     *           ((KEY block_node_or_indentless_sequence?)?
+     *           (VALUE block_node_or_indentless_sequence?)?)*
+     *           BLOCK-END
+     * </pre>
+     */
+    private Event processEmptyScalar(Mark mark) {
+        return new ScalarEvent(null, null, new ImplicitTuple(true, false), "", mark, mark, (char) 0);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/parser/Production.java b/src/main/java/org/yaml/snakeyaml/parser/Production.java
new file mode 100644
index 0000000..5dd3949
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/parser/Production.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import org.yaml.snakeyaml.events.Event;
+
+/**
+ * Helper for {@link ParserImpl}. A grammar rule to apply given the symbols on
+ * top of its stack and the next input token
+ * 
+ * @see <a href="http://en.wikipedia.org/wiki/LL_parser"></a>
+ */
+interface Production {
+    Event produce();
+}
diff --git a/src/main/java/org/yaml/snakeyaml/parser/VersionTagsTuple.java b/src/main/java/org/yaml/snakeyaml/parser/VersionTagsTuple.java
new file mode 100644
index 0000000..44ed2fb
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/parser/VersionTagsTuple.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+
+/**
+ * Store the internal state for directives
+ */
+class VersionTagsTuple {
+    private Version version;
+    private Map<String, String> tags;
+
+    public VersionTagsTuple(Version version, Map<String, String> tags) {
+        this.version = version;
+        this.tags = tags;
+    }
+
+    public Version getVersion() {
+        return version;
+    }
+
+    public Map<String, String> getTags() {
+        return tags;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("VersionTagsTuple<%s, %s>", version, tags);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/reader/ReaderException.java b/src/main/java/org/yaml/snakeyaml/reader/ReaderException.java
new file mode 100644
index 0000000..7a8a06b
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/reader/ReaderException.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class ReaderException extends YAMLException {
+    private static final long serialVersionUID = 8710781187529689083L;
+    private final String name;
+    private final char character;
+    private final int position;
+
+    public ReaderException(String name, int position, char character, String message) {
+        super(message);
+        this.name = name;
+        this.character = character;
+        this.position = position;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public char getCharacter() {
+        return character;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    @Override
+    public String toString() {
+        return "unacceptable character '" + character + "' (0x"
+                + Integer.toHexString((int) character).toUpperCase() + ") " + getMessage()
+                + "\nin \"" + name + "\", position " + position;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/reader/StreamReader.java b/src/main/java/org/yaml/snakeyaml/reader/StreamReader.java
new file mode 100644
index 0000000..56ec007
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/reader/StreamReader.java
@@ -0,0 +1,220 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.scanner.Constant;
+
+/**
+ * Reader: checks if characters are in allowed range, adds '\0' to the end.
+ */
+public class StreamReader {
+    public final static Pattern NON_PRINTABLE = Pattern
+            .compile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD]");
+    private String name;
+    private final Reader stream;
+    private int pointer = 0;
+    private boolean eof = true;
+    private String buffer;
+    private int index = 0;
+    private int line = 0;
+    private int column = 0;
+    private char[] data;
+
+    public StreamReader(String stream) {
+        this.name = "'string'";
+        this.buffer = ""; // to set length to 0
+        checkPrintable(stream);
+        this.buffer = stream + "\0";
+        this.stream = null;
+        this.eof = true;
+        this.data = null;
+    }
+
+    public StreamReader(Reader reader) {
+        this.name = "'reader'";
+        this.buffer = "";
+        this.stream = reader;
+        this.eof = false;
+        this.data = new char[1024];
+        this.update();
+    }
+
+    void checkPrintable(CharSequence data) {
+        Matcher em = NON_PRINTABLE.matcher(data);
+        if (em.find()) {
+            int position = this.index + this.buffer.length() - this.pointer + em.start();
+            throw new ReaderException(name, position, em.group().charAt(0),
+                    "special characters are not allowed");
+        }
+    }
+
+    /**
+     * Checks <code>chars</chars> for the non-printable characters.
+     * 
+     * @param chars
+     *            the array where to search.
+     * @param begin
+     *            the beginning index, inclusive.
+     * @param end
+     *            the ending index, exclusive.
+     * @throws ReaderException
+     *             if <code>chars</code> contains non-printable character(s).
+     */
+    void checkPrintable(final char[] chars, final int begin, final int end) {
+        for (int i = begin; i < end; i++) {
+            final char c = chars[i];
+
+            if (isPrintable(c)) {
+                continue;
+            }
+
+            int position = this.index + this.buffer.length() - this.pointer + i;
+            throw new ReaderException(name, position, c, "special characters are not allowed");
+        }
+    }
+
+    public static boolean isPrintable(final char c) {
+        return (c >= '\u0020' && c <= '\u007E') || c == '\n' || c == '\r' || c == '\t'
+                || c == '\u0085' || (c >= '\u00A0' && c <= '\uD7FF')
+                || (c >= '\uE000' && c <= '\uFFFD');
+    }
+
+    public Mark getMark() {
+        return new Mark(name, this.index, this.line, this.column, this.buffer, this.pointer);
+    }
+
+    public void forward() {
+        forward(1);
+    }
+
+    /**
+     * read the next length characters and move the pointer.
+     * 
+     * @param length
+     */
+    public void forward(int length) {
+        if (this.pointer + length + 1 >= this.buffer.length()) {
+            update();
+        }
+        char ch = 0;
+        for (int i = 0; i < length; i++) {
+            ch = this.buffer.charAt(this.pointer);
+            this.pointer++;
+            this.index++;
+            if (Constant.LINEBR.has(ch) || (ch == '\r' && buffer.charAt(pointer) != '\n')) {
+                this.line++;
+                this.column = 0;
+            } else if (ch != '\uFEFF') {
+                this.column++;
+            }
+        }
+    }
+
+    public char peek() {
+        return this.buffer.charAt(this.pointer);
+    }
+
+    /**
+     * Peek the next index-th character
+     * 
+     * @param index
+     * @return the next index-th character
+     */
+    public char peek(int index) {
+        if (this.pointer + index + 1 > this.buffer.length()) {
+            update();
+        }
+        return this.buffer.charAt(this.pointer + index);
+    }
+
+    /**
+     * peek the next length characters
+     * 
+     * @param length
+     * @return the next length characters
+     */
+    public String prefix(int length) {
+        if (this.pointer + length >= this.buffer.length()) {
+            update();
+        }
+        if (this.pointer + length > this.buffer.length()) {
+            return this.buffer.substring(this.pointer);
+        }
+        return this.buffer.substring(this.pointer, this.pointer + length);
+    }
+
+    /**
+     * prefix(length) immediately followed by forward(length)
+     */
+    public String prefixForward(int length) {
+        final String prefix = prefix(length);
+        this.pointer += length;
+        this.index += length;
+        // prefix never contains new line characters
+        this.column += length;
+        return prefix;
+    }
+
+    private void update() {
+        if (!this.eof) {
+            this.buffer = buffer.substring(this.pointer);
+            this.pointer = 0;
+            try {
+                int converted = this.stream.read(data);
+                if (converted > 0) {
+                    /*
+                     * Let's create StringBuilder manually. Anyway str1 + str2
+                     * generates new StringBuilder(str1).append(str2).toSting()
+                     * Giving correct capacity to the constructor prevents
+                     * unnecessary operations in appends.
+                     */
+                    checkPrintable(data, 0, converted);
+                    this.buffer = new StringBuilder(buffer.length() + converted).append(buffer)
+                            .append(data, 0, converted).toString();
+                } else {
+                    this.eof = true;
+                    this.buffer += "\0";
+                }
+            } catch (IOException ioe) {
+                throw new YAMLException(ioe);
+            }
+        }
+    }
+
+    public int getColumn() {
+        return column;
+    }
+
+    public Charset getEncoding() {
+        return Charset.forName(((UnicodeReader) this.stream).getEncoding());
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public int getLine() {
+        return line;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/reader/UnicodeReader.java b/src/main/java/org/yaml/snakeyaml/reader/UnicodeReader.java
new file mode 100644
index 0000000..dd9dc39
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/reader/UnicodeReader.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+/**
+ version: 1.1 / 2007-01-25
+ - changed BOM recognition ordering (longer boms first)
+
+ Original pseudocode   : Thomas Weidenfeller
+ Implementation tweaked: Aki Nieminen
+ Implementation changed: Andrey Somov 
+ * UTF-32 removed because it is not supported by YAML
+ * no default encoding
+
+ http://www.unicode.org/unicode/faq/utf_bom.html
+ BOMs:
+ 00 00 FE FF    = UTF-32, big-endian
+ FF FE 00 00    = UTF-32, little-endian
+ EF BB BF       = UTF-8,
+ FE FF          = UTF-16, big-endian
+ FF FE          = UTF-16, little-endian
+
+ Win2k Notepad:
+ Unicode format = UTF-16LE
+ ***/
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Generic unicode textreader, which will use BOM mark to identify the encoding
+ * to be used. If BOM is not found then use a given default or system encoding.
+ */
+public class UnicodeReader extends Reader {
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+    private static final Charset UTF16BE = Charset.forName("UTF-16BE");
+    private static final Charset UTF16LE = Charset.forName("UTF-16LE");
+
+    PushbackInputStream internalIn;
+    InputStreamReader internalIn2 = null;
+
+    private static final int BOM_SIZE = 3;
+
+    /**
+     * @param in
+     *            InputStream to be read
+     */
+    public UnicodeReader(InputStream in) {
+        internalIn = new PushbackInputStream(in, BOM_SIZE);
+    }
+
+    /**
+     * Get stream encoding or NULL if stream is uninitialized. Call init() or
+     * read() method to initialize it.
+     */
+    public String getEncoding() {
+        return internalIn2.getEncoding();
+    }
+
+    /**
+     * Read-ahead four bytes and check for BOM marks. Extra bytes are unread
+     * back to the stream, only BOM bytes are skipped.
+     */
+    protected void init() throws IOException {
+        if (internalIn2 != null)
+            return;
+
+        Charset encoding;
+        byte bom[] = new byte[BOM_SIZE];
+        int n, unread;
+        n = internalIn.read(bom, 0, bom.length);
+
+        if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
+            encoding = UTF8;
+            unread = n - 3;
+        } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
+            encoding = UTF16BE;
+            unread = n - 2;
+        } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
+            encoding = UTF16LE;
+            unread = n - 2;
+        } else {
+            // Unicode BOM mark not found, unread all bytes
+            encoding = UTF8;
+            unread = n;
+        }
+
+        if (unread > 0)
+            internalIn.unread(bom, (n - unread), unread);
+
+        // Use given encoding
+        CharsetDecoder decoder = encoding.newDecoder().onUnmappableCharacter(
+                CodingErrorAction.REPORT);
+        internalIn2 = new InputStreamReader(internalIn, decoder);
+    }
+
+    public void close() throws IOException {
+        init();
+        internalIn2.close();
+    }
+
+    public int read(char[] cbuf, int off, int len) throws IOException {
+        init();
+        return internalIn2.read(cbuf, off, len);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java b/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java
new file mode 100644
index 0000000..c4419fb
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java
@@ -0,0 +1,202 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.nodes.AnchorNode;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Represent basic YAML structures: scalar, sequence, mapping
+ */
+public abstract class BaseRepresenter {
+    protected final Map<Class<?>, Represent> representers = new HashMap<Class<?>, Represent>();
+    /**
+     * in Java 'null' is not a type. So we have to keep the null representer
+     * separately otherwise it will coincide with the default representer which
+     * is stored with the key null.
+     */
+    protected Represent nullRepresenter;
+    // the order is important (map can be also a sequence of key-values)
+    protected final Map<Class<?>, Represent> multiRepresenters = new LinkedHashMap<Class<?>, Represent>();
+    protected Character defaultScalarStyle;
+    protected FlowStyle defaultFlowStyle = FlowStyle.AUTO;
+    protected final Map<Object, Node> representedObjects = new IdentityHashMap<Object, Node>() {
+        private static final long serialVersionUID = -5576159264232131854L;
+
+        public Node put(Object key, Node value) {
+            return super.put(key, new AnchorNode(value));
+        }
+    };
+
+    protected Object objectToRepresent;
+    private PropertyUtils propertyUtils;
+    private boolean explicitPropertyUtils = false;
+
+    public Node represent(Object data) {
+        Node node = representData(data);
+        representedObjects.clear();
+        objectToRepresent = null;
+        return node;
+    }
+
+    protected final Node representData(Object data) {
+        objectToRepresent = data;
+        // check for identity
+        if (representedObjects.containsKey(objectToRepresent)) {
+            Node node = representedObjects.get(objectToRepresent);
+            return node;
+        }
+        // }
+        // check for null first
+        if (data == null) {
+            Node node = nullRepresenter.representData(null);
+            return node;
+        }
+        // check the same class
+        Node node;
+        Class<?> clazz = data.getClass();
+        if (representers.containsKey(clazz)) {
+            Represent representer = representers.get(clazz);
+            node = representer.representData(data);
+        } else {
+            // check the parents
+            for (Class<?> repr : multiRepresenters.keySet()) {
+                if (repr.isInstance(data)) {
+                    Represent representer = multiRepresenters.get(repr);
+                    node = representer.representData(data);
+                    return node;
+                }
+            }
+
+            // check defaults
+            if (multiRepresenters.containsKey(null)) {
+                Represent representer = multiRepresenters.get(null);
+                node = representer.representData(data);
+            } else {
+                Represent representer = representers.get(null);
+                node = representer.representData(data);
+            }
+        }
+        return node;
+    }
+
+    protected Node representScalar(Tag tag, String value, Character style) {
+        if (style == null) {
+            style = this.defaultScalarStyle;
+        }
+        Node node = new ScalarNode(tag, value, null, null, style);
+        return node;
+    }
+
+    protected Node representScalar(Tag tag, String value) {
+        return representScalar(tag, value, null);
+    }
+
+    protected Node representSequence(Tag tag, Iterable<?> sequence, Boolean flowStyle) {
+        int size = 10;// default for ArrayList
+        if (sequence instanceof List<?>) {
+            size = ((List<?>) sequence).size();
+        }
+        List<Node> value = new ArrayList<Node>(size);
+        SequenceNode node = new SequenceNode(tag, value, flowStyle);
+        representedObjects.put(objectToRepresent, node);
+        boolean bestStyle = true;
+        for (Object item : sequence) {
+            Node nodeItem = representData(item);
+            if (!(nodeItem instanceof ScalarNode && ((ScalarNode) nodeItem).getStyle() == null)) {
+                bestStyle = false;
+            }
+            value.add(nodeItem);
+        }
+        if (flowStyle == null) {
+            if (defaultFlowStyle != FlowStyle.AUTO) {
+                node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
+            } else {
+                node.setFlowStyle(bestStyle);
+            }
+        }
+        return node;
+    }
+
+    protected Node representMapping(Tag tag, Map<?, ?> mapping, Boolean flowStyle) {
+        List<NodeTuple> value = new ArrayList<NodeTuple>(mapping.size());
+        MappingNode node = new MappingNode(tag, value, flowStyle);
+        representedObjects.put(objectToRepresent, node);
+        boolean bestStyle = true;
+        for (Map.Entry<?, ?> entry : mapping.entrySet()) {
+            Node nodeKey = representData(entry.getKey());
+            Node nodeValue = representData(entry.getValue());
+            if (!(nodeKey instanceof ScalarNode && ((ScalarNode) nodeKey).getStyle() == null)) {
+                bestStyle = false;
+            }
+            if (!(nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).getStyle() == null)) {
+                bestStyle = false;
+            }
+            value.add(new NodeTuple(nodeKey, nodeValue));
+        }
+        if (flowStyle == null) {
+            if (defaultFlowStyle != FlowStyle.AUTO) {
+                node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
+            } else {
+                node.setFlowStyle(bestStyle);
+            }
+        }
+        return node;
+    }
+
+    public void setDefaultScalarStyle(ScalarStyle defaultStyle) {
+        this.defaultScalarStyle = defaultStyle.getChar();
+    }
+
+    public void setDefaultFlowStyle(FlowStyle defaultFlowStyle) {
+        this.defaultFlowStyle = defaultFlowStyle;
+    }
+
+    public FlowStyle getDefaultFlowStyle() {
+        return this.defaultFlowStyle;
+    }
+
+    public void setPropertyUtils(PropertyUtils propertyUtils) {
+        this.propertyUtils = propertyUtils;
+        this.explicitPropertyUtils = true;
+    }
+
+    public final PropertyUtils getPropertyUtils() {
+        if (propertyUtils == null) {
+            propertyUtils = new PropertyUtils();
+        }
+        return propertyUtils;
+    }
+
+    public final boolean isExplicitPropertyUtils() {
+        return explicitPropertyUtils;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Represent.java b/src/main/java/org/yaml/snakeyaml/representer/Represent.java
new file mode 100644
index 0000000..55629ed
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/representer/Represent.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import org.yaml.snakeyaml.nodes.Node;
+
+/**
+ * Create a Node Graph out of the provided Native Data Structure (Java
+ * instance).
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/#id859109">Chapter 3. Processing YAML
+ *      Information</a>
+ */
+public interface Represent {
+    /**
+     * Create a Node
+     * 
+     * @param data
+     *            the instance to represent
+     * @return Node to dump
+     */
+    Node representData(Object data);
+}
diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
new file mode 100644
index 0000000..e83a5ea
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
@@ -0,0 +1,248 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.beans.IntrospectionException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Represent JavaBeans
+ */
+public class Representer extends SafeRepresenter {
+
+    public Representer() {
+        this.representers.put(null, new RepresentJavaBean());
+    }
+
+    protected class RepresentJavaBean implements Represent {
+        public Node representData(Object data) {
+            try {
+                return representJavaBean(getProperties(data.getClass()), data);
+            } catch (IntrospectionException e) {
+                throw new YAMLException(e);
+            }
+        }
+    }
+
+    /**
+     * Tag logic:<br/>
+     * - explicit root tag is set in serializer <br/>
+     * - if there is a predefined class tag it is used<br/>
+     * - a global tag with class name is always used as tag. The JavaBean parent
+     * of the specified JavaBean may set another tag (tag:yaml.org,2002:map)
+     * when the property class is the same as runtime class
+     * 
+     * @param properties
+     *            JavaBean getters
+     * @param javaBean
+     *            instance for Node
+     * @return Node to get serialized
+     */
+    protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
+        List<NodeTuple> value = new ArrayList<NodeTuple>(properties.size());
+        Tag tag;
+        Tag customTag = classTags.get(javaBean.getClass());
+        tag = customTag != null ? customTag : new Tag(javaBean.getClass());
+        // flow style will be chosen by BaseRepresenter
+        MappingNode node = new MappingNode(tag, value, null);
+        representedObjects.put(javaBean, node);
+        boolean bestStyle = true;
+        for (Property property : properties) {
+            Object memberValue = property.get(javaBean);
+            Tag customPropertyTag = memberValue == null ? null : classTags.get(memberValue
+                    .getClass());
+            NodeTuple tuple = representJavaBeanProperty(javaBean, property, memberValue,
+                    customPropertyTag);
+            if (tuple == null) {
+                continue;
+            }
+            if (((ScalarNode) tuple.getKeyNode()).getStyle() != null) {
+                bestStyle = false;
+            }
+            Node nodeValue = tuple.getValueNode();
+            if (!(nodeValue instanceof ScalarNode && ((ScalarNode) nodeValue).getStyle() == null)) {
+                bestStyle = false;
+            }
+            value.add(tuple);
+        }
+        if (defaultFlowStyle != FlowStyle.AUTO) {
+            node.setFlowStyle(defaultFlowStyle.getStyleBoolean());
+        } else {
+            node.setFlowStyle(bestStyle);
+        }
+        return node;
+    }
+
+    /**
+     * Represent one JavaBean property.
+     * 
+     * @param javaBean
+     *            - the instance to be represented
+     * @param property
+     *            - the property of the instance
+     * @param propertyValue
+     *            - value to be represented
+     * @param customTag
+     *            - user defined Tag
+     * @return NodeTuple to be used in a MappingNode. Return null to skip the
+     *         property
+     */
+    protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+            Object propertyValue, Tag customTag) {
+        ScalarNode nodeKey = (ScalarNode) representData(property.getName());
+        // the first occurrence of the node must keep the tag
+        boolean hasAlias = this.representedObjects.containsKey(propertyValue);
+
+        Node nodeValue = representData(propertyValue);
+
+        if (propertyValue != null && !hasAlias) {
+            NodeId nodeId = nodeValue.getNodeId();
+            if (customTag == null) {
+                if (nodeId == NodeId.scalar) {
+                    if (propertyValue instanceof Enum<?>) {
+                        nodeValue.setTag(Tag.STR);
+                    }
+                } else {
+                    if (nodeId == NodeId.mapping) {
+                        if (property.getType() == propertyValue.getClass()) {
+                            if (!(propertyValue instanceof Map<?, ?>)) {
+                                if (!nodeValue.getTag().equals(Tag.SET)) {
+                                    nodeValue.setTag(Tag.MAP);
+                                }
+                            }
+                        }
+                    }
+                    checkGlobalTag(property, nodeValue, propertyValue);
+                }
+            }
+        }
+
+        return new NodeTuple(nodeKey, nodeValue);
+    }
+
+    /**
+     * Remove redundant global tag for a type safe (generic) collection if it is
+     * the same as defined by the JavaBean property
+     * 
+     * @param property
+     *            - JavaBean property
+     * @param node
+     *            - representation of the property
+     * @param object
+     *            - instance represented by the node
+     */
+    @SuppressWarnings("unchecked")
+    protected void checkGlobalTag(Property property, Node node, Object object) {
+        // Skip primitive arrays.
+        if (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {
+            return;
+        }
+
+        Class<?>[] arguments = property.getActualTypeArguments();
+        if (arguments != null) {
+            if (node.getNodeId() == NodeId.sequence) {
+                // apply map tag where class is the same
+                Class<? extends Object> t = arguments[0];
+                SequenceNode snode = (SequenceNode) node;
+                Iterable<Object> memberList = Collections.EMPTY_LIST;
+                if (object.getClass().isArray()) {
+                    memberList = Arrays.asList((Object[]) object);
+                } else if (object instanceof Iterable<?>) {
+                    // list
+                    memberList = (Iterable<Object>) object;
+                }
+                Iterator<Object> iter = memberList.iterator();
+                if (iter.hasNext()) {
+                    for (Node childNode : snode.getValue()) {
+                        Object member = iter.next();
+                        if (member != null) {
+                            if (t.equals(member.getClass()))
+                                if (childNode.getNodeId() == NodeId.mapping) {
+                                    childNode.setTag(Tag.MAP);
+                                }
+                        }
+                    }
+                }
+            } else if (object instanceof Set) {
+                Class<?> t = arguments[0];
+                MappingNode mnode = (MappingNode) node;
+                Iterator<NodeTuple> iter = mnode.getValue().iterator();
+                Set<?> set = (Set<?>) object;
+                for (Object member : set) {
+                    NodeTuple tuple = iter.next();
+                    Node keyNode = tuple.getKeyNode();
+                    if (t.equals(member.getClass())) {
+                        if (keyNode.getNodeId() == NodeId.mapping) {
+                            keyNode.setTag(Tag.MAP);
+                        }
+                    }
+                }
+            } else if (object instanceof Map) {
+                Class<?> keyType = arguments[0];
+                Class<?> valueType = arguments[1];
+                MappingNode mnode = (MappingNode) node;
+                for (NodeTuple tuple : mnode.getValue()) {
+                    resetTag(keyType, tuple.getKeyNode());
+                    resetTag(valueType, tuple.getValueNode());
+                }
+            } else {
+                // the type for collection entries cannot be
+                // detected
+            }
+        }
+    }
+
+    private void resetTag(Class<? extends Object> type, Node node) {
+        Tag tag = node.getTag();
+        if (tag.matches(type)) {
+            if (Enum.class.isAssignableFrom(type)) {
+                node.setTag(Tag.STR);
+            } else {
+                node.setTag(Tag.MAP);
+            }
+        }
+    }
+
+    /**
+     * Get JavaBean properties to be serialised. The order is respected. This
+     * method may be overridden to provide custom property selection or order.
+     * 
+     * @param type
+     *            - JavaBean to inspect the properties
+     * @return properties to serialise
+     */
+    protected Set<Property> getProperties(Class<? extends Object> type)
+            throws IntrospectionException {
+        return getPropertyUtils().getProperties(type);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java b/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java
new file mode 100644
index 0000000..147e3af
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java
@@ -0,0 +1,426 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.reader.StreamReader;
+
+/**
+ * Represent standard Java classes
+ */
+class SafeRepresenter extends BaseRepresenter {
+
+    protected Map<Class<? extends Object>, Tag> classTags;
+    protected TimeZone timeZone = null;
+
+    public SafeRepresenter() {
+        this.nullRepresenter = new RepresentNull();
+        this.representers.put(String.class, new RepresentString());
+        this.representers.put(Boolean.class, new RepresentBoolean());
+        this.representers.put(Character.class, new RepresentString());
+        this.representers.put(UUID.class, new RepresentUuid());
+        this.representers.put(byte[].class, new RepresentByteArray());
+
+        Represent primitiveArray = new RepresentPrimitiveArray();
+        representers.put(short[].class, primitiveArray);
+        representers.put(int[].class, primitiveArray);
+        representers.put(long[].class, primitiveArray);
+        representers.put(float[].class, primitiveArray);
+        representers.put(double[].class, primitiveArray);
+        representers.put(char[].class, primitiveArray);
+        representers.put(boolean[].class, primitiveArray);
+
+        this.multiRepresenters.put(Number.class, new RepresentNumber());
+        this.multiRepresenters.put(List.class, new RepresentList());
+        this.multiRepresenters.put(Map.class, new RepresentMap());
+        this.multiRepresenters.put(Set.class, new RepresentSet());
+        this.multiRepresenters.put(Iterator.class, new RepresentIterator());
+        this.multiRepresenters.put(new Object[0].getClass(), new RepresentArray());
+        this.multiRepresenters.put(Date.class, new RepresentDate());
+        this.multiRepresenters.put(Enum.class, new RepresentEnum());
+        this.multiRepresenters.put(Calendar.class, new RepresentDate());
+        classTags = new HashMap<Class<? extends Object>, Tag>();
+    }
+
+    protected Tag getTag(Class<?> clazz, Tag defaultTag) {
+        if (classTags.containsKey(clazz)) {
+            return classTags.get(clazz);
+        } else {
+            return defaultTag;
+        }
+    }
+
+    /**
+     * Define a tag for the <code>Class</code> to serialize.
+     * 
+     * @param clazz
+     *            <code>Class</code> which tag is changed
+     * @param tag
+     *            new tag to be used for every instance of the specified
+     *            <code>Class</code>
+     * @return the previous tag associated with the <code>Class</code>
+     */
+    public Tag addClassTag(Class<? extends Object> clazz, Tag tag) {
+        if (tag == null) {
+            throw new NullPointerException("Tag must be provided.");
+        }
+        return classTags.put(clazz, tag);
+    }
+
+    protected class RepresentNull implements Represent {
+        public Node representData(Object data) {
+            return representScalar(Tag.NULL, "null");
+        }
+    }
+
+    public static Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");
+
+    protected class RepresentString implements Represent {
+        public Node representData(Object data) {
+            Tag tag = Tag.STR;
+            Character style = null;
+            String value = data.toString();
+            if (StreamReader.NON_PRINTABLE.matcher(value).find()) {
+                tag = Tag.BINARY;
+                char[] binary;
+                try {
+                    binary = Base64Coder.encode(value.getBytes("UTF-8"));
+                } catch (UnsupportedEncodingException e) {
+                    throw new YAMLException(e);
+                }
+                value = String.valueOf(binary);
+                style = '|';
+            }
+            // if no other scalar style is explicitly set, use literal style for
+            // multiline scalars
+            if (defaultScalarStyle == null && MULTILINE_PATTERN.matcher(value).find()) {
+                style = '|';
+            }
+            return representScalar(tag, value, style);
+        }
+    }
+
+    protected class RepresentBoolean implements Represent {
+        public Node representData(Object data) {
+            String value;
+            if (Boolean.TRUE.equals(data)) {
+                value = "true";
+            } else {
+                value = "false";
+            }
+            return representScalar(Tag.BOOL, value);
+        }
+    }
+
+    protected class RepresentNumber implements Represent {
+        public Node representData(Object data) {
+            Tag tag;
+            String value;
+            if (data instanceof Byte || data instanceof Short || data instanceof Integer
+                    || data instanceof Long || data instanceof BigInteger) {
+                tag = Tag.INT;
+                value = data.toString();
+            } else {
+                Number number = (Number) data;
+                tag = Tag.FLOAT;
+                if (number.equals(Double.NaN)) {
+                    value = ".NaN";
+                } else if (number.equals(Double.POSITIVE_INFINITY)) {
+                    value = ".inf";
+                } else if (number.equals(Double.NEGATIVE_INFINITY)) {
+                    value = "-.inf";
+                } else {
+                    value = number.toString();
+                }
+            }
+            return representScalar(getTag(data.getClass(), tag), value);
+        }
+    }
+
+    protected class RepresentList implements Represent {
+        @SuppressWarnings("unchecked")
+        public Node representData(Object data) {
+            return representSequence(getTag(data.getClass(), Tag.SEQ), (List<Object>) data, null);
+        }
+    }
+
+    protected class RepresentIterator implements Represent {
+        @SuppressWarnings("unchecked")
+        public Node representData(Object data) {
+            Iterator<Object> iter = (Iterator<Object>) data;
+            return representSequence(getTag(data.getClass(), Tag.SEQ), new IteratorWrapper(iter),
+                    null);
+        }
+    }
+
+    private static class IteratorWrapper implements Iterable<Object> {
+        private Iterator<Object> iter;
+
+        public IteratorWrapper(Iterator<Object> iter) {
+            this.iter = iter;
+        }
+
+        public Iterator<Object> iterator() {
+            return iter;
+        }
+    }
+
+    protected class RepresentArray implements Represent {
+        public Node representData(Object data) {
+            Object[] array = (Object[]) data;
+            List<Object> list = Arrays.asList(array);
+            return representSequence(Tag.SEQ, list, null);
+        }
+    }
+
+    /**
+     * Represents primitive arrays, such as short[] and float[], by converting
+     * them into equivalent List<Short> and List<Float> using the appropriate
+     * autoboxing type.
+     */
+    protected class RepresentPrimitiveArray implements Represent {
+        public Node representData(Object data) {
+            Class<?> type = data.getClass().getComponentType();
+
+            if (byte.class == type) {
+                return representSequence(Tag.SEQ, asByteList(data), null);
+            } else if (short.class == type) {
+                return representSequence(Tag.SEQ, asShortList(data), null);
+            } else if (int.class == type) {
+                return representSequence(Tag.SEQ, asIntList(data), null);
+            } else if (long.class == type) {
+                return representSequence(Tag.SEQ, asLongList(data), null);
+            } else if (float.class == type) {
+                return representSequence(Tag.SEQ, asFloatList(data), null);
+            } else if (double.class == type) {
+                return representSequence(Tag.SEQ, asDoubleList(data), null);
+            } else if (char.class == type) {
+                return representSequence(Tag.SEQ, asCharList(data), null);
+            } else if (boolean.class == type) {
+                return representSequence(Tag.SEQ, asBooleanList(data), null);
+            }
+
+            throw new YAMLException("Unexpected primitive '" + type.getCanonicalName() + "'");
+        }
+
+        private List<Byte> asByteList(Object in) {
+            byte[] array = (byte[]) in;
+            List<Byte> list = new ArrayList<Byte>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Short> asShortList(Object in) {
+            short[] array = (short[]) in;
+            List<Short> list = new ArrayList<Short>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Integer> asIntList(Object in) {
+            int[] array = (int[]) in;
+            List<Integer> list = new ArrayList<Integer>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Long> asLongList(Object in) {
+            long[] array = (long[]) in;
+            List<Long> list = new ArrayList<Long>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Float> asFloatList(Object in) {
+            float[] array = (float[]) in;
+            List<Float> list = new ArrayList<Float>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Double> asDoubleList(Object in) {
+            double[] array = (double[]) in;
+            List<Double> list = new ArrayList<Double>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Character> asCharList(Object in) {
+            char[] array = (char[]) in;
+            List<Character> list = new ArrayList<Character>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+
+        private List<Boolean> asBooleanList(Object in) {
+            boolean[] array = (boolean[]) in;
+            List<Boolean> list = new ArrayList<Boolean>(array.length);
+            for (int i = 0; i < array.length; ++i)
+                list.add(array[i]);
+            return list;
+        }
+    }
+
+    protected class RepresentMap implements Represent {
+        @SuppressWarnings("unchecked")
+        public Node representData(Object data) {
+            return representMapping(getTag(data.getClass(), Tag.MAP), (Map<Object, Object>) data,
+                    null);
+        }
+    }
+
+    protected class RepresentSet implements Represent {
+        @SuppressWarnings("unchecked")
+        public Node representData(Object data) {
+            Map<Object, Object> value = new LinkedHashMap<Object, Object>();
+            Set<Object> set = (Set<Object>) data;
+            for (Object key : set) {
+                value.put(key, null);
+            }
+            return representMapping(getTag(data.getClass(), Tag.SET), value, null);
+        }
+    }
+
+    protected class RepresentDate implements Represent {
+        public Node representData(Object data) {
+            // because SimpleDateFormat ignores timezone we have to use Calendar
+            Calendar calendar;
+            if (data instanceof Calendar) {
+                calendar = (Calendar) data;
+            } else {
+                calendar = Calendar.getInstance(getTimeZone() == null ? TimeZone.getTimeZone("UTC")
+                        : timeZone);
+                calendar.setTime((Date) data);
+            }
+            int years = calendar.get(Calendar.YEAR);
+            int months = calendar.get(Calendar.MONTH) + 1; // 0..12
+            int days = calendar.get(Calendar.DAY_OF_MONTH); // 1..31
+            int hour24 = calendar.get(Calendar.HOUR_OF_DAY); // 0..24
+            int minutes = calendar.get(Calendar.MINUTE); // 0..59
+            int seconds = calendar.get(Calendar.SECOND); // 0..59
+            int millis = calendar.get(Calendar.MILLISECOND);
+            StringBuilder buffer = new StringBuilder(String.valueOf(years));
+            while (buffer.length() < 4) {
+                // ancient years
+                buffer.insert(0, "0");
+            }
+            buffer.append("-");
+            if (months < 10) {
+                buffer.append("0");
+            }
+            buffer.append(String.valueOf(months));
+            buffer.append("-");
+            if (days < 10) {
+                buffer.append("0");
+            }
+            buffer.append(String.valueOf(days));
+            buffer.append("T");
+            if (hour24 < 10) {
+                buffer.append("0");
+            }
+            buffer.append(String.valueOf(hour24));
+            buffer.append(":");
+            if (minutes < 10) {
+                buffer.append("0");
+            }
+            buffer.append(String.valueOf(minutes));
+            buffer.append(":");
+            if (seconds < 10) {
+                buffer.append("0");
+            }
+            buffer.append(String.valueOf(seconds));
+            if (millis > 0) {
+                if (millis < 10) {
+                    buffer.append(".00");
+                } else if (millis < 100) {
+                    buffer.append(".0");
+                } else {
+                    buffer.append(".");
+                }
+                buffer.append(String.valueOf(millis));
+            }
+            if (TimeZone.getTimeZone("UTC").equals(calendar.getTimeZone())) {
+                buffer.append("Z");
+            } else {
+                // Get the Offset from GMT taking DST into account
+                int gmtOffset = calendar.getTimeZone().getOffset(calendar.get(Calendar.ERA),
+                        calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
+                        calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.DAY_OF_WEEK),
+                        calendar.get(Calendar.MILLISECOND));
+                int minutesOffset = gmtOffset / (60 * 1000);
+                int hoursOffset = minutesOffset / 60;
+                int partOfHour = minutesOffset % 60;
+                buffer.append((hoursOffset > 0 ? "+" : "") + hoursOffset + ":"
+                        + (partOfHour < 10 ? "0" + partOfHour : partOfHour));
+            }
+            return representScalar(getTag(data.getClass(), Tag.TIMESTAMP), buffer.toString(), null);
+        }
+    }
+
+    protected class RepresentEnum implements Represent {
+        public Node representData(Object data) {
+            Tag tag = new Tag(data.getClass());
+            return representScalar(getTag(data.getClass(), tag), ((Enum<?>) data).name());
+        }
+    }
+
+    protected class RepresentByteArray implements Represent {
+        public Node representData(Object data) {
+            char[] binary = Base64Coder.encode((byte[]) data);
+            return representScalar(Tag.BINARY, String.valueOf(binary), '|');
+        }
+    }
+
+    public TimeZone getTimeZone() {
+        return timeZone;
+    }
+
+    public void setTimeZone(TimeZone timeZone) {
+        this.timeZone = timeZone;
+    }
+
+    protected class RepresentUuid implements Represent {
+        public Node representData(Object data) {
+            return representScalar(getTag(data.getClass(), new Tag(UUID.class)), data.toString());
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java b/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java
new file mode 100644
index 0000000..f8ec36f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/resolver/Resolver.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.resolver;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Resolver tries to detect a type by content (when the tag is implicit)
+ */
+public class Resolver {
+    public static final Pattern BOOL = Pattern
+            .compile("^(?:yes|Yes|YES|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)$");
+
+    /**
+     * The regular expression is taken from the 1.2 specification but '_'s are
+     * added to keep backwards compatibility
+     */
+    public static final Pattern FLOAT = Pattern
+            .compile("^([-+]?(\\.[0-9]+|[0-9_]+(\\.[0-9_]*)?)([eE][-+]?[0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");
+    public static final Pattern INT = Pattern
+            .compile("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$");
+    public static final Pattern MERGE = Pattern.compile("^(?:<<)$");
+    public static final Pattern NULL = Pattern.compile("^(?:~|null|Null|NULL| )$");
+    public static final Pattern EMPTY = Pattern.compile("^$");
+    public static final Pattern TIMESTAMP = Pattern
+            .compile("^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?(?:[Tt]|[ \t]+)[0-9][0-9]?:[0-9][0-9]:[0-9][0-9](?:\\.[0-9]*)?(?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$");
+    public static final Pattern VALUE = Pattern.compile("^(?:=)$");
+    public static final Pattern YAML = Pattern.compile("^(?:!|&|\\*)$");
+
+    protected Map<Character, List<ResolverTuple>> yamlImplicitResolvers = new HashMap<Character, List<ResolverTuple>>();
+
+    protected void addImplicitResolvers() {
+        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
+        /*
+         * INT must be before FLOAT because the regular expression for FLOAT
+         * matches INT (see issue 130)
+         * http://code.google.com/p/snakeyaml/issues/detail?id=130
+         */
+        addImplicitResolver(Tag.INT, INT, "-+0123456789");
+        addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789.");
+        addImplicitResolver(Tag.MERGE, MERGE, "<");
+        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
+        addImplicitResolver(Tag.NULL, EMPTY, null);
+        addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
+        // The following implicit resolver is only for documentation
+        // purposes.
+        // It cannot work
+        // because plain scalars cannot start with '!', '&', or '*'.
+        addImplicitResolver(Tag.YAML, YAML, "!&*");
+    }
+
+    public Resolver() {
+        addImplicitResolvers();
+    }
+
+    public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
+        if (first == null) {
+            List<ResolverTuple> curr = yamlImplicitResolvers.get(null);
+            if (curr == null) {
+                curr = new ArrayList<ResolverTuple>();
+                yamlImplicitResolvers.put(null, curr);
+            }
+            curr.add(new ResolverTuple(tag, regexp));
+        } else {
+            char[] chrs = first.toCharArray();
+            for (int i = 0, j = chrs.length; i < j; i++) {
+                Character theC = Character.valueOf(chrs[i]);
+                if (theC == 0) {
+                    // special case: for null
+                    theC = null;
+                }
+                List<ResolverTuple> curr = yamlImplicitResolvers.get(theC);
+                if (curr == null) {
+                    curr = new ArrayList<ResolverTuple>();
+                    yamlImplicitResolvers.put(theC, curr);
+                }
+                curr.add(new ResolverTuple(tag, regexp));
+            }
+        }
+    }
+
+    public Tag resolve(NodeId kind, String value, boolean implicit) {
+        if (kind == NodeId.scalar && implicit) {
+            List<ResolverTuple> resolvers = null;
+            if (value.length() == 0) {
+                resolvers = yamlImplicitResolvers.get('\0');
+            } else {
+                resolvers = yamlImplicitResolvers.get(value.charAt(0));
+            }
+            if (resolvers != null) {
+                for (ResolverTuple v : resolvers) {
+                    Tag tag = v.getTag();
+                    Pattern regexp = v.getRegexp();
+                    if (regexp.matcher(value).matches()) {
+                        return tag;
+                    }
+                }
+            }
+            if (yamlImplicitResolvers.containsKey(null)) {
+                for (ResolverTuple v : yamlImplicitResolvers.get(null)) {
+                    Tag tag = v.getTag();
+                    Pattern regexp = v.getRegexp();
+                    if (regexp.matcher(value).matches()) {
+                        return tag;
+                    }
+                }
+            }
+        }
+        switch (kind) {
+        case scalar:
+            return Tag.STR;
+        case sequence:
+            return Tag.SEQ;
+        default:
+            return Tag.MAP;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/resolver/ResolverTuple.java b/src/main/java/org/yaml/snakeyaml/resolver/ResolverTuple.java
new file mode 100644
index 0000000..3fbfac0
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/resolver/ResolverTuple.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.resolver;
+
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.nodes.Tag;
+
+final class ResolverTuple {
+    private final Tag tag;
+    private final Pattern regexp;
+
+    public ResolverTuple(Tag tag, Pattern regexp) {
+        this.tag = tag;
+        this.regexp = regexp;
+    }
+
+    public Tag getTag() {
+        return tag;
+    }
+
+    public Pattern getRegexp() {
+        return regexp;
+    }
+
+    @Override
+    public String toString() {
+        return "Tuple tag=" + tag + " regexp=" + regexp;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/Constant.java b/src/main/java/org/yaml/snakeyaml/scanner/Constant.java
new file mode 100644
index 0000000..391bcaa
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/scanner/Constant.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import java.util.Arrays;
+
+public final class Constant {
+    private final static String ALPHA_S = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
+
+    private final static String LINEBR_S = "\n\u0085\u2028\u2029";
+    private final static String FULL_LINEBR_S = "\r" + LINEBR_S;
+    private final static String NULL_OR_LINEBR_S = "\0" + FULL_LINEBR_S;
+    private final static String NULL_BL_LINEBR_S = " " + NULL_OR_LINEBR_S;
+    private final static String NULL_BL_T_LINEBR_S = "\t" + NULL_BL_LINEBR_S;
+    private final static String NULL_BL_T_S = "\0 \t";
+    private final static String URI_CHARS_S = ALPHA_S + "-;/?:@&=+$,_.!~*\'()[]%";
+
+    public final static Constant LINEBR = new Constant(LINEBR_S);
+    public final static Constant FULL_LINEBR = new Constant(FULL_LINEBR_S);
+    public final static Constant NULL_OR_LINEBR = new Constant(NULL_OR_LINEBR_S);
+    public final static Constant NULL_BL_LINEBR = new Constant(NULL_BL_LINEBR_S);
+    public final static Constant NULL_BL_T_LINEBR = new Constant(NULL_BL_T_LINEBR_S);
+    public final static Constant NULL_BL_T = new Constant(NULL_BL_T_S);
+    public final static Constant URI_CHARS = new Constant(URI_CHARS_S);
+
+    public final static Constant ALPHA = new Constant(ALPHA_S);
+
+    private String content;
+    boolean[] contains = new boolean[128];
+    boolean noASCII = false;
+
+    private Constant(String content) {
+        Arrays.fill(contains, false);
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < content.length(); i++) {
+            char ch = content.charAt(i);
+            if (ch < 128)
+                contains[ch] = true;
+            else
+                sb.append(ch);
+        }
+        if (sb.length() > 0) {
+            noASCII = true;
+            this.content = sb.toString();
+        }
+    }
+
+    public boolean has(char ch) {
+        return (ch < 128) ? contains[ch] : noASCII && content.indexOf(ch, 0) != -1;
+    }
+
+    public boolean hasNo(char ch) {
+        return !has(ch);
+    }
+
+    public boolean has(char ch, String additional) {
+        return has(ch) || additional.indexOf(ch, 0) != -1;
+    }
+
+    public boolean hasNo(char ch, String additional) {
+        return !has(ch, additional);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/Scanner.java b/src/main/java/org/yaml/snakeyaml/scanner/Scanner.java
new file mode 100644
index 0000000..6fc0d97
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/scanner/Scanner.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import org.yaml.snakeyaml.tokens.Token;
+
+/**
+ * This interface represents an input stream of {@link Token Tokens}.
+ * <p>
+ * The parser and the scanner form together the 'Parse' step in the loading
+ * process (see chapter 3.1 of the <a href="http://yaml.org/spec/1.1/">YAML
+ * Specification</a>).
+ * </p>
+ * 
+ * @see org.yaml.snakeyaml.tokens.Token
+ */
+public interface Scanner {
+
+    /**
+     * Check if the next token is one of the given types.
+     * 
+     * @param choices
+     *            token IDs.
+     * @return <code>true</code> if the next token can be assigned to a variable
+     *         of at least one of the given types. Returns <code>false</code> if
+     *         no more tokens are available.
+     * @throws ScannerException
+     *             Thrown in case of malformed input.
+     */
+    boolean checkToken(Token.ID... choices);
+
+    /**
+     * Return the next token, but do not delete it from the stream.
+     * 
+     * @return The token that will be returned on the next call to
+     *         {@link #getToken}
+     * @throws ScannerException
+     *             Thrown in case of malformed input.
+     */
+    Token peekToken();
+
+    /**
+     * Returns the next token.
+     * <p>
+     * The token will be removed from the stream.
+     * </p>
+     * 
+     * @throws ScannerException
+     *             Thrown in case of malformed input.
+     */
+    Token getToken();
+}
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/ScannerException.java b/src/main/java/org/yaml/snakeyaml/scanner/ScannerException.java
new file mode 100644
index 0000000..b4ee9ee
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/scanner/ScannerException.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.MarkedYAMLException;
+
+/**
+ * Exception thrown by the {@link Scanner} implementations in case of malformed
+ * input.
+ */
+public class ScannerException extends MarkedYAMLException {
+
+    private static final long serialVersionUID = 4782293188600445954L;
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param context
+     *            Part of the input document in which vicinity the problem
+     *            occurred.
+     * @param contextMark
+     *            Position of the <code>context</code> within the document.
+     * @param problem
+     *            Part of the input document that caused the problem.
+     * @param problemMark
+     *            Position of the <code>problem</code> within the document.
+     * @param note
+     *            Message for the user with further information about the
+     *            problem.
+     */
+    public ScannerException(String context, Mark contextMark, String problem, Mark problemMark,
+            String note) {
+        super(context, contextMark, problem, problemMark, note);
+    }
+
+    /**
+     * Constructs an instance.
+     * 
+     * @param context
+     *            Part of the input document in which vicinity the problem
+     *            occurred.
+     * @param contextMark
+     *            Position of the <code>context</code> within the document.
+     * @param problem
+     *            Part of the input document that caused the problem.
+     * @param problemMark
+     *            Position of the <code>problem</code> within the document.
+     */
+    public ScannerException(String context, Mark contextMark, String problem, Mark problemMark) {
+        this(context, contextMark, problem, problemMark, null);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java b/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java
new file mode 100644
index 0000000..4272aab
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/scanner/ScannerImpl.java
@@ -0,0 +1,2288 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.tokens.AliasToken;
+import org.yaml.snakeyaml.tokens.AnchorToken;
+import org.yaml.snakeyaml.tokens.BlockEndToken;
+import org.yaml.snakeyaml.tokens.BlockEntryToken;
+import org.yaml.snakeyaml.tokens.BlockMappingStartToken;
+import org.yaml.snakeyaml.tokens.BlockSequenceStartToken;
+import org.yaml.snakeyaml.tokens.DirectiveToken;
+import org.yaml.snakeyaml.tokens.DocumentEndToken;
+import org.yaml.snakeyaml.tokens.DocumentStartToken;
+import org.yaml.snakeyaml.tokens.FlowEntryToken;
+import org.yaml.snakeyaml.tokens.FlowMappingEndToken;
+import org.yaml.snakeyaml.tokens.FlowMappingStartToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceEndToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceStartToken;
+import org.yaml.snakeyaml.tokens.KeyToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.StreamEndToken;
+import org.yaml.snakeyaml.tokens.StreamStartToken;
+import org.yaml.snakeyaml.tokens.TagToken;
+import org.yaml.snakeyaml.tokens.TagTuple;
+import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.tokens.ValueToken;
+import org.yaml.snakeyaml.util.ArrayStack;
+import org.yaml.snakeyaml.util.UriEncoder;
+
+/**
+ * <pre>
+ * Scanner produces tokens of the following types:
+ * STREAM-START
+ * STREAM-END
+ * DIRECTIVE(name, value)
+ * DOCUMENT-START
+ * DOCUMENT-END
+ * BLOCK-SEQUENCE-START
+ * BLOCK-MAPPING-START
+ * BLOCK-END
+ * FLOW-SEQUENCE-START
+ * FLOW-MAPPING-START
+ * FLOW-SEQUENCE-END
+ * FLOW-MAPPING-END
+ * BLOCK-ENTRY
+ * FLOW-ENTRY
+ * KEY
+ * VALUE
+ * ALIAS(value)
+ * ANCHOR(value)
+ * TAG(value)
+ * SCALAR(value, plain, style)
+ * Read comments in the Scanner code for more details.
+ * </pre>
+ */
+public final class ScannerImpl implements Scanner {
+    /**
+     * A regular expression matching characters which are not in the hexadecimal
+     * set (0-9, A-F, a-f).
+     */
+    private final static Pattern NOT_HEXA = Pattern.compile("[^0-9A-Fa-f]");
+
+    /**
+     * A mapping from an escaped character in the input stream to the character
+     * that they should be replaced with.
+     * 
+     * YAML defines several common and a few uncommon escape sequences.
+     * 
+     * @see <a href="http://www.yaml.org/spec/current.html#id2517668">4.1.6.
+     *      Escape Sequences</a>
+     */
+    public final static Map<Character, String> ESCAPE_REPLACEMENTS = new HashMap<Character, String>();
+
+    /**
+     * A mapping from a character to a number of bytes to read-ahead for that
+     * escape sequence. These escape sequences are used to handle unicode
+     * escaping in the following formats, where H is a hexadecimal character:
+     * 
+     * <pre>
+     * &#92;xHH         : escaped 8-bit Unicode character
+     * &#92;uHHHH       : escaped 16-bit Unicode character
+     * &#92;UHHHHHHHH   : escaped 32-bit Unicode character
+     * </pre>
+     * 
+     * @see <a href="http://yaml.org/spec/1.1/current.html#id872840">5.6. Escape
+     *      Sequences</a>
+     */
+    public final static Map<Character, Integer> ESCAPE_CODES = new HashMap<Character, Integer>();
+
+    static {
+        // ASCII null
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('0'), "\0");
+        // ASCII bell
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('a'), "\u0007");
+        // ASCII backspace
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('b'), "\u0008");
+        // ASCII horizontal tab
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('t'), "\u0009");
+        // ASCII newline (line feed; &#92;n maps to 0x0A)
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('n'), "\n");
+        // ASCII vertical tab
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('v'), "\u000B");
+        // ASCII form-feed
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('f'), "\u000C");
+        // carriage-return (&#92;r maps to 0x0D)
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('r'), "\r");
+        // ASCII escape character (Esc)
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('e'), "\u001B");
+        // ASCII space
+        ESCAPE_REPLACEMENTS.put(Character.valueOf(' '), "\u0020");
+        // ASCII double-quote
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('"'), "\"");
+        // ASCII backslash
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('\\'), "\\");
+        // Unicode next line
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('N'), "\u0085");
+        // Unicode non-breaking-space
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('_'), "\u00A0");
+        // Unicode line-separator
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('L'), "\u2028");
+        // Unicode paragraph separator
+        ESCAPE_REPLACEMENTS.put(Character.valueOf('P'), "\u2029");
+
+        // 8-bit Unicode
+        ESCAPE_CODES.put(Character.valueOf('x'), 2);
+        // 16-bit Unicode
+        ESCAPE_CODES.put(Character.valueOf('u'), 4);
+        // 32-bit Unicode (Supplementary characters are supported)
+        ESCAPE_CODES.put(Character.valueOf('U'), 8);
+    }
+    private final StreamReader reader;
+    // Had we reached the end of the stream?
+    private boolean done = false;
+
+    // The number of unclosed '{' and '['. `flow_level == 0` means block
+    // context.
+    private int flowLevel = 0;
+
+    // List of processed tokens that are not yet emitted.
+    private List<Token> tokens;
+
+    // Number of tokens that were emitted through the `get_token` method.
+    private int tokensTaken = 0;
+
+    // The current indentation level.
+    private int indent = -1;
+
+    // Past indentation levels.
+    private ArrayStack<Integer> indents;
+
+    // Variables related to simple keys treatment. See PyYAML.
+
+    /**
+     * <pre>
+     * A simple key is a key that is not denoted by the '?' indicator.
+     * Example of simple keys:
+     *   ---
+     *   block simple key: value
+     *   ? not a simple key:
+     *   : { flow simple key: value }
+     * We emit the KEY token before all keys, so when we find a potential
+     * simple key, we try to locate the corresponding ':' indicator.
+     * Simple keys should be limited to a single line and 1024 characters.
+     * 
+     * Can a simple key start at the current position? A simple key may
+     * start:
+     * - at the beginning of the line, not counting indentation spaces
+     *       (in block context),
+     * - after '{', '[', ',' (in the flow context),
+     * - after '?', ':', '-' (in the block context).
+     * In the block context, this flag also signifies if a block collection
+     * may start at the current position.
+     * </pre>
+     */
+    private boolean allowSimpleKey = true;
+
+    /*
+     * Keep track of possible simple keys. This is a dictionary. The key is
+     * `flow_level`; there can be no more that one possible simple key for each
+     * level. The value is a SimpleKey record: (token_number, required, index,
+     * line, column, mark) A simple key may start with ALIAS, ANCHOR, TAG,
+     * SCALAR(flow), '[', or '{' tokens.
+     */
+    private Map<Integer, SimpleKey> possibleSimpleKeys;
+
+    public ScannerImpl(StreamReader reader) {
+        this.reader = reader;
+        this.tokens = new ArrayList<Token>(100);
+        this.indents = new ArrayStack<Integer>(10);
+        // The order in possibleSimpleKeys is kept for nextPossibleSimpleKey()
+        this.possibleSimpleKeys = new LinkedHashMap<Integer, SimpleKey>();
+        fetchStreamStart();// Add the STREAM-START token.
+    }
+
+    /**
+     * Check whether the next token is one of the given types.
+     */
+    public boolean checkToken(Token.ID... choices) {
+        while (needMoreTokens()) {
+            fetchMoreTokens();
+        }
+        if (!this.tokens.isEmpty()) {
+            if (choices.length == 0) {
+                return true;
+            }
+            // since profiler puts this method on top (it is used a lot), we
+            // should not use 'foreach' here because of the performance reasons
+            Token.ID first = this.tokens.get(0).getTokenId();
+            for (int i = 0; i < choices.length; i++) {
+                if (first == choices[i]) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return the next token, but do not delete it from the queue.
+     */
+    public Token peekToken() {
+        while (needMoreTokens()) {
+            fetchMoreTokens();
+        }
+        return this.tokens.get(0);
+    }
+
+    /**
+     * Return the next token, removing it from the queue.
+     */
+    public Token getToken() {
+        if (!this.tokens.isEmpty()) {
+            this.tokensTaken++;
+            return this.tokens.remove(0);
+        }
+        return null;
+    }
+
+    // Private methods.
+    /**
+     * Returns true if more tokens should be scanned.
+     */
+    private boolean needMoreTokens() {
+        // If we are done, we do not require more tokens.
+        if (this.done) {
+            return false;
+        }
+        // If we aren't done, but we have no tokens, we need to scan more.
+        if (this.tokens.isEmpty()) {
+            return true;
+        }
+        // The current token may be a potential simple key, so we
+        // need to look further.
+        stalePossibleSimpleKeys();
+        return nextPossibleSimpleKey() == this.tokensTaken;
+    }
+
+    /**
+     * Fetch one or more tokens from the StreamReader.
+     */
+    private void fetchMoreTokens() {
+        // Eat whitespaces and comments until we reach the next token.
+        scanToNextToken();
+        // Remove obsolete possible simple keys.
+        stalePossibleSimpleKeys();
+        // Compare the current indentation and column. It may add some tokens
+        // and decrease the current indentation level.
+        unwindIndent(reader.getColumn());
+        // Peek the next character, to decide what the next group of tokens
+        // will look like.
+        char ch = reader.peek();
+        switch (ch) {
+        case '\0':
+            // Is it the end of stream?
+            fetchStreamEnd();
+            return;
+        case '%':
+            // Is it a directive?
+            if (checkDirective()) {
+                fetchDirective();
+                return;
+            }
+            break;
+        case '-':
+            // Is it the document start?
+            if (checkDocumentStart()) {
+                fetchDocumentStart();
+                return;
+                // Is it the block entry indicator?
+            } else if (checkBlockEntry()) {
+                fetchBlockEntry();
+                return;
+            }
+            break;
+        case '.':
+            // Is it the document end?
+            if (checkDocumentEnd()) {
+                fetchDocumentEnd();
+                return;
+            }
+            break;
+        // TODO support for BOM within a stream. (not implemented in PyYAML)
+        case '[':
+            // Is it the flow sequence start indicator?
+            fetchFlowSequenceStart();
+            return;
+        case '{':
+            // Is it the flow mapping start indicator?
+            fetchFlowMappingStart();
+            return;
+        case ']':
+            // Is it the flow sequence end indicator?
+            fetchFlowSequenceEnd();
+            return;
+        case '}':
+            // Is it the flow mapping end indicator?
+            fetchFlowMappingEnd();
+            return;
+        case ',':
+            // Is it the flow entry indicator?
+            fetchFlowEntry();
+            return;
+            // see block entry indicator above
+        case '?':
+            // Is it the key indicator?
+            if (checkKey()) {
+                fetchKey();
+                return;
+            }
+            break;
+        case ':':
+            // Is it the value indicator?
+            if (checkValue()) {
+                fetchValue();
+                return;
+            }
+            break;
+        case '*':
+            // Is it an alias?
+            fetchAlias();
+            return;
+        case '&':
+            // Is it an anchor?
+            fetchAnchor();
+            return;
+        case '!':
+            // Is it a tag?
+            fetchTag();
+            return;
+        case '|':
+            // Is it a literal scalar?
+            if (this.flowLevel == 0) {
+                fetchLiteral();
+                return;
+            }
+            break;
+        case '>':
+            // Is it a folded scalar?
+            if (this.flowLevel == 0) {
+                fetchFolded();
+                return;
+            }
+            break;
+        case '\'':
+            // Is it a single quoted scalar?
+            fetchSingle();
+            return;
+        case '"':
+            // Is it a double quoted scalar?
+            fetchDouble();
+            return;
+        }
+        // It must be a plain scalar then.
+        if (checkPlain()) {
+            fetchPlain();
+            return;
+        }
+        // No? It's an error. Let's produce a nice error message.We do this by
+        // converting escaped characters into their escape sequences. This is a
+        // backwards use of the ESCAPE_REPLACEMENTS map.
+        String chRepresentation = String.valueOf(ch);
+        for (Character s : ESCAPE_REPLACEMENTS.keySet()) {
+            String v = ESCAPE_REPLACEMENTS.get(s);
+            if (v.equals(chRepresentation)) {
+                chRepresentation = "\\" + s;// ' ' -> '\t'
+                break;
+            }
+        }
+        if (ch == '\t')
+            chRepresentation += "(TAB)";
+        String text = String
+                .format("found character '%s' that cannot start any token. (Do not use %s for indentation)",
+                        chRepresentation, chRepresentation);
+        throw new ScannerException("while scanning for the next token", null, text,
+                reader.getMark());
+    }
+
+    // Simple keys treatment.
+
+    /**
+     * Return the number of the nearest possible simple key. Actually we don't
+     * need to loop through the whole dictionary.
+     */
+    private int nextPossibleSimpleKey() {
+        /*
+         * the implementation is not as in PyYAML. Because
+         * this.possibleSimpleKeys is ordered we can simply take the first key
+         */
+        if (!this.possibleSimpleKeys.isEmpty()) {
+            return this.possibleSimpleKeys.values().iterator().next().getTokenNumber();
+        }
+        return -1;
+    }
+
+    /**
+     * <pre>
+     * Remove entries that are no longer possible simple keys. According to
+     * the YAML specification, simple keys
+     * - should be limited to a single line,
+     * - should be no longer than 1024 characters.
+     * Disabling this procedure will allow simple keys of any length and
+     * height (may cause problems if indentation is broken though).
+     * </pre>
+     */
+    private void stalePossibleSimpleKeys() {
+        if (!this.possibleSimpleKeys.isEmpty()) {
+            for (Iterator<SimpleKey> iterator = this.possibleSimpleKeys.values().iterator(); iterator
+                    .hasNext();) {
+                SimpleKey key = iterator.next();
+                if ((key.getLine() != reader.getLine())
+                        || (reader.getIndex() - key.getIndex() > 1024)) {
+                    // If the key is not on the same line as the current
+                    // position OR the difference in column between the token
+                    // start and the current position is more than the maximum
+                    // simple key length, then this cannot be a simple key.
+                    if (key.isRequired()) {
+                        // If the key was required, this implies an error
+                        // condition.
+                        throw new ScannerException("while scanning a simple key", key.getMark(),
+                                "could not find expected ':'", reader.getMark());
+                    }
+                    iterator.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * The next token may start a simple key. We check if it's possible and save
+     * its position. This function is called for ALIAS, ANCHOR, TAG,
+     * SCALAR(flow), '[', and '{'.
+     */
+    private void savePossibleSimpleKey() {
+        // The next token may start a simple key. We check if it's possible
+        // and save its position. This function is called for
+        // ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'.
+
+        // Check if a simple key is required at the current position.
+        // A simple key is required if this position is the root flowLevel, AND
+        // the current indentation level is the same as the last indent-level.
+        boolean required = (this.flowLevel == 0) && (this.indent == this.reader.getColumn());
+
+        if (allowSimpleKey || !required) {
+            // A simple key is required only if it is the first token in the
+            // current line. Therefore it is always allowed.
+        } else {
+            throw new YAMLException(
+                    "A simple key is required only if it is the first token in the current line");
+        }
+
+        // The next token might be a simple key. Let's save it's number and
+        // position.
+        if (this.allowSimpleKey) {
+            removePossibleSimpleKey();
+            int tokenNumber = this.tokensTaken + this.tokens.size();
+            SimpleKey key = new SimpleKey(tokenNumber, required, reader.getIndex(),
+                    reader.getLine(), this.reader.getColumn(), this.reader.getMark());
+            this.possibleSimpleKeys.put(this.flowLevel, key);
+        }
+    }
+
+    /**
+     * Remove the saved possible key position at the current flow level.
+     */
+    private void removePossibleSimpleKey() {
+        SimpleKey key = possibleSimpleKeys.remove(flowLevel);
+        if (key != null && key.isRequired()) {
+            throw new ScannerException("while scanning a simple key", key.getMark(),
+                    "could not find expected ':'", reader.getMark());
+        }
+    }
+
+    // Indentation functions.
+
+    /**
+     * * Handle implicitly ending multiple levels of block nodes by decreased
+     * indentation. This function becomes important on lines 4 and 7 of this
+     * example:
+     * 
+     * <pre>
+     * 1) book one:
+     * 2)   part one:
+     * 3)     chapter one
+     * 4)   part two:
+     * 5)     chapter one
+     * 6)     chapter two
+     * 7) book two:
+     * </pre>
+     * 
+     * In flow context, tokens should respect indentation. Actually the
+     * condition should be `self.indent &gt;= column` according to the spec. But
+     * this condition will prohibit intuitively correct constructions such as
+     * key : { } </pre>
+     */
+    private void unwindIndent(int col) {
+        // In the flow context, indentation is ignored. We make the scanner less
+        // restrictive then specification requires.
+        if (this.flowLevel != 0) {
+            return;
+        }
+
+        // In block context, we may need to issue the BLOCK-END tokens.
+        while (this.indent > col) {
+            Mark mark = reader.getMark();
+            this.indent = this.indents.pop();
+            this.tokens.add(new BlockEndToken(mark, mark));
+        }
+    }
+
+    /**
+     * Check if we need to increase indentation.
+     */
+    private boolean addIndent(int column) {
+        if (this.indent < column) {
+            this.indents.push(this.indent);
+            this.indent = column;
+            return true;
+        }
+        return false;
+    }
+
+    // Fetchers.
+
+    /**
+     * We always add STREAM-START as the first token and STREAM-END as the last
+     * token.
+     */
+    private void fetchStreamStart() {
+        // Read the token.
+        Mark mark = reader.getMark();
+
+        // Add STREAM-START.
+        Token token = new StreamStartToken(mark, mark);
+        this.tokens.add(token);
+    }
+
+    private void fetchStreamEnd() {
+        // Set the current intendation to -1.
+        unwindIndent(-1);
+
+        // Reset simple keys.
+        removePossibleSimpleKey();
+        this.allowSimpleKey = false;
+        this.possibleSimpleKeys.clear();
+
+        // Read the token.
+        Mark mark = reader.getMark();
+
+        // Add STREAM-END.
+        Token token = new StreamEndToken(mark, mark);
+        this.tokens.add(token);
+
+        // The stream is finished.
+        this.done = true;
+    }
+
+    /**
+     * Fetch a YAML directive. Directives are presentation details that are
+     * interpreted as instructions to the processor. YAML defines two kinds of
+     * directives, YAML and TAG; all other types are reserved for future use.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id864824"></a>
+     */
+    private void fetchDirective() {
+        // Set the current intendation to -1.
+        unwindIndent(-1);
+
+        // Reset simple keys.
+        removePossibleSimpleKey();
+        this.allowSimpleKey = false;
+
+        // Scan and add DIRECTIVE.
+        Token tok = scanDirective();
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch a document-start token ("---").
+     */
+    private void fetchDocumentStart() {
+        fetchDocumentIndicator(true);
+    }
+
+    /**
+     * Fetch a document-end token ("...").
+     */
+    private void fetchDocumentEnd() {
+        fetchDocumentIndicator(false);
+    }
+
+    /**
+     * Fetch a document indicator, either "---" for "document-start", or else
+     * "..." for "document-end. The type is chosen by the given boolean.
+     */
+    private void fetchDocumentIndicator(boolean isDocumentStart) {
+        // Set the current intendation to -1.
+        unwindIndent(-1);
+
+        // Reset simple keys. Note that there could not be a block collection
+        // after '---'.
+        removePossibleSimpleKey();
+        this.allowSimpleKey = false;
+
+        // Add DOCUMENT-START or DOCUMENT-END.
+        Mark startMark = reader.getMark();
+        reader.forward(3);
+        Mark endMark = reader.getMark();
+        Token token;
+        if (isDocumentStart) {
+            token = new DocumentStartToken(startMark, endMark);
+        } else {
+            token = new DocumentEndToken(startMark, endMark);
+        }
+        this.tokens.add(token);
+    }
+
+    private void fetchFlowSequenceStart() {
+        fetchFlowCollectionStart(false);
+    }
+
+    private void fetchFlowMappingStart() {
+        fetchFlowCollectionStart(true);
+    }
+
+    /**
+     * Fetch a flow-style collection start, which is either a sequence or a
+     * mapping. The type is determined by the given boolean.
+     * 
+     * A flow-style collection is in a format similar to JSON. Sequences are
+     * started by '[' and ended by ']'; mappings are started by '{' and ended by
+     * '}'.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     * 
+     * @param isMappingStart
+     */
+    private void fetchFlowCollectionStart(boolean isMappingStart) {
+        // '[' and '{' may start a simple key.
+        savePossibleSimpleKey();
+
+        // Increase the flow level.
+        this.flowLevel++;
+
+        // Simple keys are allowed after '[' and '{'.
+        this.allowSimpleKey = true;
+
+        // Add FLOW-SEQUENCE-START or FLOW-MAPPING-START.
+        Mark startMark = reader.getMark();
+        reader.forward(1);
+        Mark endMark = reader.getMark();
+        Token token;
+        if (isMappingStart) {
+            token = new FlowMappingStartToken(startMark, endMark);
+        } else {
+            token = new FlowSequenceStartToken(startMark, endMark);
+        }
+        this.tokens.add(token);
+    }
+
+    private void fetchFlowSequenceEnd() {
+        fetchFlowCollectionEnd(false);
+    }
+
+    private void fetchFlowMappingEnd() {
+        fetchFlowCollectionEnd(true);
+    }
+
+    /**
+     * Fetch a flow-style collection end, which is either a sequence or a
+     * mapping. The type is determined by the given boolean.
+     * 
+     * A flow-style collection is in a format similar to JSON. Sequences are
+     * started by '[' and ended by ']'; mappings are started by '{' and ended by
+     * '}'.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchFlowCollectionEnd(boolean isMappingEnd) {
+        // Reset possible simple key on the current level.
+        removePossibleSimpleKey();
+
+        // Decrease the flow level.
+        this.flowLevel--;
+
+        // No simple keys after ']' or '}'.
+        this.allowSimpleKey = false;
+
+        // Add FLOW-SEQUENCE-END or FLOW-MAPPING-END.
+        Mark startMark = reader.getMark();
+        reader.forward();
+        Mark endMark = reader.getMark();
+        Token token;
+        if (isMappingEnd) {
+            token = new FlowMappingEndToken(startMark, endMark);
+        } else {
+            token = new FlowSequenceEndToken(startMark, endMark);
+        }
+        this.tokens.add(token);
+    }
+
+    /**
+     * Fetch an entry in the flow style. Flow-style entries occur either
+     * immediately after the start of a collection, or else after a comma.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchFlowEntry() {
+        // Simple keys are allowed after ','.
+        this.allowSimpleKey = true;
+
+        // Reset possible simple key on the current level.
+        removePossibleSimpleKey();
+
+        // Add FLOW-ENTRY.
+        Mark startMark = reader.getMark();
+        reader.forward();
+        Mark endMark = reader.getMark();
+        Token token = new FlowEntryToken(startMark, endMark);
+        this.tokens.add(token);
+    }
+
+    /**
+     * Fetch an entry in the block style.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchBlockEntry() {
+        // Block context needs additional checks.
+        if (this.flowLevel == 0) {
+            // Are we allowed to start a new entry?
+            if (!this.allowSimpleKey) {
+                throw new ScannerException(null, null, "sequence entries are not allowed here",
+                        reader.getMark());
+            }
+
+            // We may need to add BLOCK-SEQUENCE-START.
+            if (addIndent(this.reader.getColumn())) {
+                Mark mark = reader.getMark();
+                this.tokens.add(new BlockSequenceStartToken(mark, mark));
+            }
+        } else {
+            // It's an error for the block entry to occur in the flow
+            // context,but we let the parser detect this.
+        }
+        // Simple keys are allowed after '-'.
+        this.allowSimpleKey = true;
+
+        // Reset possible simple key on the current level.
+        removePossibleSimpleKey();
+
+        // Add BLOCK-ENTRY.
+        Mark startMark = reader.getMark();
+        reader.forward();
+        Mark endMark = reader.getMark();
+        Token token = new BlockEntryToken(startMark, endMark);
+        this.tokens.add(token);
+    }
+
+    /**
+     * Fetch a key in a block-style mapping.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchKey() {
+        // Block context needs additional checks.
+        if (this.flowLevel == 0) {
+            // Are we allowed to start a key (not necessary a simple)?
+            if (!this.allowSimpleKey) {
+                throw new ScannerException(null, null, "mapping keys are not allowed here",
+                        reader.getMark());
+            }
+            // We may need to add BLOCK-MAPPING-START.
+            if (addIndent(this.reader.getColumn())) {
+                Mark mark = reader.getMark();
+                this.tokens.add(new BlockMappingStartToken(mark, mark));
+            }
+        }
+        // Simple keys are allowed after '?' in the block context.
+        this.allowSimpleKey = this.flowLevel == 0;
+
+        // Reset possible simple key on the current level.
+        removePossibleSimpleKey();
+
+        // Add KEY.
+        Mark startMark = reader.getMark();
+        reader.forward();
+        Mark endMark = reader.getMark();
+        Token token = new KeyToken(startMark, endMark);
+        this.tokens.add(token);
+    }
+
+    /**
+     * Fetch a value in a block-style mapping.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchValue() {
+        // Do we determine a simple key?
+        SimpleKey key = this.possibleSimpleKeys.remove(this.flowLevel);
+        if (key != null) {
+            // Add KEY.
+            this.tokens.add(key.getTokenNumber() - this.tokensTaken, new KeyToken(key.getMark(),
+                    key.getMark()));
+
+            // If this key starts a new block mapping, we need to add
+            // BLOCK-MAPPING-START.
+            if (this.flowLevel == 0) {
+                if (addIndent(key.getColumn())) {
+                    this.tokens.add(key.getTokenNumber() - this.tokensTaken,
+                            new BlockMappingStartToken(key.getMark(), key.getMark()));
+                }
+            }
+            // There cannot be two simple keys one after another.
+            this.allowSimpleKey = false;
+
+        } else {
+            // It must be a part of a complex key.
+            // Block context needs additional checks. Do we really need them?
+            // They will be caught by the parser anyway.
+            if (this.flowLevel == 0) {
+
+                // We are allowed to start a complex value if and only if we can
+                // start a simple key.
+                if (!this.allowSimpleKey) {
+                    throw new ScannerException(null, null, "mapping values are not allowed here",
+                            reader.getMark());
+                }
+            }
+
+            // If this value starts a new block mapping, we need to add
+            // BLOCK-MAPPING-START. It will be detected as an error later by
+            // the parser.
+            if (flowLevel == 0) {
+                if (addIndent(reader.getColumn())) {
+                    Mark mark = reader.getMark();
+                    this.tokens.add(new BlockMappingStartToken(mark, mark));
+                }
+            }
+
+            // Simple keys are allowed after ':' in the block context.
+            allowSimpleKey = flowLevel == 0;
+
+            // Reset possible simple key on the current level.
+            removePossibleSimpleKey();
+        }
+        // Add VALUE.
+        Mark startMark = reader.getMark();
+        reader.forward();
+        Mark endMark = reader.getMark();
+        Token token = new ValueToken(startMark, endMark);
+        this.tokens.add(token);
+    }
+
+    /**
+     * Fetch an alias, which is a reference to an anchor. Aliases take the
+     * format:
+     * 
+     * <pre>
+     * *(anchor name)
+     * </pre>
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863390"></a>
+     */
+    private void fetchAlias() {
+        // ALIAS could be a simple key.
+        savePossibleSimpleKey();
+
+        // No simple keys after ALIAS.
+        this.allowSimpleKey = false;
+
+        // Scan and add ALIAS.
+        Token tok = scanAnchor(false);
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch an anchor. Anchors take the form:
+     * 
+     * <pre>
+     * &(anchor name)
+     * </pre>
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863390"></a>
+     */
+    private void fetchAnchor() {
+        // ANCHOR could start a simple key.
+        savePossibleSimpleKey();
+
+        // No simple keys after ANCHOR.
+        this.allowSimpleKey = false;
+
+        // Scan and add ANCHOR.
+        Token tok = scanAnchor(true);
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch a tag. Tags take a complex form.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id861700"></a>
+     */
+    private void fetchTag() {
+        // TAG could start a simple key.
+        savePossibleSimpleKey();
+
+        // No simple keys after TAG.
+        this.allowSimpleKey = false;
+
+        // Scan and add TAG.
+        Token tok = scanTag();
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch a literal scalar, denoted with a vertical-bar. This is the type
+     * best used for source code and other content, such as binary data, which
+     * must be included verbatim.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchLiteral() {
+        fetchBlockScalar('|');
+    }
+
+    /**
+     * Fetch a folded scalar, denoted with a greater-than sign. This is the type
+     * best used for long content, such as the text of a chapter or description.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     */
+    private void fetchFolded() {
+        fetchBlockScalar('>');
+    }
+
+    /**
+     * Fetch a block scalar (literal or folded).
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     * 
+     * @param style
+     */
+    private void fetchBlockScalar(char style) {
+        // A simple key may follow a block scalar.
+        this.allowSimpleKey = true;
+
+        // Reset possible simple key on the current level.
+        removePossibleSimpleKey();
+
+        // Scan and add SCALAR.
+        Token tok = scanBlockScalar(style);
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch a single-quoted (') scalar.
+     */
+    private void fetchSingle() {
+        fetchFlowScalar('\'');
+    }
+
+    /**
+     * Fetch a double-quoted (") scalar.
+     */
+    private void fetchDouble() {
+        fetchFlowScalar('"');
+    }
+
+    /**
+     * Fetch a flow scalar (single- or double-quoted).
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id863975"></a>
+     * 
+     * @param style
+     */
+    private void fetchFlowScalar(char style) {
+        // A flow scalar could be a simple key.
+        savePossibleSimpleKey();
+
+        // No simple keys after flow scalars.
+        this.allowSimpleKey = false;
+
+        // Scan and add SCALAR.
+        Token tok = scanFlowScalar(style);
+        this.tokens.add(tok);
+    }
+
+    /**
+     * Fetch a plain scalar.
+     */
+    private void fetchPlain() {
+        // A plain scalar could be a simple key.
+        savePossibleSimpleKey();
+
+        // No simple keys after plain scalars. But note that `scan_plain` will
+        // change this flag if the scan is finished at the beginning of the
+        // line.
+        this.allowSimpleKey = false;
+
+        // Scan and add SCALAR. May change `allow_simple_key`.
+        Token tok = scanPlain();
+        this.tokens.add(tok);
+    }
+
+    // Checkers.
+    /**
+     * Returns true if the next thing on the reader is a directive, given that
+     * the leading '%' has already been checked.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id864824"></a>
+     */
+    private boolean checkDirective() {
+        // DIRECTIVE: ^ '%' ...
+        // The '%' indicator is already checked.
+        return reader.getColumn() == 0;
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a document-start ("---").
+     * A document-start is always followed immediately by a new line.
+     */
+    private boolean checkDocumentStart() {
+        // DOCUMENT-START: ^ '---' (' '|'\n')
+        if (reader.getColumn() == 0) {
+            if ("---".equals(reader.prefix(3)) && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a document-end ("..."). A
+     * document-end is always followed immediately by a new line.
+     */
+    private boolean checkDocumentEnd() {
+        // DOCUMENT-END: ^ '...' (' '|'\n')
+        if (reader.getColumn() == 0) {
+            if ("...".equals(reader.prefix(3)) && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a block token.
+     */
+    private boolean checkBlockEntry() {
+        // BLOCK-ENTRY: '-' (' '|'\n')
+        return Constant.NULL_BL_T_LINEBR.has(reader.peek(1));
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a key token.
+     */
+    private boolean checkKey() {
+        // KEY(flow context): '?'
+        if (this.flowLevel != 0) {
+            return true;
+        } else {
+            // KEY(block context): '?' (' '|'\n')
+            return Constant.NULL_BL_T_LINEBR.has(reader.peek(1));
+        }
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a value token.
+     */
+    private boolean checkValue() {
+        // VALUE(flow context): ':'
+        if (flowLevel != 0) {
+            return true;
+        } else {
+            // VALUE(block context): ':' (' '|'\n')
+            return Constant.NULL_BL_T_LINEBR.has(reader.peek(1));
+        }
+    }
+
+    /**
+     * Returns true if the next thing on the reader is a plain token.
+     */
+    private boolean checkPlain() {
+        /**
+         * <pre>
+         * A plain scalar may start with any non-space character except:
+         *   '-', '?', ':', ',', '[', ']', '{', '}',
+         *   '#', '&amp;', '*', '!', '|', '&gt;', '\'', '\&quot;',
+         *   '%', '@', '`'.
+         * 
+         * It may also start with
+         *   '-', '?', ':'
+         * if it is followed by a non-space character.
+         * 
+         * Note that we limit the last rule to the block context (except the
+         * '-' character) because we want the flow context to be space
+         * independent.
+         * </pre>
+         */
+        char ch = reader.peek();
+        // If the next char is NOT one of the forbidden chars above or
+        // whitespace, then this is the start of a plain scalar.
+        return Constant.NULL_BL_T_LINEBR.hasNo(ch, "-?:,[]{}#&*!|>\'\"%@`")
+                || (Constant.NULL_BL_T_LINEBR.hasNo(reader.peek(1)) && (ch == '-' || (this.flowLevel == 0 && "?:"
+                        .indexOf(ch) != -1)));
+    }
+
+    // Scanners.
+
+    /**
+     * <pre>
+     * We ignore spaces, line breaks and comments.
+     * If we find a line break in the block context, we set the flag
+     * `allow_simple_key` on.
+     * The byte order mark is stripped if it's the first character in the
+     * stream. We do not yet support BOM inside the stream as the
+     * specification requires. Any such mark will be considered as a part
+     * of the document.
+     * TODO: We need to make tab handling rules more sane. A good rule is
+     *   Tabs cannot precede tokens
+     *   BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END,
+     *   KEY(block), VALUE(block), BLOCK-ENTRY
+     * So the checking code is
+     *   if &lt;TAB&gt;:
+     *       self.allow_simple_keys = False
+     * We also need to add the check for `allow_simple_keys == True` to
+     * `unwind_indent` before issuing BLOCK-END.
+     * Scanners for block, flow, and plain scalars need to be modified.
+     * </pre>
+     */
+    private void scanToNextToken() {
+        // If there is a byte order mark (BOM) at the beginning of the stream,
+        // forward past it.
+        if (reader.getIndex() == 0 && reader.peek() == '\uFEFF') {
+            reader.forward();
+        }
+        boolean found = false;
+        while (!found) {
+            int ff = 0;
+            // Peek ahead until we find the first non-space character, then
+            // move forward directly to that character.
+            while (reader.peek(ff) == ' ') {
+                ff++;
+            }
+            if (ff > 0) {
+                reader.forward(ff);
+            }
+            // If the character we have skipped forward to is a comment (#),
+            // then peek ahead until we find the next end of line. YAML
+            // comments are from a # to the next new-line. We then forward
+            // past the comment.
+            if (reader.peek() == '#') {
+                ff = 0;
+                while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(ff))) {
+                    ff++;
+                }
+                if (ff > 0) {
+                    reader.forward(ff);
+                }
+            }
+            // If we scanned a line break, then (depending on flow level),
+            // simple keys may be allowed.
+            if (scanLineBreak().length() != 0) {// found a line-break
+                if (this.flowLevel == 0) {
+                    // Simple keys are allowed at flow-level 0 after a line
+                    // break
+                    this.allowSimpleKey = true;
+                }
+            } else {
+                found = true;
+            }
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private Token scanDirective() {
+        // See the specification for details.
+        Mark startMark = reader.getMark();
+        Mark endMark;
+        reader.forward();
+        String name = scanDirectiveName(startMark);
+        List<?> value = null;
+        if ("YAML".equals(name)) {
+            value = scanYamlDirectiveValue(startMark);
+            endMark = reader.getMark();
+        } else if ("TAG".equals(name)) {
+            value = scanTagDirectiveValue(startMark);
+            endMark = reader.getMark();
+        } else {
+            endMark = reader.getMark();
+            int ff = 0;
+            while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(ff))) {
+                ff++;
+            }
+            if (ff > 0) {
+                reader.forward(ff);
+            }
+        }
+        scanDirectiveIgnoredLine(startMark);
+        return new DirectiveToken(name, value, startMark, endMark);
+    }
+
+    /**
+     * Scan a directive name. Directive names are a series of non-space
+     * characters.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id895217"></a>
+     */
+    private String scanDirectiveName(Mark startMark) {
+        // See the specification for details.
+        int length = 0;
+        // A Directive-name is a sequence of alphanumeric characters
+        // (a-z,A-Z,0-9). We scan until we find something that isn't.
+        // FIXME this disagrees with the specification.
+        char ch = reader.peek(length);
+        while (Constant.ALPHA.has(ch)) {
+            length++;
+            ch = reader.peek(length);
+        }
+        // If the name would be empty, an error occurs.
+        if (length == 0) {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected alphabetic or numeric character, but found " + ch + "(" + ((int) ch)
+                            + ")", reader.getMark());
+        }
+        String value = reader.prefixForward(length);
+        ch = reader.peek();
+        if (Constant.NULL_BL_LINEBR.hasNo(ch)) {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected alphabetic or numeric character, but found " + ch + "(" + ((int) ch)
+                            + ")", reader.getMark());
+        }
+        return value;
+    }
+
+    private List<Integer> scanYamlDirectiveValue(Mark startMark) {
+        // See the specification for details.
+        while (reader.peek() == ' ') {
+            reader.forward();
+        }
+        Integer major = scanYamlDirectiveNumber(startMark);
+        if (reader.peek() != '.') {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected a digit or '.', but found " + reader.peek() + "("
+                            + ((int) reader.peek()) + ")", reader.getMark());
+        }
+        reader.forward();
+        Integer minor = scanYamlDirectiveNumber(startMark);
+        if (Constant.NULL_BL_LINEBR.hasNo(reader.peek())) {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected a digit or ' ', but found " + reader.peek() + "("
+                            + ((int) reader.peek()) + ")", reader.getMark());
+        }
+        List<Integer> result = new ArrayList<Integer>(2);
+        result.add(major);
+        result.add(minor);
+        return result;
+    }
+
+    /**
+     * Read a %YAML directive number: this is either the major or the minor
+     * part. Stop reading at a non-digit character (usually either '.' or '\n').
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id895631"></a>
+     * @see <a href="http://www.yaml.org/spec/1.1/#ns-dec-digit"></a>
+     */
+    private Integer scanYamlDirectiveNumber(Mark startMark) {
+        // See the specification for details.
+        char ch = reader.peek();
+        if (!Character.isDigit(ch)) {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected a digit, but found " + ch + "(" + ((int) ch) + ")", reader.getMark());
+        }
+        int length = 0;
+        while (Character.isDigit(reader.peek(length))) {
+            length++;
+        }
+        Integer value = Integer.parseInt(reader.prefixForward(length));
+        return value;
+    }
+
+    /**
+     * <p>
+     * Read a %TAG directive value:
+     * 
+     * <pre>
+     * s-ignored-space+ c-tag-handle s-ignored-space+ ns-tag-prefix s-l-comments
+     * </pre>
+     * 
+     * </p>
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id896044"></a>
+     */
+    private List<String> scanTagDirectiveValue(Mark startMark) {
+        // See the specification for details.
+        while (reader.peek() == ' ') {
+            reader.forward();
+        }
+        String handle = scanTagDirectiveHandle(startMark);
+        while (reader.peek() == ' ') {
+            reader.forward();
+        }
+        String prefix = scanTagDirectivePrefix(startMark);
+        List<String> result = new ArrayList<String>(2);
+        result.add(handle);
+        result.add(prefix);
+        return result;
+    }
+
+    /**
+     * Scan a %TAG directive's handle. This is YAML's c-tag-handle.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id896876"></a>
+     * @param startMark
+     * @return
+     */
+    private String scanTagDirectiveHandle(Mark startMark) {
+        // See the specification for details.
+        String value = scanTagHandle("directive", startMark);
+        char ch = reader.peek();
+        if (ch != ' ') {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected ' ', but found " + reader.peek() + "(" + ch + ")", reader.getMark());
+        }
+        return value;
+    }
+
+    /**
+     * Scan a %TAG directive's prefix. This is YAML's ns-tag-prefix.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#ns-tag-prefix"></a>
+     */
+    private String scanTagDirectivePrefix(Mark startMark) {
+        // See the specification for details.
+        String value = scanTagUri("directive", startMark);
+        if (Constant.NULL_BL_LINEBR.hasNo(reader.peek())) {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected ' ', but found " + reader.peek() + "(" + ((int) reader.peek()) + ")",
+                    reader.getMark());
+        }
+        return value;
+    }
+
+    private String scanDirectiveIgnoredLine(Mark startMark) {
+        // See the specification for details.
+        int ff = 0;
+        while (reader.peek(ff) == ' ') {
+            ff++;
+        }
+        if (ff > 0) {
+            reader.forward(ff);
+        }
+        if (reader.peek() == '#') {
+            ff = 0;
+            while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(ff))) {
+                ff++;
+            }
+            reader.forward(ff);
+        }
+        char ch = reader.peek();
+        String lineBreak = scanLineBreak();
+        if (lineBreak.length() == 0 && ch != '\0') {
+            throw new ScannerException("while scanning a directive", startMark,
+                    "expected a comment or a line break, but found " + ch + "(" + ((int) ch) + ")",
+                    reader.getMark());
+        }
+        return lineBreak;
+    }
+
+    /**
+     * <pre>
+     * The specification does not restrict characters for anchors and
+     * aliases. This may lead to problems, for instance, the document:
+     *   [ *alias, value ]
+     * can be interpreted in two ways, as
+     *   [ &quot;value&quot; ]
+     * and
+     *   [ *alias , &quot;value&quot; ]
+     * Therefore we restrict aliases to numbers and ASCII letters.
+     * </pre>
+     */
+    private Token scanAnchor(boolean isAnchor) {
+        Mark startMark = reader.getMark();
+        char indicator = reader.peek();
+        String name = indicator == '*' ? "alias" : "anchor";
+        reader.forward();
+        int length = 0;
+        char ch = reader.peek(length);
+        while (Constant.ALPHA.has(ch)) {
+            length++;
+            ch = reader.peek(length);
+        }
+        if (length == 0) {
+            throw new ScannerException("while scanning an " + name, startMark,
+                    "expected alphabetic or numeric character, but found " + ch,
+                    reader.getMark());
+        }
+        String value = reader.prefixForward(length);
+        ch = reader.peek();
+        if (Constant.NULL_BL_T_LINEBR.hasNo(ch, "?:,]}%@`")) {
+            throw new ScannerException("while scanning an " + name, startMark,
+                    "expected alphabetic or numeric character, but found " + ch + "("
+                            + ((int) reader.peek()) + ")", reader.getMark());
+        }
+        Mark endMark = reader.getMark();
+        Token tok;
+        if (isAnchor) {
+            tok = new AnchorToken(value, startMark, endMark);
+        } else {
+            tok = new AliasToken(value, startMark, endMark);
+        }
+        return tok;
+    }
+
+    /**
+     * <p>
+     * Scan a Tag property. A Tag property may be specified in one of three
+     * ways: c-verbatim-tag, c-ns-shorthand-tag, or c-ns-non-specific-tag
+     * </p>
+     * 
+     * <p>
+     * c-verbatim-tag takes the form !&lt;ns-uri-char+&gt; and must be delivered
+     * verbatim (as-is) to the application. In particular, verbatim tags are not
+     * subject to tag resolution.
+     * </p>
+     * 
+     * <p>
+     * c-ns-shorthand-tag is a valid tag handle followed by a non-empty suffix.
+     * If the tag handle is a c-primary-tag-handle ('!') then the suffix must
+     * have all exclamation marks properly URI-escaped (%21); otherwise, the
+     * string will look like a named tag handle: !foo!bar would be interpreted
+     * as (handle="!foo!", suffix="bar").
+     * </p>
+     * 
+     * <p>
+     * c-ns-non-specific-tag is always a lone '!'; this is only useful for plain
+     * scalars, where its specification means that the scalar MUST be resolved
+     * to have type tag:yaml.org,2002:str.
+     * </p>
+     * 
+     * TODO SnakeYaml incorrectly ignores c-ns-non-specific-tag right now.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id900262"></a>
+     * 
+     *      TODO Note that this method does not enforce rules about local versus
+     *      global tags!
+     */
+    private Token scanTag() {
+        // See the specification for details.
+        Mark startMark = reader.getMark();
+        // Determine the type of tag property based on the first character
+        // encountered
+        char ch = reader.peek(1);
+        String handle = null;
+        String suffix = null;
+        // Verbatim tag! (c-verbatim-tag)
+        if (ch == '<') {
+            // Skip the exclamation mark and &gt;, then read the tag suffix (as
+            // a URI).
+            reader.forward(2);
+            suffix = scanTagUri("tag", startMark);
+            if (reader.peek() != '>') {
+                // If there are any characters between the end of the tag-suffix
+                // URI and the closing &gt;, then an error has occurred.
+                throw new ScannerException("while scanning a tag", startMark,
+                        "expected '>', but found '" + reader.peek() + "' (" + ((int) reader.peek())
+                                + ")", reader.getMark());
+            }
+            reader.forward();
+        } else if (Constant.NULL_BL_T_LINEBR.has(ch)) {
+            // A NUL, blank, tab, or line-break means that this was a
+            // c-ns-non-specific tag.
+            suffix = "!";
+            reader.forward();
+        } else {
+            // Any other character implies c-ns-shorthand-tag type.
+
+            // Look ahead in the stream to determine whether this tag property
+            // is of the form !foo or !foo!bar.
+            int length = 1;
+            boolean useHandle = false;
+            while (Constant.NULL_BL_LINEBR.hasNo(ch)) {
+                if (ch == '!') {
+                    useHandle = true;
+                    break;
+                }
+                length++;
+                ch = reader.peek(length);
+            }
+            handle = "!";
+            // If we need to use a handle, scan it in; otherwise, the handle is
+            // presumed to be '!'.
+            if (useHandle) {
+                handle = scanTagHandle("tag", startMark);
+            } else {
+                handle = "!";
+                reader.forward();
+            }
+            suffix = scanTagUri("tag", startMark);
+        }
+        ch = reader.peek();
+        // Check that the next character is allowed to follow a tag-property;
+        // if it is not, raise the error.
+        if (Constant.NULL_BL_LINEBR.hasNo(ch)) {
+            throw new ScannerException("while scanning a tag", startMark,
+                    "expected ' ', but found '" + ch + "' (" + ((int) ch) + ")", reader.getMark());
+        }
+        TagTuple value = new TagTuple(handle, suffix);
+        Mark endMark = reader.getMark();
+        return new TagToken(value, startMark, endMark);
+    }
+
+    private Token scanBlockScalar(char style) {
+        // See the specification for details.
+        boolean folded;
+        // Depending on the given style, we determine whether the scalar is
+        // folded ('>') or literal ('|')
+        if (style == '>') {
+            folded = true;
+        } else {
+            folded = false;
+        }
+        StringBuilder chunks = new StringBuilder();
+        Mark startMark = reader.getMark();
+        // Scan the header.
+        reader.forward();
+        Chomping chompi = scanBlockScalarIndicators(startMark);
+        int increment = chompi.getIncrement();
+        scanBlockScalarIgnoredLine(startMark);
+
+        // Determine the indentation level and go to the first non-empty line.
+        int minIndent = this.indent + 1;
+        if (minIndent < 1) {
+            minIndent = 1;
+        }
+        String breaks = null;
+        int maxIndent = 0;
+        int indent = 0;
+        Mark endMark;
+        if (increment == -1) {
+            Object[] brme = scanBlockScalarIndentation();
+            breaks = (String) brme[0];
+            maxIndent = ((Integer) brme[1]).intValue();
+            endMark = (Mark) brme[2];
+            indent = Math.max(minIndent, maxIndent);
+        } else {
+            indent = minIndent + increment - 1;
+            Object[] brme = scanBlockScalarBreaks(indent);
+            breaks = (String) brme[0];
+            endMark = (Mark) brme[1];
+        }
+
+        String lineBreak = "";
+
+        // Scan the inner part of the block scalar.
+        while (this.reader.getColumn() == indent && reader.peek() != '\0') {
+            chunks.append(breaks);
+            boolean leadingNonSpace = " \t".indexOf(reader.peek()) == -1;
+            int length = 0;
+            while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(length))) {
+                length++;
+            }
+            chunks.append(reader.prefixForward(length));
+            lineBreak = scanLineBreak();
+            Object[] brme = scanBlockScalarBreaks(indent);
+            breaks = (String) brme[0];
+            endMark = (Mark) brme[1];
+            if (this.reader.getColumn() == indent && reader.peek() != '\0') {
+
+                // Unfortunately, folding rules are ambiguous.
+                //
+                // This is the folding according to the specification:
+                if (folded && "\n".equals(lineBreak) && leadingNonSpace
+                        && " \t".indexOf(reader.peek()) == -1) {
+                    if (breaks.length() == 0) {
+                        chunks.append(" ");
+                    }
+                } else {
+                    chunks.append(lineBreak);
+                }
+                // Clark Evans's interpretation (also in the spec examples) not
+                // imported from PyYAML
+            } else {
+                break;
+            }
+        }
+        // Chomp the tail.
+        if (chompi.chompTailIsNotFalse()) {
+            chunks.append(lineBreak);
+        }
+        if (chompi.chompTailIsTrue()) {
+            chunks.append(breaks);
+        }
+        // We are done.
+        return new ScalarToken(chunks.toString(), false, startMark, endMark, style);
+    }
+
+    /**
+     * Scan a block scalar indicator. The block scalar indicator includes two
+     * optional components, which may appear in either order.
+     * 
+     * A block indentation indicator is a non-zero digit describing the
+     * indentation level of the block scalar to follow. This indentation is an
+     * additional number of spaces relative to the current indentation level.
+     * 
+     * A block chomping indicator is a + or -, selecting the chomping mode away
+     * from the default (clip) to either -(strip) or +(keep).
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id868988"></a>
+     * @see <a href="http://www.yaml.org/spec/1.1/#id927035"></a>
+     * @see <a href="http://www.yaml.org/spec/1.1/#id927557"></a>
+     */
+    private Chomping scanBlockScalarIndicators(Mark startMark) {
+        // See the specification for details.
+        Boolean chomping = null;
+        int increment = -1;
+        char ch = reader.peek();
+        if (ch == '-' || ch == '+') {
+            if (ch == '+') {
+                chomping = Boolean.TRUE;
+            } else {
+                chomping = Boolean.FALSE;
+            }
+            reader.forward();
+            ch = reader.peek();
+            if (Character.isDigit(ch)) {
+                increment = Integer.parseInt(String.valueOf(ch));
+                if (increment == 0) {
+                    throw new ScannerException("while scanning a block scalar", startMark,
+                            "expected indentation indicator in the range 1-9, but found 0",
+                            reader.getMark());
+                }
+                reader.forward();
+            }
+        } else if (Character.isDigit(ch)) {
+            increment = Integer.parseInt(String.valueOf(ch));
+            if (increment == 0) {
+                throw new ScannerException("while scanning a block scalar", startMark,
+                        "expected indentation indicator in the range 1-9, but found 0",
+                        reader.getMark());
+            }
+            reader.forward();
+            ch = reader.peek();
+            if (ch == '-' || ch == '+') {
+                if (ch == '+') {
+                    chomping = Boolean.TRUE;
+                } else {
+                    chomping = Boolean.FALSE;
+                }
+                reader.forward();
+            }
+        }
+        ch = reader.peek();
+        if (Constant.NULL_BL_LINEBR.hasNo(ch)) {
+            throw new ScannerException("while scanning a block scalar", startMark,
+                    "expected chomping or indentation indicators, but found " + ch,
+                    reader.getMark());
+        }
+        return new Chomping(chomping, increment);
+    }
+
+    /**
+     * Scan to the end of the line after a block scalar has been scanned; the
+     * only things that are permitted at this time are comments and spaces.
+     */
+    private String scanBlockScalarIgnoredLine(Mark startMark) {
+        // See the specification for details.
+        int ff = 0;
+        // Forward past any number of trailing spaces
+        while (reader.peek(ff) == ' ') {
+            ff++;
+        }
+        if (ff > 0) {
+            reader.forward(ff);
+        }
+        // If a comment occurs, scan to just before the end of line.
+        if (reader.peek() == '#') {
+            ff = 0;
+            while (Constant.NULL_OR_LINEBR.hasNo(reader.peek(ff))) {
+                ff++;
+            }
+            if (ff > 0) {
+                reader.forward(ff);
+            }
+        }
+        // If the next character is not a null or line break, an error has
+        // occurred.
+        char ch = reader.peek();
+        String lineBreak = scanLineBreak();
+        if (lineBreak.length() == 0 && ch != '\0') {
+            throw new ScannerException("while scanning a block scalar", startMark,
+                    "expected a comment or a line break, but found " + ch, reader.getMark());
+        }
+        return lineBreak;
+    }
+
+    /**
+     * Scans for the indentation of a block scalar implicitly. This mechanism is
+     * used only if the block did not explicitly state an indentation to be
+     * used.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#id927035"></a>
+     */
+    private Object[] scanBlockScalarIndentation() {
+        // See the specification for details.
+        StringBuilder chunks = new StringBuilder();
+        int maxIndent = 0;
+        Mark endMark = reader.getMark();
+        // Look ahead some number of lines until the first non-blank character
+        // occurs; the determined indentation will be the maximum number of
+        // leading spaces on any of these lines.
+        while (Constant.LINEBR.has(reader.peek(), " \r")) {
+            if (reader.peek() != ' ') {
+                // If the character isn't a space, it must be some kind of
+                // line-break; scan the line break and track it.
+                chunks.append(scanLineBreak());
+                endMark = reader.getMark();
+            } else {
+                // If the character is a space, move forward to the next
+                // character; if we surpass our previous maximum for indent
+                // level, update that too.
+                reader.forward();
+                if (this.reader.getColumn() > maxIndent) {
+                    maxIndent = reader.getColumn();
+                }
+            }
+        }
+        // Pass several results back together.
+        return new Object[] { chunks.toString(), maxIndent, endMark };
+    }
+
+    private Object[] scanBlockScalarBreaks(int indent) {
+        // See the specification for details.
+        StringBuilder chunks = new StringBuilder();
+        Mark endMark = reader.getMark();
+        int ff = 0;
+        int col = this.reader.getColumn();
+        // Scan for up to the expected indentation-level of spaces, then move
+        // forward past that amount.
+        while (col < indent && reader.peek(ff) == ' ') {
+            ff++;
+            col++;
+        }
+        if (ff > 0) {
+            reader.forward(ff);
+        }
+        // Consume one or more line breaks followed by any amount of spaces,
+        // until we find something that isn't a line-break.
+        String lineBreak = null;
+        while ((lineBreak = scanLineBreak()).length() != 0) {
+            chunks.append(lineBreak);
+            endMark = reader.getMark();
+            // Scan past up to (indent) spaces on the next line, then forward
+            // past them.
+            ff = 0;
+            col = this.reader.getColumn();
+            while (col < indent && reader.peek(ff) == ' ') {
+                ff++;
+                col++;
+            }
+            if (ff > 0) {
+                reader.forward(ff);
+            }
+        }
+        // Return both the assembled intervening string and the end-mark.
+        return new Object[] { chunks.toString(), endMark };
+    }
+
+    /**
+     * Scan a flow-style scalar. Flow scalars are presented in one of two forms;
+     * first, a flow scalar may be a double-quoted string; second, a flow scalar
+     * may be a single-quoted string.
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#flow"></a> style/syntax
+     * 
+     *      <pre>
+     * See the specification for details.
+     * Note that we loose indentation rules for quoted scalars. Quoted
+     * scalars don't need to adhere indentation because &quot; and ' clearly
+     * mark the beginning and the end of them. Therefore we are less
+     * restrictive then the specification requires. We only need to check
+     * that document separators are not included in scalars.
+     * </pre>
+     */
+    private Token scanFlowScalar(char style) {
+        boolean _double;
+        // The style will be either single- or double-quoted; we determine this
+        // by the first character in the entry (supplied)
+        if (style == '"') {
+            _double = true;
+        } else {
+            _double = false;
+        }
+        StringBuilder chunks = new StringBuilder();
+        Mark startMark = reader.getMark();
+        char quote = reader.peek();
+        reader.forward();
+        chunks.append(scanFlowScalarNonSpaces(_double, startMark));
+        while (reader.peek() != quote) {
+            chunks.append(scanFlowScalarSpaces(startMark));
+            chunks.append(scanFlowScalarNonSpaces(_double, startMark));
+        }
+        reader.forward();
+        Mark endMark = reader.getMark();
+        return new ScalarToken(chunks.toString(), false, startMark, endMark, style);
+    }
+
+    /**
+     * Scan some number of flow-scalar non-space characters.
+     */
+    private String scanFlowScalarNonSpaces(boolean doubleQuoted, Mark startMark) {
+        // See the specification for details.
+        StringBuilder chunks = new StringBuilder();
+        while (true) {
+            // Scan through any number of characters which are not: NUL, blank,
+            // tabs, line breaks, single-quotes, double-quotes, or backslashes.
+            int length = 0;
+            while (Constant.NULL_BL_T_LINEBR.hasNo(reader.peek(length), "\'\"\\")) {
+                length++;
+            }
+            if (length != 0) {
+                chunks.append(reader.prefixForward(length));
+            }
+            // Depending on our quoting-type, the characters ', " and \ have
+            // differing meanings.
+            char ch = reader.peek();
+            if (!doubleQuoted && ch == '\'' && reader.peek(1) == '\'') {
+                chunks.append("'");
+                reader.forward(2);
+            } else if ((doubleQuoted && ch == '\'') || (!doubleQuoted && "\"\\".indexOf(ch) != -1)) {
+                chunks.append(ch);
+                reader.forward();
+            } else if (doubleQuoted && ch == '\\') {
+                reader.forward();
+                ch = reader.peek();
+                if (ESCAPE_REPLACEMENTS.containsKey(Character.valueOf(ch))) {
+                    // The character is one of the single-replacement
+                    // types; these are replaced with a literal character
+                    // from the mapping.
+                    chunks.append(ESCAPE_REPLACEMENTS.get(Character.valueOf(ch)));
+                    reader.forward();
+                } else if (ESCAPE_CODES.containsKey(Character.valueOf(ch))) {
+                    // The character is a multi-digit escape sequence, with
+                    // length defined by the value in the ESCAPE_CODES map.
+                    length = ESCAPE_CODES.get(Character.valueOf(ch)).intValue();
+                    reader.forward();
+                    String hex = reader.prefix(length);
+                    if (NOT_HEXA.matcher(hex).find()) {
+                        throw new ScannerException("while scanning a double-quoted scalar",
+                                startMark, "expected escape sequence of " + length
+                                        + " hexadecimal numbers, but found: " + hex,
+                                reader.getMark());
+                    }
+                    int decimal = Integer.parseInt(hex, 16);
+                    String unicode = new String(Character.toChars(decimal));
+                    chunks.append(unicode);
+                    reader.forward(length);
+                } else if (scanLineBreak().length() != 0) {
+                    chunks.append(scanFlowScalarBreaks(startMark));
+                } else {
+                    throw new ScannerException("while scanning a double-quoted scalar", startMark,
+                            "found unknown escape character " + ch + "(" + ((int) ch) + ")",
+                            reader.getMark());
+                }
+            } else {
+                return chunks.toString();
+            }
+        }
+    }
+
+    private String scanFlowScalarSpaces(Mark startMark) {
+        // See the specification for details.
+        StringBuilder chunks = new StringBuilder();
+        int length = 0;
+        // Scan through any number of whitespace (space, tab) characters,
+        // consuming them.
+        while (" \t".indexOf(reader.peek(length)) != -1) {
+            length++;
+        }
+        String whitespaces = reader.prefixForward(length);
+        char ch = reader.peek();
+        if (ch == '\0') {
+            // A flow scalar cannot end with an end-of-stream
+            throw new ScannerException("while scanning a quoted scalar", startMark,
+                    "found unexpected end of stream", reader.getMark());
+        }
+        // If we encounter a line break, scan it into our assembled string...
+        String lineBreak = scanLineBreak();
+        if (lineBreak.length() != 0) {
+            String breaks = scanFlowScalarBreaks(startMark);
+            if (!"\n".equals(lineBreak)) {
+                chunks.append(lineBreak);
+            } else if (breaks.length() == 0) {
+                chunks.append(" ");
+            }
+            chunks.append(breaks);
+        } else {
+            chunks.append(whitespaces);
+        }
+        return chunks.toString();
+    }
+
+    private String scanFlowScalarBreaks(Mark startMark) {
+        // See the specification for details.
+        StringBuilder chunks = new StringBuilder();
+        while (true) {
+            // Instead of checking indentation, we check for document
+            // separators.
+            String prefix = reader.prefix(3);
+            if (("---".equals(prefix) || "...".equals(prefix))
+                    && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) {
+                throw new ScannerException("while scanning a quoted scalar", startMark,
+                        "found unexpected document separator", reader.getMark());
+            }
+            // Scan past any number of spaces and tabs, ignoring them
+            while (" \t".indexOf(reader.peek()) != -1) {
+                reader.forward();
+            }
+            // If we stopped at a line break, add that; otherwise, return the
+            // assembled set of scalar breaks.
+            String lineBreak = scanLineBreak();
+            if (lineBreak.length() != 0) {
+                chunks.append(lineBreak);
+            } else {
+                return chunks.toString();
+            }
+        }
+    }
+
+    /**
+     * Scan a plain scalar.
+     * 
+     * <pre>
+     * See the specification for details.
+     * We add an additional restriction for the flow context:
+     *   plain scalars in the flow context cannot contain ',', ':' and '?'.
+     * We also keep track of the `allow_simple_key` flag here.
+     * Indentation rules are loosed for the flow context.
+     * </pre>
+     */
+    private Token scanPlain() {
+        StringBuilder chunks = new StringBuilder();
+        Mark startMark = reader.getMark();
+        Mark endMark = startMark;
+        int indent = this.indent + 1;
+        String spaces = "";
+        while (true) {
+            char ch;
+            int length = 0;
+            // A comment indicates the end of the scalar.
+            if (reader.peek() == '#') {
+                break;
+            }
+            while (true) {
+                ch = reader.peek(length);
+                if (Constant.NULL_BL_T_LINEBR.has(ch)
+                        || (this.flowLevel == 0 && ch == ':' && Constant.NULL_BL_T_LINEBR
+                                .has(reader.peek(length + 1)))
+                        || (this.flowLevel != 0 && ",:?[]{}".indexOf(ch) != -1)) {
+                    break;
+                }
+                length++;
+            }
+            // It's not clear what we should do with ':' in the flow context.
+            if (this.flowLevel != 0 && ch == ':'
+                    && Constant.NULL_BL_T_LINEBR.hasNo(reader.peek(length + 1), ",[]{}")) {
+                reader.forward(length);
+                throw new ScannerException("while scanning a plain scalar", startMark,
+                        "found unexpected ':'", reader.getMark(),
+                        "Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details.");
+            }
+            if (length == 0) {
+                break;
+            }
+            this.allowSimpleKey = false;
+            chunks.append(spaces);
+            chunks.append(reader.prefixForward(length));
+            endMark = reader.getMark();
+            spaces = scanPlainSpaces();
+            // System.out.printf("spaces[%s]\n", spaces);
+            if (spaces.length() == 0 || reader.peek() == '#'
+                    || (this.flowLevel == 0 && this.reader.getColumn() < indent)) {
+                break;
+            }
+        }
+        return new ScalarToken(chunks.toString(), startMark, endMark, true);
+    }
+
+    /**
+     * See the specification for details. SnakeYAML and libyaml allow tabs
+     * inside plain scalar
+     */
+    private String scanPlainSpaces() {
+        int length = 0;
+        while (reader.peek(length) == ' ' || reader.peek(length) == '\t') {
+            length++;
+        }
+        String whitespaces = reader.prefixForward(length);
+        String lineBreak = scanLineBreak();
+        if (lineBreak.length() != 0) {
+            this.allowSimpleKey = true;
+            String prefix = reader.prefix(3);
+            if ("---".equals(prefix) || "...".equals(prefix)
+                    && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) {
+                return "";
+            }
+            StringBuilder breaks = new StringBuilder();
+            while (true) {
+                if (reader.peek() == ' ') {
+                    reader.forward();
+                } else {
+                    String lb = scanLineBreak();
+                    if (lb.length() != 0) {
+                        breaks.append(lb);
+                        prefix = reader.prefix(3);
+                        if ("---".equals(prefix) || "...".equals(prefix)
+                                && Constant.NULL_BL_T_LINEBR.has(reader.peek(3))) {
+                            return "";
+                        }
+                    } else {
+                        break;
+                    }
+                }
+            }
+            if (!"\n".equals(lineBreak)) {
+                return lineBreak + breaks;
+            } else if (breaks.length() == 0) {
+                return " ";
+            }
+            return breaks.toString();
+        }
+        return whitespaces;
+    }
+
+    /**
+     * <p>
+     * Scan a Tag handle. A Tag handle takes one of three forms:
+     * 
+     * <pre>
+     * "!" (c-primary-tag-handle)
+     * "!!" (ns-secondary-tag-handle)
+     * "!(name)!" (c-named-tag-handle)
+     * </pre>
+     * 
+     * Where (name) must be formatted as an ns-word-char.
+     * </p>
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#c-tag-handle"></a>
+     * @see <a href="http://www.yaml.org/spec/1.1/#ns-word-char"></a>
+     * 
+     *      <pre>
+     * See the specification for details.
+     * For some strange reasons, the specification does not allow '_' in
+     * tag handles. I have allowed it anyway.
+     * </pre>
+     */
+    private String scanTagHandle(String name, Mark startMark) {
+        char ch = reader.peek();
+        if (ch != '!') {
+            throw new ScannerException("while scanning a " + name, startMark,
+                    "expected '!', but found " + ch + "(" + ((int) ch) + ")", reader.getMark());
+        }
+        // Look for the next '!' in the stream, stopping if we hit a
+        // non-word-character. If the first character is a space, then the
+        // tag-handle is a c-primary-tag-handle ('!').
+        int length = 1;
+        ch = reader.peek(length);
+        if (ch != ' ') {
+            // Scan through 0+ alphabetic characters.
+            // FIXME According to the specification, these should be
+            // ns-word-char only, which prohibits '_'. This might be a
+            // candidate for a configuration option.
+            while (Constant.ALPHA.has(ch)) {
+                length++;
+                ch = reader.peek(length);
+            }
+            // Found the next non-word-char. If this is not a space and not an
+            // '!', then this is an error, as the tag-handle was specified as:
+            // !(name) or similar; the trailing '!' is missing.
+            if (ch != '!') {
+                reader.forward(length);
+                throw new ScannerException("while scanning a " + name, startMark,
+                        "expected '!', but found " + ch + "(" + ((int) ch) + ")", reader.getMark());
+            }
+            length++;
+        }
+        String value = reader.prefixForward(length);
+        return value;
+    }
+
+    /**
+     * <p>
+     * Scan a Tag URI. This scanning is valid for both local and global tag
+     * directives, because both appear to be valid URIs as far as scanning is
+     * concerned. The difference may be distinguished later, in parsing. This
+     * method will scan for ns-uri-char*, which covers both cases.
+     * </p>
+     * 
+     * <p>
+     * This method performs no verification that the scanned URI conforms to any
+     * particular kind of URI specification.
+     * </p>
+     * 
+     * @see <a href="http://www.yaml.org/spec/1.1/#ns-uri-char"></a>
+     */
+    private String scanTagUri(String name, Mark startMark) {
+        // See the specification for details.
+        // Note: we do not check if URI is well-formed.
+        StringBuilder chunks = new StringBuilder();
+        // Scan through accepted URI characters, which includes the standard
+        // URI characters, plus the start-escape character ('%'). When we get
+        // to a start-escape, scan the escaped sequence, then return.
+        int length = 0;
+        char ch = reader.peek(length);
+        while (Constant.URI_CHARS.has(ch)) {
+            if (ch == '%') {
+                chunks.append(reader.prefixForward(length));
+                length = 0;
+                chunks.append(scanUriEscapes(name, startMark));
+            } else {
+                length++;
+            }
+            ch = reader.peek(length);
+        }
+        // Consume the last "chunk", which would not otherwise be consumed by
+        // the loop above.
+        if (length != 0) {
+            chunks.append(reader.prefixForward(length));
+            length = 0;
+        }
+        if (chunks.length() == 0) {
+            // If no URI was found, an error has occurred.
+            throw new ScannerException("while scanning a " + name, startMark,
+                    "expected URI, but found " + ch + "(" + ((int) ch) + ")", reader.getMark());
+        }
+        return chunks.toString();
+    }
+
+    /**
+     * <p>
+     * Scan a sequence of %-escaped URI escape codes and convert them into a
+     * String representing the unescaped values.
+     * </p>
+     * 
+     * FIXME This method fails for more than 256 bytes' worth of URI-encoded
+     * characters in a row. Is this possible? Is this a use-case?
+     * 
+     * @see <a href="http://www.ietf.org/rfc/rfc2396.txt"></a>, section 2.4, Escaped Encoding.
+     */
+    private String scanUriEscapes(String name, Mark startMark) {
+        // First, look ahead to see how many URI-escaped characters we should
+        // expect, so we can use the correct buffer size.
+        int length = 1;
+        while (reader.peek(length * 3) == '%') {
+            length++;
+        }
+        // See the specification for details.
+        // URIs containing 16 and 32 bit Unicode characters are
+        // encoded in UTF-8, and then each octet is written as a
+        // separate character.
+        Mark beginningMark = reader.getMark();
+        ByteBuffer buff = ByteBuffer.allocate(length);
+        while (reader.peek() == '%') {
+            reader.forward();
+            try {
+                byte code = (byte) Integer.parseInt(reader.prefix(2), 16);
+                buff.put(code);
+            } catch (NumberFormatException nfe) {
+                throw new ScannerException("while scanning a " + name, startMark,
+                        "expected URI escape sequence of 2 hexadecimal numbers, but found "
+                                + reader.peek() + "(" + ((int) reader.peek()) + ") and "
+                                + reader.peek(1) + "(" + ((int) reader.peek(1)) + ")",
+                        reader.getMark());
+            }
+            reader.forward(2);
+        }
+        buff.flip();
+        try {
+            return UriEncoder.decode(buff);
+        } catch (CharacterCodingException e) {
+            throw new ScannerException("while scanning a " + name, startMark,
+                    "expected URI in UTF-8: " + e.getMessage(), beginningMark);
+        }
+    }
+
+    /**
+     * Scan a line break, transforming:
+     * 
+     * <pre>
+     * '\r\n' : '\n'
+     * '\r' : '\n'
+     * '\n' : '\n'
+     * '\x85' : '\n'
+     * default : ''
+     * </pre>
+     */
+    private String scanLineBreak() {
+        // Transforms:
+        // '\r\n' : '\n'
+        // '\r' : '\n'
+        // '\n' : '\n'
+        // '\x85' : '\n'
+        // default : ''
+        char ch = reader.peek();
+        if (ch == '\r' || ch == '\n' || ch == '\u0085') {
+            if (ch == '\r' && '\n' == reader.peek(1)) {
+                reader.forward(2);
+            } else {
+                reader.forward();
+            }
+            return "\n";
+        } else if (ch == '\u2028' || ch == '\u2029') {
+            reader.forward();
+            return String.valueOf(ch);
+        }
+        return "";
+    }
+
+    /**
+     * Chomping the tail may have 3 values - yes, no, not defined.
+     */
+    private static class Chomping {
+        private final Boolean value;
+        private final int increment;
+
+        public Chomping(Boolean value, int increment) {
+            this.value = value;
+            this.increment = increment;
+        }
+
+        public boolean chompTailIsNotFalse() {
+            return value == null || value;
+        }
+
+        public boolean chompTailIsTrue() {
+            return value != null && value;
+        }
+
+        public int getIncrement() {
+            return increment;
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/scanner/SimpleKey.java b/src/main/java/org/yaml/snakeyaml/scanner/SimpleKey.java
new file mode 100644
index 0000000..3fe710c
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/scanner/SimpleKey.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * Simple keys treatment.
+ * <p>
+ * Helper class for {@link ScannerImpl}.
+ * </p>
+ * 
+ * @see ScannerImpl
+ */
+final class SimpleKey {
+    private int tokenNumber;
+    private boolean required;
+    private int index;
+    private int line;
+    private int column;
+    private Mark mark;
+
+    public SimpleKey(int tokenNumber, boolean required, int index, int line, int column, Mark mark) {
+        this.tokenNumber = tokenNumber;
+        this.required = required;
+        this.index = index;
+        this.line = line;
+        this.column = column;
+        this.mark = mark;
+    }
+
+    public int getTokenNumber() {
+        return this.tokenNumber;
+    }
+
+    public int getColumn() {
+        return this.column;
+    }
+
+    public Mark getMark() {
+        return mark;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public int getLine() {
+        return line;
+    }
+
+    public boolean isRequired() {
+        return required;
+    }
+
+    @Override
+    public String toString() {
+        return "SimpleKey - tokenNumber=" + tokenNumber + " required=" + required + " index="
+                + index + " line=" + line + " column=" + column;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/serializer/AnchorGenerator.java b/src/main/java/org/yaml/snakeyaml/serializer/AnchorGenerator.java
new file mode 100644
index 0000000..2308eb5
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/serializer/AnchorGenerator.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import org.yaml.snakeyaml.nodes.Node;
+
+public interface AnchorGenerator {
+
+    String nextAnchor(Node node);
+}
diff --git a/src/main/java/org/yaml/snakeyaml/serializer/NumberAnchorGenerator.java b/src/main/java/org/yaml/snakeyaml/serializer/NumberAnchorGenerator.java
new file mode 100644
index 0000000..2f316f8
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/serializer/NumberAnchorGenerator.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import org.yaml.snakeyaml.nodes.Node;
+
+import java.text.NumberFormat;
+
+public class NumberAnchorGenerator implements AnchorGenerator {
+
+    private int lastAnchorId = 0;
+
+    public NumberAnchorGenerator(int lastAnchorId) {
+        this.lastAnchorId = lastAnchorId;
+    }
+
+    public String nextAnchor(Node node) {
+        this.lastAnchorId++;
+        NumberFormat format = NumberFormat.getNumberInstance();
+        format.setMinimumIntegerDigits(3);
+        format.setMaximumFractionDigits(0);// issue 172
+        format.setGroupingUsed(false);
+        String anchorId = format.format(this.lastAnchorId);
+        return "id" + anchorId;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java b/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java
new file mode 100644
index 0000000..2decf3f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/serializer/Serializer.java
@@ -0,0 +1,201 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.emitter.Emitable;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.nodes.AnchorNode;
+import org.yaml.snakeyaml.nodes.CollectionNode;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public final class Serializer {
+    private final Emitable emitter;
+    private final Resolver resolver;
+    private boolean explicitStart;
+    private boolean explicitEnd;
+    private Version useVersion;
+    private Map<String, String> useTags;
+    private Set<Node> serializedNodes;
+    private Map<Node, String> anchors;
+    private AnchorGenerator anchorGenerator;
+    private Boolean closed;
+    private Tag explicitRoot;
+
+    public Serializer(Emitable emitter, Resolver resolver, DumperOptions opts, Tag rootTag) {
+        this.emitter = emitter;
+        this.resolver = resolver;
+        this.explicitStart = opts.isExplicitStart();
+        this.explicitEnd = opts.isExplicitEnd();
+        if (opts.getVersion() != null) {
+            this.useVersion = opts.getVersion();
+        }
+        this.useTags = opts.getTags();
+        this.serializedNodes = new HashSet<Node>();
+        this.anchors = new HashMap<Node, String>();
+        this.anchorGenerator = opts.getAnchorGenerator();
+        this.closed = null;
+        this.explicitRoot = rootTag;
+    }
+
+    public void open() throws IOException {
+        if (closed == null) {
+            this.emitter.emit(new StreamStartEvent(null, null));
+            this.closed = Boolean.FALSE;
+        } else if (Boolean.TRUE.equals(closed)) {
+            throw new SerializerException("serializer is closed");
+        } else {
+            throw new SerializerException("serializer is already opened");
+        }
+    }
+
+    public void close() throws IOException {
+        if (closed == null) {
+            throw new SerializerException("serializer is not opened");
+        } else if (!Boolean.TRUE.equals(closed)) {
+            this.emitter.emit(new StreamEndEvent(null, null));
+            this.closed = Boolean.TRUE;
+        }
+    }
+
+    public void serialize(Node node) throws IOException {
+        if (closed == null) {
+            throw new SerializerException("serializer is not opened");
+        } else if (closed) {
+            throw new SerializerException("serializer is closed");
+        }
+        this.emitter.emit(new DocumentStartEvent(null, null, this.explicitStart, this.useVersion,
+                useTags));
+        anchorNode(node);
+        if (explicitRoot != null) {
+            node.setTag(explicitRoot);
+        }
+        serializeNode(node, null);
+        this.emitter.emit(new DocumentEndEvent(null, null, this.explicitEnd));
+        this.serializedNodes.clear();
+        this.anchors.clear();
+    }
+
+    private void anchorNode(Node node) {
+        if (node.getNodeId() == NodeId.anchor) {
+            node = ((AnchorNode) node).getRealNode();
+        }
+        if (this.anchors.containsKey(node)) {
+            String anchor = this.anchors.get(node);
+            if (null == anchor) {
+                anchor = this.anchorGenerator.nextAnchor(node);
+                this.anchors.put(node, anchor);
+            }
+        } else {
+            this.anchors.put(node, null);
+            switch (node.getNodeId()) {
+            case sequence:
+                SequenceNode seqNode = (SequenceNode) node;
+                List<Node> list = seqNode.getValue();
+                for (Node item : list) {
+                    anchorNode(item);
+                }
+                break;
+            case mapping:
+                MappingNode mnode = (MappingNode) node;
+                List<NodeTuple> map = mnode.getValue();
+                for (NodeTuple object : map) {
+                    Node key = object.getKeyNode();
+                    Node value = object.getValueNode();
+                    anchorNode(key);
+                    anchorNode(value);
+                }
+                break;
+            }
+        }
+    }
+
+    private void serializeNode(Node node, Node parent) throws IOException {
+        if (node.getNodeId() == NodeId.anchor) {
+            node = ((AnchorNode) node).getRealNode();
+        }
+        String tAlias = this.anchors.get(node);
+        if (this.serializedNodes.contains(node)) {
+            this.emitter.emit(new AliasEvent(tAlias, null, null));
+        } else {
+            this.serializedNodes.add(node);
+            switch (node.getNodeId()) {
+            case scalar:
+                ScalarNode scalarNode = (ScalarNode) node;
+                Tag detectedTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), true);
+                Tag defaultTag = this.resolver.resolve(NodeId.scalar, scalarNode.getValue(), false);
+                ImplicitTuple tuple = new ImplicitTuple(node.getTag().equals(detectedTag), node
+                        .getTag().equals(defaultTag));
+                ScalarEvent event = new ScalarEvent(tAlias, node.getTag().getValue(), tuple,
+                        scalarNode.getValue(), null, null, scalarNode.getStyle());
+                this.emitter.emit(event);
+                break;
+            case sequence:
+                SequenceNode seqNode = (SequenceNode) node;
+                boolean implicitS = node.getTag().equals(this.resolver.resolve(NodeId.sequence,
+                        null, true));
+                this.emitter.emit(new SequenceStartEvent(tAlias, node.getTag().getValue(),
+                        implicitS, null, null, seqNode.getFlowStyle()));
+                List<Node> list = seqNode.getValue();
+                for (Node item : list) {
+                    serializeNode(item, node);
+                }
+                this.emitter.emit(new SequenceEndEvent(null, null));
+                break;
+            default:// instance of MappingNode
+                Tag implicitTag = this.resolver.resolve(NodeId.mapping, null, true);
+                boolean implicitM = node.getTag().equals(implicitTag);
+                this.emitter.emit(new MappingStartEvent(tAlias, node.getTag().getValue(),
+                        implicitM, null, null, ((CollectionNode) node).getFlowStyle()));
+                MappingNode mnode = (MappingNode) node;
+                List<NodeTuple> map = mnode.getValue();
+                for (NodeTuple row : map) {
+                    Node key = row.getKeyNode();
+                    Node value = row.getValueNode();
+                    serializeNode(key, mnode);
+                    serializeNode(value, mnode);
+                }
+                this.emitter.emit(new MappingEndEvent(null, null));
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/serializer/SerializerException.java b/src/main/java/org/yaml/snakeyaml/serializer/SerializerException.java
new file mode 100644
index 0000000..0cb6e88
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/serializer/SerializerException.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class SerializerException extends YAMLException {
+    private static final long serialVersionUID = 2632638197498912433L;
+
+    public SerializerException(String message) {
+        super(message);
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/AliasToken.java b/src/main/java/org/yaml/snakeyaml/tokens/AliasToken.java
new file mode 100644
index 0000000..df2ee2e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/AliasToken.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class AliasToken extends Token {
+    private final String value;
+
+    public AliasToken(String value, Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+        this.value = value;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+
+    @Override
+    protected String getArguments() {
+        return "value=" + value;
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Alias;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/AnchorToken.java b/src/main/java/org/yaml/snakeyaml/tokens/AnchorToken.java
new file mode 100644
index 0000000..3629eea
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/AnchorToken.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class AnchorToken extends Token {
+    private final String value;
+
+    public AnchorToken(String value, Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+        this.value = value;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+
+    @Override
+    protected String getArguments() {
+        return "value=" + value;
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Anchor;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/BlockEndToken.java b/src/main/java/org/yaml/snakeyaml/tokens/BlockEndToken.java
new file mode 100644
index 0000000..3315bc4
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/BlockEndToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class BlockEndToken extends Token {
+
+    public BlockEndToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.BlockEnd;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/BlockEntryToken.java b/src/main/java/org/yaml/snakeyaml/tokens/BlockEntryToken.java
new file mode 100644
index 0000000..574445f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/BlockEntryToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class BlockEntryToken extends Token {
+
+    public BlockEntryToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.BlockEntry;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/BlockMappingStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/BlockMappingStartToken.java
new file mode 100644
index 0000000..95a6164
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/BlockMappingStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class BlockMappingStartToken extends Token {
+
+    public BlockMappingStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.BlockMappingStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/BlockSequenceStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/BlockSequenceStartToken.java
new file mode 100644
index 0000000..d70194c
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/BlockSequenceStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class BlockSequenceStartToken extends Token {
+
+    public BlockSequenceStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.BlockSequenceStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java b/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java
new file mode 100644
index 0000000..12c067e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/CommentToken.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public class CommentToken extends Token {
+    public CommentToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public ID getTokenId() {
+        return ID.Comment;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/DirectiveToken.java b/src/main/java/org/yaml/snakeyaml/tokens/DirectiveToken.java
new file mode 100644
index 0000000..af1743f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/DirectiveToken.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import java.util.List;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public final class DirectiveToken<T> extends Token {
+    private final String name;
+    private final List<T> value;
+
+    public DirectiveToken(String name, List<T> value, Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+        this.name = name;
+        if (value != null && value.size() != 2) {
+            throw new YAMLException("Two strings must be provided instead of "
+                    + String.valueOf(value.size()));
+        }
+        this.value = value;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public List<T> getValue() {
+        return this.value;
+    }
+
+    @Override
+    protected String getArguments() {
+        if (value != null) {
+            return "name=" + name + ", value=[" + value.get(0) + ", " + value.get(1) + "]";
+        } else {
+            return "name=" + name;
+        }
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Directive;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/DocumentEndToken.java b/src/main/java/org/yaml/snakeyaml/tokens/DocumentEndToken.java
new file mode 100644
index 0000000..ee17dab
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/DocumentEndToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class DocumentEndToken extends Token {
+
+    public DocumentEndToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.DocumentEnd;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/DocumentStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/DocumentStartToken.java
new file mode 100644
index 0000000..0b72deb
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/DocumentStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class DocumentStartToken extends Token {
+
+    public DocumentStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.DocumentStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/FlowEntryToken.java b/src/main/java/org/yaml/snakeyaml/tokens/FlowEntryToken.java
new file mode 100644
index 0000000..b1afb0f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/FlowEntryToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class FlowEntryToken extends Token {
+
+    public FlowEntryToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.FlowEntry;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingEndToken.java b/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingEndToken.java
new file mode 100644
index 0000000..1659a9f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingEndToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class FlowMappingEndToken extends Token {
+
+    public FlowMappingEndToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.FlowMappingEnd;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingStartToken.java
new file mode 100644
index 0000000..5a984c7
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/FlowMappingStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class FlowMappingStartToken extends Token {
+
+    public FlowMappingStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.FlowMappingStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceEndToken.java b/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceEndToken.java
new file mode 100644
index 0000000..39b03c4
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceEndToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class FlowSequenceEndToken extends Token {
+
+    public FlowSequenceEndToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.FlowSequenceEnd;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceStartToken.java
new file mode 100644
index 0000000..da89785
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/FlowSequenceStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class FlowSequenceStartToken extends Token {
+
+    public FlowSequenceStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.FlowSequenceStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/KeyToken.java b/src/main/java/org/yaml/snakeyaml/tokens/KeyToken.java
new file mode 100644
index 0000000..0f88043
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/KeyToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class KeyToken extends Token {
+
+    public KeyToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Key;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/ScalarToken.java b/src/main/java/org/yaml/snakeyaml/tokens/ScalarToken.java
new file mode 100644
index 0000000..828189e
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/ScalarToken.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class ScalarToken extends Token {
+    private final String value;
+    private final boolean plain;
+    private final char style;
+
+    public ScalarToken(String value, Mark startMark, Mark endMark, boolean plain) {
+        this(value, plain, startMark, endMark, (char) 0);
+    }
+
+    public ScalarToken(String value, boolean plain, Mark startMark, Mark endMark, char style) {
+        super(startMark, endMark);
+        this.value = value;
+        this.plain = plain;
+        this.style = style;
+    }
+
+    public boolean getPlain() {
+        return this.plain;
+    }
+
+    public String getValue() {
+        return this.value;
+    }
+
+    public char getStyle() {
+        return this.style;
+    }
+
+    @Override
+    protected String getArguments() {
+        return "value=" + value + ", plain=" + plain + ", style=" + style;
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Scalar;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/StreamEndToken.java b/src/main/java/org/yaml/snakeyaml/tokens/StreamEndToken.java
new file mode 100644
index 0000000..ece87b9
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/StreamEndToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class StreamEndToken extends Token {
+
+    public StreamEndToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.StreamEnd;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/StreamStartToken.java b/src/main/java/org/yaml/snakeyaml/tokens/StreamStartToken.java
new file mode 100644
index 0000000..4b5419a
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/StreamStartToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class StreamStartToken extends Token {
+
+    public StreamStartToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.StreamStart;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/TagToken.java b/src/main/java/org/yaml/snakeyaml/tokens/TagToken.java
new file mode 100644
index 0000000..505a360
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/TagToken.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class TagToken extends Token {
+    private final TagTuple value;
+
+    public TagToken(TagTuple value, Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+        this.value = value;
+    }
+
+    public TagTuple getValue() {
+        return this.value;
+    }
+
+    @Override
+    protected String getArguments() {
+        return "value=[" + value.getHandle() + ", " + value.getSuffix() + "]";
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Tag;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/TagTuple.java b/src/main/java/org/yaml/snakeyaml/tokens/TagTuple.java
new file mode 100644
index 0000000..b4ea064
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/TagTuple.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+public final class TagTuple {
+    private final String handle;
+    private final String suffix;
+
+    public TagTuple(String handle, String suffix) {
+        if (suffix == null) {
+            throw new NullPointerException("Suffix must be provided.");
+        }
+        this.handle = handle;
+        this.suffix = suffix;
+    }
+
+    public String getHandle() {
+        return handle;
+    }
+
+    public String getSuffix() {
+        return suffix;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/Token.java b/src/main/java/org/yaml/snakeyaml/tokens/Token.java
new file mode 100644
index 0000000..8b583f5
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/Token.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public abstract class Token {
+    public enum ID {
+        Alias, Anchor, BlockEnd, BlockEntry, BlockMappingStart, BlockSequenceStart, Directive, DocumentEnd, DocumentStart, FlowEntry, FlowMappingEnd, FlowMappingStart, FlowSequenceEnd, FlowSequenceStart, Key, Scalar, StreamEnd, StreamStart, Tag, Value, Whitespace, Comment, Error
+    }
+
+    private final Mark startMark;
+    private final Mark endMark;
+
+    public Token(Mark startMark, Mark endMark) {
+        if (startMark == null || endMark == null) {
+            throw new YAMLException("Token requires marks.");
+        }
+        this.startMark = startMark;
+        this.endMark = endMark;
+    }
+
+    public String toString() {
+        return "<" + this.getClass().getName() + "(" + getArguments() + ")>";
+    }
+
+    public Mark getStartMark() {
+        return startMark;
+    }
+
+    public Mark getEndMark() {
+        return endMark;
+    }
+
+    /**
+     * @see "__repr__ for Token in PyYAML"
+     */
+    protected String getArguments() {
+        return "";
+    }
+
+    /**
+     * For error reporting.
+     * 
+     * @see "class variable 'id' in PyYAML"
+     */
+    public abstract Token.ID getTokenId();
+
+    /*
+     * for tests only
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Token) {
+            return toString().equals(obj.toString());
+        } else {
+            return false;
+        }
+    }
+
+    /*
+     * for tests only
+     */
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/ValueToken.java b/src/main/java/org/yaml/snakeyaml/tokens/ValueToken.java
new file mode 100644
index 0000000..58fe057
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/ValueToken.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public final class ValueToken extends Token {
+
+    public ValueToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public Token.ID getTokenId() {
+        return ID.Value;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/tokens/WhitespaceToken.java b/src/main/java/org/yaml/snakeyaml/tokens/WhitespaceToken.java
new file mode 100644
index 0000000..65af212
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/tokens/WhitespaceToken.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public class WhitespaceToken extends Token {
+    public WhitespaceToken(Mark startMark, Mark endMark) {
+        super(startMark, endMark);
+    }
+
+    @Override
+    public ID getTokenId() {
+        return ID.Whitespace;
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/util/ArrayStack.java b/src/main/java/org/yaml/snakeyaml/util/ArrayStack.java
new file mode 100644
index 0000000..4bab182
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/util/ArrayStack.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.util;
+
+import java.util.ArrayList;
+
+public class ArrayStack<T> {
+    private ArrayList<T> stack;
+
+    public ArrayStack(int initSize) {
+        stack = new ArrayList<T>(initSize);
+    }
+
+    public void push(T obj) {
+        stack.add(obj);
+    }
+
+    public T pop() {
+        return stack.remove(stack.size() - 1);
+    }
+
+    public boolean isEmpty() {
+        return stack.isEmpty();
+    }
+
+    public void clear() {
+        stack.clear();
+    }
+}
diff --git a/src/main/java/org/yaml/snakeyaml/util/UriEncoder.java b/src/main/java/org/yaml/snakeyaml/util/UriEncoder.java
new file mode 100644
index 0000000..e23904f
--- /dev/null
+++ b/src/main/java/org/yaml/snakeyaml/util/UriEncoder.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.external.com.google.gdata.util.common.base.Escaper;
+import org.yaml.snakeyaml.external.com.google.gdata.util.common.base.PercentEscaper;
+
+public abstract class UriEncoder {
+    private static final CharsetDecoder UTF8Decoder = Charset.forName("UTF-8").newDecoder()
+            .onMalformedInput(CodingErrorAction.REPORT);
+    // Include the [] chars to the SAFEPATHCHARS_URLENCODER to avoid
+    // its escape as required by spec. See
+    // http://yaml.org/spec/1.1/#escaping%20in%20URI/
+    private static final String SAFE_CHARS = PercentEscaper.SAFEPATHCHARS_URLENCODER + "[]/";
+    private static final Escaper escaper = new PercentEscaper(SAFE_CHARS, false);
+
+    /**
+     * Escape special characters with '%'
+     */
+    public static String encode(String uri) {
+        return escaper.escape(uri);
+    }
+
+    /**
+     * Decode '%'-escaped characters. Decoding fails in case of invalid UTF-8
+     */
+    public static String decode(ByteBuffer buff) throws CharacterCodingException {
+        CharBuffer chars = UTF8Decoder.decode(buff);
+        return chars.toString();
+    }
+
+    public static String decode(String buff) {
+        try {
+            return URLDecoder.decode(buff, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new YAMLException(e);
+        }
+    }
+}
diff --git a/src/patches/android/CompactConstructor.patch b/src/patches/android/CompactConstructor.patch
new file mode 100644
index 0000000..c935ecd
--- /dev/null
+++ b/src/patches/android/CompactConstructor.patch
@@ -0,0 +1,22 @@
+# HG changeset patch
+# Parent 802af3ea9a2228c074259858b737e2cda0d22a7c
+diff --git a/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java
+--- a/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java
++++ b/src/main/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructor.java
+@@ -15,7 +15,6 @@
+  */
+ package org.yaml.snakeyaml.extensions.compactnotation;
+ 
+-import java.beans.IntrospectionException;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.List;
+@@ -186,7 +185,7 @@
+      * 
+      * @throws IntrospectionException
+      */
+-    protected String getSequencePropertyName(Class<?> bean) throws IntrospectionException {
++    protected String getSequencePropertyName(Class<?> bean) {
+         Set<Property> properties = getPropertyUtils().getProperties(bean);
+         for (Iterator<Property> iterator = properties.iterator(); iterator.hasNext();) {
+             Property property = iterator.next();
diff --git a/src/patches/android/Constructor.patch b/src/patches/android/Constructor.patch
new file mode 100644
index 0000000..1588666
--- /dev/null
+++ b/src/patches/android/Constructor.patch
@@ -0,0 +1,24 @@
+# HG changeset patch
+# Parent 6211618dbbb85e16ccdfea2c1c68e098ac1bc333
+
+diff --git a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+--- a/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
++++ b/src/main/java/org/yaml/snakeyaml/constructor/Constructor.java
+@@ -15,7 +15,6 @@
+  */
+ package org.yaml.snakeyaml.constructor;
+ 
+-import java.beans.IntrospectionException;
+ import java.math.BigDecimal;
+ import java.math.BigInteger;
+ import java.util.ArrayList;
+@@ -303,8 +302,7 @@
+             return object;
+         }
+ 
+-        protected Property getProperty(Class<? extends Object> type, String name)
+-                throws IntrospectionException {
++        protected Property getProperty(Class<? extends Object> type, String name) {
+             return getPropertyUtils().getProperty(type, name);
+         }
+     }
diff --git a/src/patches/android/PropertyUtils.patch b/src/patches/android/PropertyUtils.patch
new file mode 100644
index 0000000..dc72ef3
--- /dev/null
+++ b/src/patches/android/PropertyUtils.patch
@@ -0,0 +1,118 @@
+# HG changeset patch
+# Parent 698e9f1d6348fc1066ceaac7d264cbbf63bdcd21
+diff --git a/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
+--- a/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
++++ b/src/main/java/org/yaml/snakeyaml/introspector/PropertyUtils.java
+@@ -15,11 +15,7 @@
+  */
+ package org.yaml.snakeyaml.introspector;
+ 
+-import java.beans.IntrospectionException;
+-import java.beans.Introspector;
+-import java.beans.PropertyDescriptor;
+ import java.lang.reflect.Field;
+-import java.lang.reflect.Method;
+ import java.lang.reflect.Modifier;
+ import java.util.Collection;
+ import java.util.HashMap;
+@@ -37,64 +33,31 @@
+     private BeanAccess beanAccess = BeanAccess.DEFAULT;
+     private boolean allowReadOnlyProperties = false;
+ 
+-    protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess)
+-            throws IntrospectionException {
++    protected Map<String, Property> getPropertiesMap(Class<?> type, BeanAccess bAccess) {
+         if (propertiesCache.containsKey(type)) {
+             return propertiesCache.get(type);
+         }
+ 
+         Map<String, Property> properties = new LinkedHashMap<String, Property>();
+-        boolean inaccessableFieldsExist = false;
+-        switch (bAccess) {
+-        case FIELD:
+-            for (Class<?> c = type; c != null; c = c.getSuperclass()) {
+-                for (Field field : c.getDeclaredFields()) {
+-                    int modifiers = field.getModifiers();
+-                    if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
+-                            && !properties.containsKey(field.getName())) {
+-                        properties.put(field.getName(), new FieldProperty(field));
+-                    }
++        for (Class<?> c = type; c != null; c = c.getSuperclass()) {
++            for (Field field : c.getDeclaredFields()) {
++                int modifiers = field.getModifiers();
++                if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)
++                        && !properties.containsKey(field.getName())) {
++                    properties.put(field.getName(), new FieldProperty(field));
+                 }
+             }
+-            break;
+-        default:
+-            // add JavaBean properties
+-            for (PropertyDescriptor property : Introspector.getBeanInfo(type)
+-                    .getPropertyDescriptors()) {
+-                Method readMethod = property.getReadMethod();
+-                if (readMethod == null || !readMethod.getName().equals("getClass")) {
+-                    properties.put(property.getName(), new MethodProperty(property));
+-                }
+-            }
++        }
+ 
+-            // add public fields
+-            for (Class<?> c = type; c != null; c = c.getSuperclass()) {
+-                for (Field field : c.getDeclaredFields()) {
+-                    int modifiers = field.getModifiers();
+-                    if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
+-                        if (Modifier.isPublic(modifiers)) {
+-                            properties.put(field.getName(), new FieldProperty(field));
+-                        } else {
+-                            inaccessableFieldsExist = true;
+-                        }
+-                    }
+-                }
+-            }
+-            break;
+-        }
+-        if (properties.isEmpty() && inaccessableFieldsExist) {
+-            throw new YAMLException("No JavaBean properties found in " + type.getName());
+-        }
+         propertiesCache.put(type, properties);
+         return properties;
+     }
+ 
+-    public Set<Property> getProperties(Class<? extends Object> type) throws IntrospectionException {
++    public Set<Property> getProperties(Class<? extends Object> type) {
+         return getProperties(type, beanAccess);
+     }
+ 
+-    public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess)
+-            throws IntrospectionException {
++    public Set<Property> getProperties(Class<? extends Object> type, BeanAccess bAccess) {
+         if (readableProperties.containsKey(type)) {
+             return readableProperties.get(type);
+         }
+@@ -103,8 +66,7 @@
+         return properties;
+     }
+ 
+-    protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+-            throws IntrospectionException {
++    protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess) {
+         Set<Property> properties = new TreeSet<Property>();
+         Collection<Property> props = getPropertiesMap(type, bAccess).values();
+         for (Property property : props) {
+@@ -115,13 +77,11 @@
+         return properties;
+     }
+ 
+-    public Property getProperty(Class<? extends Object> type, String name)
+-            throws IntrospectionException {
++    public Property getProperty(Class<? extends Object> type, String name) {
+         return getProperty(type, name, beanAccess);
+     }
+ 
+-    public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess)
+-            throws IntrospectionException {
++    public Property getProperty(Class<? extends Object> type, String name, BeanAccess bAccess) {
+         Map<String, Property> properties = getPropertiesMap(type, bAccess);
+         Property property = properties.get(name);
+         if (property == null || !property.isWritable()) {
diff --git a/src/patches/android/Representer.patch b/src/patches/android/Representer.patch
new file mode 100644
index 0000000..187853e
--- /dev/null
+++ b/src/patches/android/Representer.patch
@@ -0,0 +1,44 @@
+# HG changeset patch
+# Parent 44449b0d7ff73bbda13c28f2ebd4e8257c67a181
+diff --git a/src/main/java/org/yaml/snakeyaml/representer/Representer.java b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+--- a/src/main/java/org/yaml/snakeyaml/representer/Representer.java
++++ b/src/main/java/org/yaml/snakeyaml/representer/Representer.java
+@@ -15,7 +15,6 @@
+  */
+ package org.yaml.snakeyaml.representer;
+ 
+-import java.beans.IntrospectionException;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.Iterator;
+@@ -24,7 +23,6 @@
+ import java.util.Set;
+ 
+ import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+-import org.yaml.snakeyaml.error.YAMLException;
+ import org.yaml.snakeyaml.introspector.Property;
+ import org.yaml.snakeyaml.nodes.MappingNode;
+ import org.yaml.snakeyaml.nodes.Node;
+@@ -45,11 +43,7 @@
+ 
+     protected class RepresentJavaBean implements Represent {
+         public Node representData(Object data) {
+-            try {
+-                return representJavaBean(getProperties(data.getClass()), data);
+-            } catch (IntrospectionException e) {
+-                throw new YAMLException(e);
+-            }
++            return representJavaBean(getProperties(data.getClass()), data);
+         }
+     }
+ 
+@@ -233,8 +227,7 @@
+      *            - JavaBean to inspect the properties
+      * @return properties to serialise
+      */
+-    protected Set<Property> getProperties(Class<? extends Object> type)
+-            throws IntrospectionException {
++    protected Set<Property> getProperties(Class<? extends Object> type) {
+         return getPropertyUtils().getProperties(type);
+     }
+ }
diff --git a/src/test/java/biz/source_code/base64Coder/Base64CoderTest.java b/src/test/java/biz/source_code/base64Coder/Base64CoderTest.java
new file mode 100644
index 0000000..60f6d84
--- /dev/null
+++ b/src/test/java/biz/source_code/base64Coder/Base64CoderTest.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package biz.source_code.base64Coder;
+
+import java.io.UnsupportedEncodingException;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+
+public class Base64CoderTest extends TestCase {
+
+    public void testDecode() throws UnsupportedEncodingException {
+        check("Aladdin:open sesame", "QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
+        check("a", "YQ==");
+        check("aa", "YWE=");
+        check("a=", "YT0=");
+        check("", "");
+    }
+
+    public void testFailure1() throws UnsupportedEncodingException {
+        try {
+            Base64Coder.decode("YQ=".toCharArray());
+            fail();
+        } catch (Exception e) {
+            assertEquals("Length of Base64 encoded input string is not a multiple of 4.",
+                    e.getMessage());
+        }
+    }
+
+    public void testFailure2() throws UnsupportedEncodingException {
+        checkInvalid("\tWE=");
+        checkInvalid("Y\tE=");
+        checkInvalid("YW\t=");
+        checkInvalid("YWE\t");
+        //
+        checkInvalid("©WE=");
+        checkInvalid("Y©E=");
+        checkInvalid("YW©=");
+        checkInvalid("YWE©");
+    }
+
+    private void checkInvalid(String encoded) {
+        try {
+            Base64Coder.decode(encoded.toCharArray());
+            fail("Illegal chanracter.");
+        } catch (Exception e) {
+            assertEquals("Illegal character in Base64 encoded data.", e.getMessage());
+        }
+    }
+
+    private void check(String text, String encoded) throws UnsupportedEncodingException {
+        char[] s1 = Base64Coder.encode(text.getBytes("UTF-8"));
+        String t1 = new String(s1);
+        assertEquals(encoded, t1);
+        byte[] s2 = Base64Coder.decode(encoded.toCharArray());
+        String t2 = new String(s2, "UTF-8");
+        assertEquals(text, t2);
+    }
+}
diff --git a/src/test/java/examples/AnyObjectExampleTest.java b/src/test/java/examples/AnyObjectExampleTest.java
new file mode 100644
index 0000000..10ca00c
--- /dev/null
+++ b/src/test/java/examples/AnyObjectExampleTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class AnyObjectExampleTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testLoad() {
+        String doc = Util.getLocalResource("examples/any-object-example.yaml");
+        Yaml yaml = new Yaml();
+        Map<String, Object> object = (Map<String, Object>) yaml.load(doc);
+        assertEquals(6, object.size());
+        assertEquals("[null, null]", object.get("none").toString());
+        List<?> list1 = (List<?>) object.get("none");
+        assertEquals(2, list1.size());
+        for (Object object2 : list1) {
+            assertNull(object2);
+        }
+        //
+        assertEquals("[true, false, true, false]", object.get("bool").toString());
+        assertEquals(4, ((List<?>) object.get("bool")).size());
+        //
+        assertEquals(new Integer(42), object.get("int"));
+        assertEquals(new Double(3.14159), object.get("float"));
+        //
+        assertEquals("[LITE, RES_ACID, SUS_DEXT]", object.get("list").toString());
+        List<?> list2 = (List<?>) object.get("list");
+        assertEquals(3, list2.size());
+        for (Object object2 : list2) {
+            assertEquals(object2.toString(), object2.toString().toUpperCase());
+        }
+        //
+        assertEquals("{hp=13, sp=5}", object.get("dict").toString());
+        Map<String, Integer> map = (Map<String, Integer>) object.get("dict");
+        assertEquals(2, map.keySet().size());
+        assertEquals(new Integer(13), map.get("hp"));
+        assertEquals(new Integer(5), map.get("sp"));
+    }
+}
diff --git a/src/test/java/examples/CollectionStyleTest.java b/src/test/java/examples/CollectionStyleTest.java
new file mode 100644
index 0000000..795f9d3
--- /dev/null
+++ b/src/test/java/examples/CollectionStyleTest.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class CollectionStyleTest extends TestCase {
+    public void testNestedStyle() {
+        Yaml yaml = new Yaml();
+        String document = "  a: 1\n  b:\n    c: 3\n    d: 4\n";
+        assertEquals("a: 1\nb: {c: 3, d: 4}\n", yaml.dump(yaml.load(document)));
+    }
+
+    public void testNestedStyle2() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String document = "  a: 1\n  b:\n    c: 3\n    d: 4\n";
+        assertEquals("a: 1\nb:\n  c: 3\n  d: 4\n", yaml.dump(yaml.load(document)));
+    }
+}
diff --git a/src/test/java/examples/CustomBeanResolverTest.java b/src/test/java/examples/CustomBeanResolverTest.java
new file mode 100644
index 0000000..ce59ab4
--- /dev/null
+++ b/src/test/java/examples/CustomBeanResolverTest.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.math.BigDecimal;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+
+/**
+ * http://code.google.com/p/snakeyaml/issues/detail?id=75
+ */
+public class CustomBeanResolverTest extends TestCase {
+    private final Pattern CUSTOM_PATTERN = Pattern.compile("\\d+%");
+
+    public void testOnlyBigDecimal() {
+        Yaml yaml = new Yaml(new BigBeanConstructor());
+        Foo foo = (Foo) yaml.load("bar: 50\nbaz: 35%\nbas: 1250");
+        assertEquals(50.0, foo.bar);
+        assertEquals("0.35", foo.baz.toString());
+        assertEquals("1250", foo.bas);
+    }
+
+    public void testPrimitive() {
+        Yaml yaml = new Yaml(new BigBeanConstructor());
+        Foo foo = (Foo) yaml.load("bar: 50%\nbaz: 35%\nbas: 1250%\nbaw: 35");
+        assertEquals(0.5, foo.bar);
+        assertEquals("0.35", foo.baz.toString());
+        assertEquals("1250%", foo.bas);
+        assertEquals("35", foo.baw.toString());
+    }
+
+    class BigBeanConstructor extends Constructor {
+        public BigBeanConstructor() {
+            super(Foo.class);
+            yamlClassConstructors.put(NodeId.scalar, new ConstructBig());
+        }
+
+        private class ConstructBig extends ConstructScalar {
+            public Object construct(Node node) {
+                if (node.getType().equals(BigDecimal.class)) {
+                    String val = (String) constructScalar((ScalarNode) node);
+                    if (CUSTOM_PATTERN.matcher(val).matches()) {
+                        return new BigDecimal(val.substring(0, val.length() - 1))
+                                .divide(new BigDecimal(100));
+                    }
+                } else if (node.getType().isAssignableFrom(double.class)) {
+                    String val = (String) constructScalar((ScalarNode) node);
+                    if (CUSTOM_PATTERN.matcher(val).matches()) {
+                        return new Double(val.substring(0, val.length() - 1)) / 100;
+                    }
+                }
+                return super.construct(node);
+            }
+        }
+    }
+
+    public static class Foo {
+        public double bar = 0;
+        public BigDecimal baz;
+        public BigDecimal baw;
+        public String bas;
+    }
+}
diff --git a/src/test/java/examples/CustomConstructor.java b/src/test/java/examples/CustomConstructor.java
new file mode 100644
index 0000000..1d61a0a
--- /dev/null
+++ b/src/test/java/examples/CustomConstructor.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.yaml.snakeyaml.Invoice;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class CustomConstructor extends Constructor {
+
+    public CustomConstructor() {
+        super(Invoice.class);
+    }
+
+    @Override
+    protected List<Object> createDefaultList(int initSize) {
+        return new LinkedList<Object>();
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/examples/CustomImplicitResolverTest.java b/src/test/java/examples/CustomImplicitResolverTest.java
new file mode 100644
index 0000000..c5c0315
--- /dev/null
+++ b/src/test/java/examples/CustomImplicitResolverTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Use custom implicit resolver when the runtime class is not defined.
+ * http://code.google.com/p/snakeyaml/issues/detail?id=75
+ */
+public class CustomImplicitResolverTest extends TestCase {
+    private final Tag CUSTOM_TAG = new Tag("!BigDecimalDividedBy100");
+    private final Pattern CUSTOM_PATTERN = Pattern.compile("\\d+%");
+
+    @SuppressWarnings("unchecked")
+    public void testImplicit() {
+        Yaml yaml = new Yaml(new BigConstructor());
+        yaml.addImplicitResolver(CUSTOM_TAG, CUSTOM_PATTERN, "-0123456789");
+        Map<String, Object> obj = (Map<String, Object>) yaml.load("bar: 50%");
+        assertEquals("0.5", obj.get("bar").toString());
+        assertEquals(BigDecimal.class, obj.get("bar").getClass());
+    }
+
+    public void testImplicitFailure() {
+        Yaml yaml = new Yaml(new BigConstructor());
+        yaml.addImplicitResolver(CUSTOM_TAG, Pattern.compile("\\d+%"), "-0123456789");
+        try {
+            yaml.load("bar: !!float 50%");
+            fail("Both implicit and explicit are present.");
+        } catch (NumberFormatException e) {
+            assertEquals("For input string: \"50%\"", e.getMessage());
+        }
+    }
+
+    class BigConstructor extends SafeConstructor {
+        public BigConstructor() {
+            this.yamlConstructors.put(CUSTOM_TAG, new ConstructBig());
+        }
+
+        private class ConstructBig extends AbstractConstruct {
+            public Object construct(Node node) {
+                String val = (String) constructScalar((ScalarNode) node);
+                return new BigDecimal(val.substring(0, val.length() - 1))
+                        .divide(new BigDecimal(100));
+            }
+        }
+    }
+}
diff --git a/src/test/java/examples/CustomJavaObjectWithBinaryStringTest.java b/src/test/java/examples/CustomJavaObjectWithBinaryStringTest.java
new file mode 100644
index 0000000..755be99
--- /dev/null
+++ b/src/test/java/examples/CustomJavaObjectWithBinaryStringTest.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class CustomJavaObjectWithBinaryStringTest extends TestCase {
+    public static class Pojo {
+        private String data;
+
+        public Pojo() {
+        }
+
+        public Pojo(String data) {
+            this.data = data;
+        }
+
+        public String getData() {
+            return data;
+        }
+
+        public void setData(String data) {
+            this.data = data;
+        }
+
+        @Override public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((data == null) ? 0 : data.hashCode());
+            return result;
+        }
+
+        @Override public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Pojo other = (Pojo) obj;
+            if (data == null) {
+                if (other.data != null)
+                    return false;
+            } else if (!data.equals(other.data))
+                return false;
+            return true;
+        }
+
+    }
+
+    public void testDump() {
+        Yaml yaml = new Yaml();
+        Pojo expected = new Pojo(new String(new byte[] { 13, 14, 15, 16 }));
+        String output = yaml.dump(expected);
+
+        assertTrue(output.contains("data: !!binary |-"));
+        assertTrue(output.contains("DQ4PEA=="));
+
+        Pojo actual = (Pojo) yaml.load(new StringReader(output));
+        assertEquals(expected, actual);
+    }
+
+}
diff --git a/src/test/java/examples/CustomListExampleTest.java b/src/test/java/examples/CustomListExampleTest.java
new file mode 100644
index 0000000..11c98ca
--- /dev/null
+++ b/src/test/java/examples/CustomListExampleTest.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class CustomListExampleTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testList() {
+        Yaml yaml = new Yaml(new CustomConstructor());
+        List<Integer> data = (List<Integer>) yaml.load("[1, 2, 3]");
+        assertTrue(data instanceof LinkedList);
+    }
+
+    class CustomConstructor extends Constructor {
+        @Override
+        protected List<Object> createDefaultList(int initSize) {
+            return new LinkedList<Object>();
+        }
+    }
+}
diff --git a/src/test/java/examples/CustomMapExampleTest.java b/src/test/java/examples/CustomMapExampleTest.java
new file mode 100644
index 0000000..95fe7fc
--- /dev/null
+++ b/src/test/java/examples/CustomMapExampleTest.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class CustomMapExampleTest extends TestCase {
+    public void testMap() {
+        Yaml yaml = new Yaml(new CustomConstructor());
+        @SuppressWarnings("unchecked")
+        Map<Integer, String> data = (Map<Integer, String>) yaml
+                .load("{2: '222', 1: '111', 3: '333'}");
+        assertTrue(data instanceof TreeMap);
+        Object[] keys = data.keySet().toArray();
+        // must be sorted
+        assertEquals(new Integer(1), keys[0]);
+        assertEquals(new Integer(2), keys[1]);
+        assertEquals(new Integer(3), keys[2]);
+    }
+
+    class CustomConstructor extends Constructor {
+        @Override
+        protected Map<Object, Object> createDefaultMap() {
+            return new TreeMap<Object, Object>();
+        }
+    }
+}
diff --git a/src/test/java/examples/Dice.java b/src/test/java/examples/Dice.java
new file mode 100644
index 0000000..62e9262
--- /dev/null
+++ b/src/test/java/examples/Dice.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+public class Dice {
+    private Integer a;
+    private Integer b;
+
+    public Dice(Integer a, Integer b) {
+        super();
+        this.a = a;
+        this.b = b;
+    }
+
+    public Integer getA() {
+        return a;
+    }
+
+    public Integer getB() {
+        return b;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Dice) {
+            return toString().equals(obj.toString());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Dice " + a + "d" + b;
+    }
+}
diff --git a/src/test/java/examples/DiceExampleTest.java b/src/test/java/examples/DiceExampleTest.java
new file mode 100644
index 0000000..c0d0b63
--- /dev/null
+++ b/src/test/java/examples/DiceExampleTest.java
@@ -0,0 +1,161 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class DiceExampleTest extends TestCase {
+    public void testRepresenter() {
+        Dice dice = new Dice(3, 6);
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(dice);
+        assertEquals("!!examples.Dice {a: 3, b: 6}\n", output);
+    }
+
+    public void testDiceRepresenter() {
+        Dice dice = new Dice(3, 6);
+        Map<String, Dice> data = new HashMap<String, Dice>();
+        data.put("gold", dice);
+        Yaml yaml = new Yaml(new DiceRepresenter(), new DumperOptions());
+        String output = yaml.dump(data);
+        assertEquals("{gold: !dice '3d6'}\n", output);
+    }
+
+    class DiceRepresenter extends Representer {
+        public DiceRepresenter() {
+            this.representers.put(Dice.class, new RepresentDice());
+        }
+
+        private class RepresentDice implements Represent {
+            public Node representData(Object data) {
+                Dice dice = (Dice) data;
+                String value = dice.getA() + "d" + dice.getB();
+                return representScalar(new Tag("!dice"), value);
+            }
+        }
+    }
+
+    class DiceConstructor extends Constructor {
+        public DiceConstructor() {
+            this.yamlConstructors.put(new Tag("!dice"), new ConstructDice());
+        }
+
+        private class ConstructDice extends AbstractConstruct {
+            public Object construct(Node node) {
+                String val = (String) constructScalar((ScalarNode) node);
+                int position = val.indexOf('d');
+                Integer a = new Integer(val.substring(0, position));
+                Integer b = new Integer(val.substring(position + 1));
+                return new Dice(a, b);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testConstructor() {
+        Yaml yaml = new Yaml(new DiceConstructor());
+        Object data = yaml.load("{initial hit points: !dice '8d4'}");
+        Map<String, Dice> map = (Map<String, Dice>) data;
+        assertEquals(new Dice(8, 4), map.get("initial hit points"));
+    }
+
+    // the tag must start with a digit
+    @SuppressWarnings("unchecked")
+    public void testImplicitResolver() {
+        Yaml yaml = new Yaml(new DiceConstructor(), new DiceRepresenter());
+        // the tag must start with a digit
+        yaml.addImplicitResolver(new Tag("!dice"), Pattern.compile("\\d+d\\d+"), "123456789");
+        // dump
+        Map<String, Dice> treasure = new HashMap<String, Dice>();
+        treasure.put("treasure", new Dice(10, 20));
+        String output = yaml.dump(treasure);
+        assertEquals("{treasure: 10d20}\n", output);
+        // load
+        Object data = yaml.load("{damage: 5d10}");
+        Map<String, Dice> map = (Map<String, Dice>) data;
+        assertEquals(new Dice(5, 10), map.get("damage"));
+    }
+
+    // the tag may start with anything
+    @SuppressWarnings("unchecked")
+    public void testImplicitResolverWithNull() {
+        Yaml yaml = new Yaml(new DiceConstructor(), new DiceRepresenter());
+        // the tag may start with anything
+        yaml.addImplicitResolver(new Tag("!dice"), Pattern.compile("\\d+d\\d+"), null);
+        // dump
+        Map<String, Dice> treasure = new HashMap<String, Dice>();
+        treasure.put("treasure", new Dice(10, 20));
+        String output = yaml.dump(treasure);
+        assertEquals("{treasure: 10d20}\n", output);
+        // load
+        Object data = yaml.load("{damage: 5d10}");
+        Map<String, Dice> map = (Map<String, Dice>) data;
+        assertEquals(new Dice(5, 10), map.get("damage"));
+    }
+
+    static class DiceBean {
+        public Dice treasure;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o)
+                return true;
+            if (!(o instanceof DiceBean))
+                return false;
+
+            DiceBean diceBean = (DiceBean) o;
+            if (treasure != null ? !treasure.equals(diceBean.treasure) : diceBean.treasure != null)
+                return false;
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return treasure != null ? treasure.hashCode() : 0;
+        }
+    }
+
+    public void testImplicitResolverJavaBean() {
+        Yaml yaml = new Yaml(new DiceConstructor(), new DiceRepresenter());
+        // the tag must start with a digit
+        yaml.addImplicitResolver(new Tag("!dice"), Pattern.compile("\\d+d\\d+"), "123456789");
+        // dump
+        DiceBean bean = new DiceBean();
+        bean.treasure = new Dice(10, 20);
+        String output = yaml.dump(bean);
+        assertEquals("!!examples.DiceExampleTest$DiceBean {treasure: 10d20}\n", output);
+        // load
+        Object loaded = yaml.load(output);
+        assertEquals(loaded, bean);
+    }
+}
diff --git a/src/test/java/examples/DumpExampleTest.java b/src/test/java/examples/DumpExampleTest.java
new file mode 100644
index 0000000..358c55c
--- /dev/null
+++ b/src/test/java/examples/DumpExampleTest.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class DumpExampleTest extends TestCase {
+    public void testDump() {
+        Map<String, Object> data = new HashMap<String, Object>();
+        data.put("name", "Silenthand Olleander");
+        data.put("race", "Human");
+        data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(data);
+        assertTrue(output.contains("name: Silenthand Olleander"));
+        assertTrue(output.contains("race: Human"));
+        assertTrue(output.contains("traits: [ONE_HAND, ONE_EYE]"));
+    }
+
+    public void testDumpWriter() {
+        Map<String, Object> data = new HashMap<String, Object>();
+        data.put("name", "Silenthand Olleander");
+        data.put("race", "Human");
+        data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
+        Yaml yaml = new Yaml();
+        StringWriter writer = new StringWriter();
+        yaml.dump(data, writer);
+        assertTrue(writer.toString().contains("name: Silenthand Olleander"));
+        assertTrue(writer.toString().contains("race: Human"));
+        assertTrue(writer.toString().contains("traits: [ONE_HAND, ONE_EYE]"));
+    }
+
+    public void testDumpMany() {
+        List<Integer> docs = new ArrayList<Integer>();
+        for (int i = 1; i < 4; i++) {
+            docs.add(i);
+        }
+        DumperOptions options = new DumperOptions();
+        options.setExplicitStart(true);
+        Yaml yaml = new Yaml(options);
+        String result = yaml.dumpAll(docs.iterator());
+        assertNotNull(result);
+        assertTrue(result.contains("--- 2"));
+    }
+
+    public void testDumpCustomJavaClass() {
+        Hero hero = new Hero("Galain Ysseleg", -3, 2);
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(hero);
+        assertEquals("!!examples.Hero {hp: -3, name: Galain Ysseleg, sp: 2}\n", output);
+    }
+
+    public void testDumperOptions() {
+        List<Integer> data = new ArrayList<Integer>();
+        for (int i = 0; i < 50; i++) {
+            data.add(i);
+        }
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(data);
+        assertTrue(output.contains("[0, 1, 2, 3, 4, 5, 6, 7, 8"));
+        //
+        DumperOptions options = new DumperOptions();
+        options.setWidth(50);
+        options.setIndent(4);
+        yaml = new Yaml(options);
+        output = yaml.dump(data);
+        assertTrue(output.contains("1, 2"));
+    }
+
+    public void testDumperOptionsCanonical() {
+        List<Integer> data = new ArrayList<Integer>();
+        for (int i = 0; i < 5; i++) {
+            data.add(i);
+        }
+        DumperOptions options = new DumperOptions();
+        options.setCanonical(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(data);
+        assertTrue(output.contains("---"));
+        assertTrue(output.contains("!!seq ["));
+        assertTrue(output.contains("!!int \"3\","));
+    }
+
+    public void testDumperOptionsFlowStyle() {
+        List<Integer> data = new ArrayList<Integer>();
+        for (int i = 0; i < 5; i++) {
+            data.add(i);
+        }
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(data);
+        assertTrue(output.contains("- 0\n"));
+        assertTrue(output.contains("- 1\n"));
+        assertTrue(output.contains("- 4\n"));
+    }
+
+    public void testDumperOptionsStyle() {
+        List<Integer> data = new ArrayList<Integer>();
+        for (int i = 0; i < 5; i++) {
+            data.add(i);
+        }
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(data);
+        assertTrue(output.contains("- !!int \"0\""));
+        assertTrue(output.contains("- !!int \"1\""));
+        assertTrue(output.contains("- !!int \"4\""));
+    }
+}
diff --git a/src/test/java/examples/Hero.java b/src/test/java/examples/Hero.java
new file mode 100644
index 0000000..e309ed3
--- /dev/null
+++ b/src/test/java/examples/Hero.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+public class Hero {
+    private String name;
+    private Integer sp;
+    private Integer hp;
+
+    public Hero(String name, Integer hp, Integer sp) {
+        super();
+        this.name = name;
+        this.sp = sp;
+        this.hp = hp;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Integer getSp() {
+        return sp;
+    }
+
+    public Integer getHp() {
+        return hp;
+    }
+
+}
diff --git a/src/test/java/examples/IgnoreTagsExampleTest.java b/src/test/java/examples/IgnoreTagsExampleTest.java
new file mode 100644
index 0000000..eb175eb
--- /dev/null
+++ b/src/test/java/examples/IgnoreTagsExampleTest.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class IgnoreTagsExampleTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testLoad() {
+        String input = Util.getLocalResource("examples/unknown-tags-example.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml(new MyConstructor());
+        Map<String, Object> result = (Map<String, Object>) yaml.load(input);
+        // Check the result
+        assertNotNull(result);
+        assertEquals(3, result.size());
+        assertEquals("123", result.get("aaa"));
+        //
+        List<Object> bbb = (List<Object>) result.get("bbb");
+        assertEquals(2, bbb.size());
+        assertEquals(new Integer(111), bbb.get(0));
+        assertEquals("ddd", bbb.get(1));
+        //
+        Map<String, Object> ccc = (Map<String, Object>) result.get("ccc");
+        assertEquals(2, ccc.size());
+        assertEquals(1.0, ccc.get("x"));
+        assertEquals(3.1416, ccc.get("y"));
+    }
+
+    private class MyConstructor extends Constructor {
+        private Construct original;
+
+        public MyConstructor() {
+            original = this.yamlConstructors.get(null);
+            this.yamlConstructors.put(null, new IgnoringConstruct());
+        }
+
+        private class IgnoringConstruct extends AbstractConstruct {
+            public Object construct(Node node) {
+                if (node.getTag().startsWith("!KnownTag")) {
+                    return original.construct(node);
+                } else {
+                    switch (node.getNodeId()) {
+                    case scalar:
+                        return yamlConstructors.get(Tag.STR).construct(node);
+                    case sequence:
+                        return yamlConstructors.get(Tag.SEQ).construct(node);
+                    case mapping:
+                        return yamlConstructors.get(Tag.MAP).construct(node);
+                    default:
+                        throw new YAMLException("Unexpected node");
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/examples/LoadExampleTest.java b/src/test/java/examples/LoadExampleTest.java
new file mode 100644
index 0000000..e66ee1a
--- /dev/null
+++ b/src/test/java/examples/LoadExampleTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class LoadExampleTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testLoad() {
+        Yaml yaml = new Yaml();
+        String document = "\n- Hesperiidae\n- Papilionidae\n- Apatelodidae\n- Epiplemidae";
+        List<String> list = (List<String>) yaml.load(document);
+        assertEquals("[Hesperiidae, Papilionidae, Apatelodidae, Epiplemidae]", list.toString());
+    }
+
+    public void testLoadFromString() {
+        Yaml yaml = new Yaml();
+        String document = "hello: 25";
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> map = (Map<String, Integer>) yaml.load(document);
+        assertEquals("{hello=25}", map.toString());
+        assertEquals(new Integer(25), map.get("hello"));
+    }
+
+    public void testLoadFromStream() throws IOException {
+        InputStream input = new FileInputStream(new File("src/test/resources/reader/utf-8.txt"));
+        Yaml yaml = new Yaml();
+        Object data = yaml.load(input);
+        assertEquals("test", data);
+        //
+        data = yaml.load(new ByteArrayInputStream("test2".getBytes("UTF-8")));
+        assertEquals("test2", data);
+        input.close();
+    }
+
+    public void testLoadManyDocuments() throws IOException {
+        InputStream input = new FileInputStream(new File(
+                "src/test/resources/specification/example2_28.yaml"));
+        Yaml yaml = new Yaml();
+        int counter = 0;
+        for (Object data : yaml.loadAll(input)) {
+            assertNotNull(data);
+            assertTrue(data.toString().length() > 1);
+            counter++;
+        }
+        assertEquals(3, counter);
+        input.close();
+    }
+}
diff --git a/src/test/java/examples/SafeConstructorExampleTest.java b/src/test/java/examples/SafeConstructorExampleTest.java
new file mode 100644
index 0000000..80af417
--- /dev/null
+++ b/src/test/java/examples/SafeConstructorExampleTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+public class SafeConstructorExampleTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testConstruct() {
+        String doc = "- 5\n- Person\n- true";
+        Yaml yaml = new Yaml(new SafeConstructor());
+        List<Object> list = (List<Object>) yaml.load(doc);
+        assertEquals(3, list.size());
+        assertEquals(new Integer(5), list.get(0));
+        assertEquals("Person", list.get(1));
+        assertEquals(Boolean.TRUE, list.get(2));
+    }
+
+    public void testSafeConstruct() {
+        String doc = "- 5\n- !org.yaml.snakeyaml.constructor.Person\n  firstName: Andrey\n  age: 99\n- true";
+        Yaml yaml = new Yaml(new SafeConstructor());
+        try {
+            yaml.load(doc);
+            fail("Custom Java classes should not be created.");
+        } catch (Exception e) {
+            assertEquals(
+                    "could not determine a constructor for the tag !org.yaml.snakeyaml.constructor.Person\n"
+                            + " in 'string', line 2, column 3:\n"
+                            + "    - !org.yaml.snakeyaml.constructor. ... \n" + "      ^\n",
+                    e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/examples/SelectiveConstructorTest.java b/src/test/java/examples/SelectiveConstructorTest.java
new file mode 100644
index 0000000..1117d46
--- /dev/null
+++ b/src/test/java/examples/SelectiveConstructorTest.java
@@ -0,0 +1,101 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.NodeId;
+
+/**
+ * Example for http://code.google.com/p/snakeyaml/wiki/howto
+ */
+public class SelectiveConstructorTest extends TestCase {
+    class SelectiveConstructor extends Constructor {
+        public SelectiveConstructor() {
+            // define a custom way to create a mapping node
+            yamlClassConstructors.put(NodeId.mapping, new MyPersistentObjectConstruct());
+        }
+
+        class MyPersistentObjectConstruct extends Constructor.ConstructMapping {
+            @Override
+            protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
+                Class<?> type = node.getType();
+                if (type.equals(MyPersistentObject.class)) {
+                    // create a map
+                    Map<Object, Object> map = constructMapping(node);
+                    String id = (String) map.get("id");
+                    return new MyPersistentObject(id, 17);
+                } else {
+                    // create JavaBean
+                    return super.constructJavaBean2ndStep(node, object);
+                }
+            }
+        }
+    }
+
+    public void testConstructor() {
+        Yaml yaml = new Yaml(new SelectiveConstructor());
+        List<?> data = (List<?>) yaml
+                .load("- 1\n- 2\n- !!examples.MyPersistentObject {amount: 222, id: persistent}");
+        // System.out.println(data);
+        assertEquals(3, data.size());
+        MyPersistentObject myObject = (MyPersistentObject) data.get(2);
+        assertEquals(17, myObject.getAmount());
+        assertEquals("persistent", myObject.getId());
+    }
+}
+
+class MyPersistentObject {
+    private String id;
+    private int amount;
+
+    public MyPersistentObject() {
+        this.id = "noid";
+        this.amount = 222;
+    }
+
+    public MyPersistentObject(String id, int amount) {
+        this.id = id;
+        this.amount = amount;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getAmount() {
+        return amount;
+    }
+
+    public void setAmount(int amount) {
+        this.amount = amount;
+    }
+
+    @Override
+    public String toString() {
+        return "MyPersistentObject [id=" + id + ", amount=" + amount + "]";
+    }
+}
diff --git a/src/test/java/examples/SpringTest.java b/src/test/java/examples/SpringTest.java
new file mode 100644
index 0000000..5a79a60
--- /dev/null
+++ b/src/test/java/examples/SpringTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples;
+
+import junit.framework.TestCase;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.yaml.snakeyaml.Yaml;
+
+public class SpringTest extends TestCase {
+    public void testSimple() {
+        ApplicationContext context = new ClassPathXmlApplicationContext("examples/spring.xml");
+        Yaml yaml = (Yaml) context.getBean("standardYaml");
+        assertNotNull(yaml);
+        //
+        yaml = (Yaml) context.getBean("javabeanYaml");
+        assertNotNull(yaml);
+        //
+        yaml = (Yaml) context.getBean("snakeYaml");
+        assertNotNull(yaml);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/examples/collections/ListFileldBeanTest.java b/src/test/java/examples/collections/ListFileldBeanTest.java
new file mode 100644
index 0000000..0bea870
--- /dev/null
+++ b/src/test/java/examples/collections/ListFileldBeanTest.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test public field ListBean->List<Developer> developers <br/>
+ * Developer class must be properly recognised
+ */
+public class ListFileldBeanTest extends TestCase {
+    public void testDumpList() {
+        ListFieldBean bean = new ListFieldBean();
+        List<String> list = new ArrayList<String>();
+        list.add("aaa");
+        list.add("bbb");
+        bean.setChildren(list);
+        List<Developer> developers = new ArrayList<Developer>();
+        developers.add(new Developer("Fred", "creator"));
+        developers.add(new Developer("John", "committer"));
+        bean.developers = developers;
+        bean.setName("Bean123");
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/list-bean-1.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadList() {
+        String output = Util.getLocalResource("examples/list-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        ListFieldBean parsed = beanLoader.loadAs(output, ListFieldBean.class);
+        assertNotNull(parsed);
+        List<String> list2 = parsed.getChildren();
+        assertEquals(2, list2.size());
+        assertEquals("aaa", list2.get(0));
+        assertEquals("bbb", list2.get(1));
+        List<Developer> developers = parsed.developers;
+        assertEquals(2, developers.size());
+        assertEquals("Developer must be recognised.", Developer.class, developers.get(0).getClass());
+        Developer fred = developers.get(0);
+        assertEquals("Fred", fred.getName());
+        assertEquals("creator", fred.getRole());
+    }
+
+    public static class ListFieldBean {
+        private List<String> children;
+        private String name;
+        public List<Developer> developers;
+
+        public ListFieldBean() {
+            name = "Bean456";
+        }
+
+        public List<String> getChildren() {
+            return children;
+        }
+
+        public void setChildren(List<String> children) {
+            this.children = children;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    public static class Developer {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeListNoGerericsTest.java b/src/test/java/examples/collections/TypeSafeListNoGerericsTest.java
new file mode 100644
index 0000000..63c38cd
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeListNoGerericsTest.java
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test ListBean->List developers <br/>
+ * Developer class cannot be properly recognised
+ */
+public class TypeSafeListNoGerericsTest extends TestCase {
+    public void testDumpList() {
+        ListBean bean = new ListBean();
+        List<String> list = new ArrayList<String>();
+        list.add("aaa");
+        list.add("bbb");
+        bean.setChildren(list);
+        List<Developer> developers = new ArrayList<Developer>();
+        developers.add(new Developer("Fred", "creator"));
+        developers.add(new Developer("John", "committer"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/list-bean-4.yaml");
+        assertEquals(etalon, output);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadList() {
+        String output = Util.getLocalResource("examples/list-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        ListBean parsed = beanLoader.loadAs(output, ListBean.class);
+        assertNotNull(parsed);
+        List<String> list2 = parsed.getChildren();
+        assertEquals(2, list2.size());
+        assertEquals("aaa", list2.get(0));
+        assertEquals("bbb", list2.get(1));
+        List<Map<String, String>> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        Map<String, String> fred = developers.get(0);
+        assertEquals("Fred", fred.get("name"));
+        assertEquals("creator", fred.get("role"));
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static class ListBean {
+        private List<String> children;
+        private String name;
+        private List developers;
+
+        public ListBean() {
+            name = "Bean123";
+        }
+
+        public List<String> getChildren() {
+            return children;
+        }
+
+        public void setChildren(List<String> children) {
+            this.children = children;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public List getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(List developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static class Developer {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeListTest.java b/src/test/java/examples/collections/TypeSafeListTest.java
new file mode 100644
index 0000000..f6c9c45
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeListTest.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test ListBean->List<Developer> developers <br/>
+ * Developer class must be properly recognised
+ */
+public class TypeSafeListTest extends TestCase {
+    public void testDumpList() {
+        ListBean1 bean = new ListBean1();
+        List<String> list = new ArrayList<String>();
+        list.add("aaa");
+        list.add("bbb");
+        bean.setChildren(list);
+        List<Developer> developers = new ArrayList<Developer>();
+        developers.add(new Developer("Fred", "creator"));
+        developers.add(new Developer("John", "committer"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/list-bean-1.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadList() {
+        String output = Util.getLocalResource("examples/list-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        ListBean1 parsed = beanLoader.loadAs(output, ListBean1.class);
+        assertNotNull(parsed);
+        List<String> list2 = parsed.getChildren();
+        assertEquals(2, list2.size());
+        assertEquals("aaa", list2.get(0));
+        assertEquals("bbb", list2.get(1));
+        List<Developer> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("Developer must be recognised.", Developer.class, developers.get(0).getClass());
+        Developer fred = developers.get(0);
+        assertEquals("Fred", fred.getName());
+        assertEquals("creator", fred.getRole());
+    }
+
+    public static class ListBean1 {
+        private List<String> children;
+        private String name;
+        private List<Developer> developers;
+
+        public ListBean1() {
+            name = "Bean123";
+        }
+
+        public List<String> getChildren() {
+            return children;
+        }
+
+        public void setChildren(List<String> children) {
+            this.children = children;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public List<Developer> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(List<Developer> developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static class Developer {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeListWithInterfaceTest.java b/src/test/java/examples/collections/TypeSafeListWithInterfaceTest.java
new file mode 100644
index 0000000..4f6c08c
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeListWithInterfaceTest.java
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test ListBean->List<Human> developers <br/>
+ * Human is an interface and the global tags are required
+ */
+public class TypeSafeListWithInterfaceTest extends TestCase {
+    public void testDumpList() {
+        ListBean bean = new ListBean();
+        List<String> list = new ArrayList<String>();
+        list.add("aaa");
+        list.add("bbb");
+        bean.setChildren(list);
+        List<Human> developers = new ArrayList<Human>();
+        developers.add(new Developer("Fred", "creator"));
+        developers.add(new Committer("John", "committer", 34));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/list-bean-2.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadWrongList() {
+        String output = Util.getLocalResource("examples/list-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(output, ListBean.class);
+            fail("Global tags are required since Human is an interface.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Cannot create property=developers"));
+        }
+    }
+
+    public void testLoadList() {
+        String output = Util.getLocalResource("examples/list-bean-2.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        ListBean parsed = beanLoader.loadAs(output, ListBean.class);
+        assertNotNull(parsed);
+        List<String> list2 = parsed.getChildren();
+        assertEquals(2, list2.size());
+        assertEquals("aaa", list2.get(0));
+        assertEquals("bbb", list2.get(1));
+        List<Human> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("Developer must be recognised.", Developer.class, developers.get(0).getClass());
+        Developer fred = (Developer) developers.get(0);
+        assertEquals("Fred", fred.getName());
+        assertEquals("creator", fred.getRole());
+        Committer john = (Committer) developers.get(1);
+        assertEquals("John", john.getName());
+        assertEquals("committer", john.getRole());
+        assertEquals(34, john.getKey());
+    }
+
+    public static class ListBean {
+        private List<String> children;
+        private String name;
+        private List<Human> developers;
+
+        public ListBean() {
+            name = "Bean123";
+        }
+
+        public List<String> getChildren() {
+            return children;
+        }
+
+        public void setChildren(List<String> children) {
+            this.children = children;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public List<Human> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(List<Human> developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static interface Human {
+
+        public String getName();
+
+        public void setName(String name);
+
+    }
+
+    public static class Developer implements Human {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+
+    public static class Committer extends Developer {
+        private int key;
+
+        public Committer() {
+        }
+
+        public Committer(String string, String string2, int i) {
+            super(string, string2);
+            this.key = i;
+        }
+
+        public int getKey() {
+            return key;
+        }
+
+        public void setKey(int key) {
+            this.key = key;
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeMap2Test.java b/src/test/java/examples/collections/TypeSafeMap2Test.java
new file mode 100644
index 0000000..74673b3
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeMap2Test.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test MapBean->Map<Enum, Developer> developers <br/>
+ * Developer class must be properly recognised
+ */
+public class TypeSafeMap2Test extends TestCase {
+    public void testDumpMap() {
+        MapBean2 bean = new MapBean2();
+        Map<Developer2, Color> data = new LinkedHashMap<Developer2, Color>();
+        data.put(new Developer2("Andy", "tester"), Color.BLACK);
+        data.put(new Developer2("Lisa", "owner"), Color.RED);
+        bean.setData(data);
+        Map<Color, Developer2> developers = new LinkedHashMap<Color, Developer2>();
+        developers.put(Color.WHITE, new Developer2("Fred", "creator"));
+        developers.put(Color.BLACK, new Developer2("John", "committer"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-12.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testMap2() {
+        MapBean2 bean = new MapBean2();
+        Map<Developer2, Color> data = new LinkedHashMap<Developer2, Color>();
+        data.put(new Developer2("Andy", "tester"), Color.BLACK);
+        data.put(new SuperMan("Bill", "cleaner", false), Color.BLACK);
+        data.put(new Developer2("Lisa", "owner"), Color.RED);
+        bean.setData(data);
+        Map<Color, Developer2> developers = new LinkedHashMap<Color, Developer2>();
+        developers.put(Color.WHITE, new Developer2("Fred", "creator"));
+        developers.put(Color.RED, new SuperMan("Jason", "contributor", true));
+        developers.put(Color.BLACK, new Developer2("John", "committer"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-13.yaml");
+        assertEquals(etalon, output);
+        // load
+        Yaml beanLoader = new Yaml();
+        MapBean2 parsed = beanLoader.loadAs(etalon, MapBean2.class);
+        assertNotNull(parsed);
+        Map<Developer2, Color> parsedData = parsed.getData();
+        assertEquals(3, parsedData.size());
+        assertTrue(parsedData.containsKey(new SuperMan("Bill", "cleaner", false)));
+        assertEquals(Color.BLACK, parsedData.get(new SuperMan("Bill", "cleaner", false)));
+        //
+        Map<Color, Developer2> parsedDevelopers = parsed.getDevelopers();
+        assertEquals(3, parsedDevelopers.size());
+        assertEquals(new SuperMan("Jason", "contributor", true), parsedDevelopers.get(Color.RED));
+    }
+
+    public void testLoadMap() {
+        String output = Util.getLocalResource("examples/map-bean-12.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        MapBean2 parsed = beanLoader.loadAs(output, MapBean2.class);
+        assertNotNull(parsed);
+        Map<Developer2, Color> data = parsed.getData();
+        assertEquals(2, data.size());
+        Iterator<Developer2> iter = data.keySet().iterator();
+        Developer2 first = iter.next();
+        assertEquals("Andy", first.getName());
+        assertEquals("tester", first.getRole());
+        assertEquals(Color.BLACK, data.get(first));
+        Developer2 second = iter.next();
+        assertEquals("Lisa", second.getName());
+        assertEquals("owner", second.getRole());
+        assertEquals(Color.RED, data.get(second));
+        //
+        Map<Color, Developer2> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        Iterator<Color> iter2 = developers.keySet().iterator();
+        Color firstColor = iter2.next();
+        assertEquals(Color.WHITE, firstColor);
+        Developer2 dev1 = developers.get(firstColor);
+        assertEquals("Fred", dev1.getName());
+        assertEquals("creator", dev1.getRole());
+        Color secondColor = iter2.next();
+        assertEquals(Color.BLACK, secondColor);
+        Developer2 dev2 = developers.get(secondColor);
+        assertEquals("John", dev2.getName());
+        assertEquals("committer", dev2.getRole());
+    }
+
+    public static enum Color {
+        WHITE, BLACK, RED;
+    }
+
+    public static class MapBean2 {
+        private Map<Developer2, Color> data;
+        private String name;
+        private Map<Color, Developer2> developers;
+
+        public MapBean2() {
+            name = "Bean123";
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Map<Color, Developer2> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(Map<Color, Developer2> developers) {
+            this.developers = developers;
+        }
+
+        public Map<Developer2, Color> getData() {
+            return data;
+        }
+
+        public void setData(Map<Developer2, Color> data) {
+            this.data = data;
+        }
+
+    }
+
+    public static class Developer2 implements Comparable<Developer2> {
+        private String name;
+        private String role;
+
+        public Developer2() {
+        }
+
+        private Developer2(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+
+        public int compareTo(Developer2 o) {
+            return name.compareTo(o.name);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Developer2) {
+                return toString().equals(obj.toString());
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return toString().hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "Developer " + name + " " + role;
+        }
+
+    }
+
+    public static class SuperMan extends Developer2 {
+        private boolean smart;
+
+        public SuperMan() {
+            super();
+        }
+
+        private SuperMan(String name, String role, boolean smart) {
+            super(name, role);
+            this.smart = smart;
+        }
+
+        public boolean isSmart() {
+            return smart;
+        }
+
+        public void setSmart(boolean smart) {
+            this.smart = smart;
+        }
+
+        @Override
+        public String toString() {
+            return "Super" + super.toString();
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeMapImplementationsTest.java b/src/test/java/examples/collections/TypeSafeMapImplementationsTest.java
new file mode 100644
index 0000000..2eff986
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeMapImplementationsTest.java
@@ -0,0 +1,203 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test different Map implementations as JavaBean properties
+ */
+public class TypeSafeMapImplementationsTest extends TestCase {
+    public void testDumpMap() {
+        MapBean bean = new MapBean();
+        SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+        sortedMap.put("2", "two");
+        sortedMap.put("1", "one");
+        bean.setSorted(sortedMap);
+        Properties props = new Properties();
+        props.setProperty("key1", "value1");
+        props.setProperty("key2", "value2");
+        bean.setProperties(props);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-1.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadMap() {
+        String output = Util.getLocalResource("examples/map-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        MapBean parsed = beanLoader.loadAs(output, MapBean.class);
+        assertNotNull(parsed);
+        SortedMap<String, String> sortedMap = parsed.getSorted();
+        assertEquals(2, sortedMap.size());
+        assertEquals("one", sortedMap.get("1"));
+        assertEquals("two", sortedMap.get("2"));
+        String first = sortedMap.keySet().iterator().next();
+        assertEquals("1", first);
+        //
+        Properties props = parsed.getProperties();
+        assertEquals(2, props.size());
+        assertEquals("value1", props.getProperty("key1"));
+        assertEquals("value2", props.getProperty("key2"));
+    }
+
+    public static class MapBean {
+        private SortedMap<String, String> sorted;
+        private Properties properties;
+        private String name;
+
+        public MapBean() {
+            name = "Bean123";
+        }
+
+        public SortedMap<String, String> getSorted() {
+            return sorted;
+        }
+
+        public void setSorted(SortedMap<String, String> sorted) {
+            this.sorted = sorted;
+        }
+
+        public Properties getProperties() {
+            return properties;
+        }
+
+        public void setProperties(Properties properties) {
+            this.properties = properties;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testNoJavaBeanMap() {
+        List<Object> list = new ArrayList<Object>(3);
+        SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+        sortedMap.put("2", "two");
+        sortedMap.put("1", "one");
+        list.add(sortedMap);
+        Properties props = new Properties();
+        props.setProperty("key1", "value1");
+        props.setProperty("key2", "value2");
+        list.add(props);
+        list.add("aaa");
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(list);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-2.yaml");
+        assertEquals(etalon, output);
+        // load
+        List<Object> list2 = (List<Object>) yaml.load(output);
+        assertEquals(3, list2.size());
+        Map<Object, Object> map1 = (Map<Object, Object>) list.get(0);// it was
+                                                                     // SortedMap
+        assertEquals(2, map1.size());
+        assertEquals("one", map1.get("1"));
+        assertEquals("two", map1.get("2"));
+        Map<Object, Object> map2 = (Map<Object, Object>) list.get(1);// it was
+                                                                     // Properties
+        assertEquals(2, map2.size());
+        assertEquals("value1", map2.get("key1"));
+        assertEquals("value2", map2.get("key2"));
+        assertEquals("aaa", list.get(2));
+    }
+
+    public void testRecursiveNoJavaBeanMap1() {
+        SortedMap<String, Object> sortedMap = new TreeMap<String, Object>();
+        sortedMap.put("2", "two");
+        sortedMap.put("1", "one");
+        sortedMap.put("3", sortedMap);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(sortedMap);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-recursive-1.yaml");
+        assertEquals(etalon, output);
+        // load with different order
+        @SuppressWarnings("unchecked")
+        Map<Object, Object> map1 = (Map<Object, Object>) yaml.load(Util
+                .getLocalResource("examples/map-recursive-1_1.yaml"));
+        assertEquals(3, map1.size());
+        assertEquals("one", map1.get("1"));
+        assertEquals("two", map1.get("2"));
+        // test that the order is taken from YAML instead of sorting
+        String first = (String) map1.keySet().iterator().next();
+        assertEquals("2", first);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRecursiveNoJavaBeanProperties2() {
+        Properties props = new Properties();
+        props.setProperty("key1", "value1");
+        props.setProperty("key2", "value2");
+        Map<Object, Object> map = props;
+        map.put("key3", props);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(props);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-recursive-2.yaml");
+        assertEquals(etalon, output);
+        // load
+        Map<Object, Object> map2 = (Map<Object, Object>) yaml.load(output);
+        assertEquals(3, map2.size());
+        assertEquals("value1", map2.get("key1"));
+        assertEquals("value2", map2.get("key2"));
+    }
+
+    public void testRecursiveNoJavaBeanMap3() {
+        Yaml yaml = new Yaml();
+        String output = Util.getLocalResource("examples/map-recursive-3.yaml");
+        // System.out.println(output);
+        @SuppressWarnings("unchecked")
+        SortedMap<Object, Object> map1 = (SortedMap<Object, Object>) yaml.load(output);
+        assertEquals(3, map1.size());
+        assertEquals("one", map1.get("1"));
+        assertEquals("two", map1.get("2"));
+        // test that the order is NOT taken from YAML but sorted
+        String first = (String) map1.keySet().iterator().next();
+        assertEquals("1", first);
+    }
+
+    public void testRecursiveNoJavaBeanProperties4() {
+        Yaml yaml = new Yaml();
+        String output = Util.getLocalResource("examples/map-recursive-4.yaml");
+        // System.out.println(output);
+        try {
+            yaml.load(output);
+            fail("Recursive Properties are not supported.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Properties must not be recursive."));
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafeMapTest.java b/src/test/java/examples/collections/TypeSafeMapTest.java
new file mode 100644
index 0000000..d21b954
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeMapTest.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test MapBean->Map<String, Developer> developers <br/>
+ * Developer class must be properly recognised
+ */
+public class TypeSafeMapTest extends TestCase {
+    public void testDumpMap() {
+        MapBean bean = new MapBean();
+        Map<String, Integer> data = new LinkedHashMap<String, Integer>();
+        data.put("aaa", 1);
+        data.put("bbb", 2);
+        data.put("zzz", 3);
+        bean.setData(data);
+        Map<String, Developer2> developers = new LinkedHashMap<String, Developer2>();
+        developers.put("team1", new Developer2("Fred", "creator"));
+        developers.put("team2", new Developer2("John", "committer"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-10.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testDumpMap2() {
+        MapBean bean = new MapBean();
+        Map<String, Integer> data = new LinkedHashMap<String, Integer>();
+        data.put("aaa", 1);
+        data.put("bbb", 2);
+        bean.setData(data);
+        Map<String, Developer2> developers = new LinkedHashMap<String, Developer2>();
+        developers.put("team1", new Developer2("Fred", "creator"));
+        developers.put("team2", new Developer2("John", "committer"));
+        developers.put("team3", new Developer222("Bill", "head"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/map-bean-11.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadMap() {
+        String output = Util.getLocalResource("examples/map-bean-10.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        MapBean parsed = beanLoader.loadAs(output, MapBean.class);
+        assertNotNull(parsed);
+        Map<String, Integer> data = parsed.getData();
+        assertEquals(3, data.size());
+        assertEquals(new Integer(1), data.get("aaa"));
+        assertEquals(new Integer(2), data.get("bbb"));
+        assertEquals(new Integer(3), data.get("zzz"));
+        Map<String, Developer2> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("Developer must be recognised.", Developer2.class, developers.get("team1")
+                .getClass());
+        Developer2 fred = developers.get("team1");
+        assertEquals("Fred", fred.getName());
+        assertEquals("creator", fred.getRole());
+    }
+
+    public static class MapBean {
+        private Map<String, Integer> data;
+        private String name;
+        private Map<String, Developer2> developers;
+
+        public MapBean() {
+            name = "Bean123";
+        }
+
+        public Map<String, Integer> getData() {
+            return data;
+        }
+
+        public void setData(Map<String, Integer> data) {
+            this.data = data;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Map<String, Developer2> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(Map<String, Developer2> developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static class Developer2 {
+        private String name;
+        private String role;
+
+        public Developer2() {
+        }
+
+        public Developer2(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+
+    public static class Developer222 extends Developer2 {
+        public Developer222() {
+            super();
+        }
+
+        public Developer222(String name, String role) {
+            super(name, role);
+        }
+    }
+
+    /*
+     * No generic collection
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testLoadMapWithObject() {
+        String output = Util.getLocalResource("examples/map-bean-10.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        MapBeanNoGenerics parsed = beanLoader.loadAs(output, MapBeanNoGenerics.class);
+        assertNotNull(parsed);
+        Map<String, Integer> data = parsed.getData();
+        assertEquals(3, data.size());
+        assertEquals(new Integer(1), data.get("aaa"));
+        assertEquals(new Integer(2), data.get("bbb"));
+        assertEquals(new Integer(3), data.get("zzz"));
+        Map developers = parsed.getDevelopers();
+        assertNotNull(developers);
+        assertEquals(2, developers.size());
+        Object o1 = developers.get("team1");
+        // because of erasure we get simply Map
+        Map<String, String> developer = (Map<String, String>) o1;
+        assertEquals("Fred", developer.get("name"));
+        assertEquals("creator", developer.get("role"));
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static class MapBeanNoGenerics {
+        private Map data;
+        private String name;
+        private Map developers;
+
+        public MapBeanNoGenerics() {
+            name = "Bean123";
+        }
+
+        public Map getData() {
+            return data;
+        }
+
+        public void setData(Map data) {
+            this.data = data;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Map getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(Map developers) {
+            this.developers = developers;
+        }
+    }
+}
diff --git a/src/test/java/examples/collections/TypeSafePriorityTest.java b/src/test/java/examples/collections/TypeSafePriorityTest.java
new file mode 100644
index 0000000..bbe390d
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafePriorityTest.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+/**
+ * Test ListBean->List<Human> developers <br/>
+ * Human is an interface and the global tags are required
+ */
+public class TypeSafePriorityTest extends TestCase {
+
+    /**
+     * explicit TypeDescription is more important then runtime class (which may
+     * be an interface)
+     */
+    public void testLoadList2() {
+        String output = Util.getLocalResource("examples/list-bean-3.yaml");
+        // System.out.println(output);
+        TypeDescription descr = new TypeDescription(ListBean.class);
+        descr.putListPropertyType("developers", Developer.class);
+        Yaml beanLoader = new Yaml(new Constructor(descr));
+        ListBean parsed = beanLoader.loadAs(output, ListBean.class);
+        assertNotNull(parsed);
+        List<Human> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("Committer must be recognised.", Developer.class, developers.get(0).getClass());
+        Developer fred = (Developer) developers.get(0);
+        assertEquals("Fred", fred.getName());
+        assertEquals("creator", fred.getRole());
+        Developer john = (Developer) developers.get(1);
+        assertEquals("John", john.getName());
+        assertEquals("committer", john.getRole());
+    }
+
+    public static class ListBean {
+        private String name;
+        private List<Human> developers;
+
+        public ListBean() {
+            name = "Bean123";
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public List<Human> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(List<Human> developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static interface Human {
+
+        public String getName();
+
+        public void setName(String name);
+
+    }
+
+    public static class Developer implements Human {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+    }
+
+}
diff --git a/src/test/java/examples/collections/TypeSafeSetImplementationsTest.java b/src/test/java/examples/collections/TypeSafeSetImplementationsTest.java
new file mode 100644
index 0000000..297db14
--- /dev/null
+++ b/src/test/java/examples/collections/TypeSafeSetImplementationsTest.java
@@ -0,0 +1,270 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.collections;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test different Map implementations as JavaBean properties
+ */
+public class TypeSafeSetImplementationsTest extends TestCase {
+    public void testDumpSet() {
+        SetBean bean = new SetBean();
+        SortedSet<String> sortedSet = new TreeSet<String>();
+        sortedSet.add("two");
+        sortedSet.add("one");
+        sortedSet.add("three");
+        bean.setSorted(sortedSet);
+        SortedSet<Developer> developers = new TreeSet<Developer>();
+        developers.add(new Developer("John", "founder"));
+        developers.add(new Developer("Karl", "user"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/set-bean-1.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testDumpSet2() {
+        SetBean bean = new SetBean();
+        SortedSet<String> sortedSet = new TreeSet<String>();
+        sortedSet.add("two");
+        sortedSet.add("one");
+        sortedSet.add("three");
+        bean.setSorted(sortedSet);
+        SortedSet<Developer> developers = new TreeSet<Developer>();
+        developers.add(new Developer("John", "founder"));
+        developers.add(new Developer("Karl", "user"));
+        developers.add(new SuperDeveloper("Bill", "super"));
+        bean.setDevelopers(developers);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("examples/set-bean-6.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testLoadSet() {
+        String output = Util.getLocalResource("examples/set-bean-1.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        SetBean parsed = beanLoader.loadAs(output, SetBean.class);
+        assertNotNull(parsed);
+        SortedSet<String> sortedMap = parsed.getSorted();
+        assertEquals(3, sortedMap.size());
+        assertTrue(sortedMap.contains("one"));
+        assertTrue(sortedMap.contains("two"));
+        assertTrue(sortedMap.contains("three"));
+        String first = sortedMap.iterator().next();
+        assertEquals("one", first);
+        //
+        SortedSet<Developer> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("John", developers.first().getName());
+        assertEquals("Karl", developers.last().getName());
+    }
+
+    public void testLoadSetReversed() {
+        String output = Util.getLocalResource("examples/set-bean-2.yaml");
+        // System.out.println(output);
+        Yaml beanLoader = new Yaml();
+        SetBean parsed = beanLoader.loadAs(output, SetBean.class);
+        assertNotNull(parsed);
+        SortedSet<String> sortedMap = parsed.getSorted();
+        assertEquals(3, sortedMap.size());
+        assertTrue(sortedMap.contains("one"));
+        assertTrue(sortedMap.contains("two"));
+        assertTrue(sortedMap.contains("three"));
+        // alphabetically: one, three, two
+        assertEquals("one", sortedMap.first());
+        assertEquals("two", sortedMap.last());
+        // the order is not from YAML (must be sorted)
+        SortedSet<Developer> developers = parsed.getDevelopers();
+        assertEquals(2, developers.size());
+        assertEquals("John", developers.first().getName());
+        assertEquals("Karl", developers.last().getName());
+    }
+
+    public static class SetBean {
+        private SortedSet<String> sorted;
+        private SortedSet<Developer> developers;
+        private String name;
+
+        public SetBean() {
+            name = "Bean123";
+        }
+
+        public SortedSet<String> getSorted() {
+            return sorted;
+        }
+
+        public void setSorted(SortedSet<String> sorted) {
+            this.sorted = sorted;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public SortedSet<Developer> getDevelopers() {
+            return developers;
+        }
+
+        public void setDevelopers(SortedSet<Developer> developers) {
+            this.developers = developers;
+        }
+    }
+
+    public static class Developer implements Comparable<Developer> {
+        private String name;
+        private String role;
+
+        public Developer() {
+        }
+
+        public Developer(String name, String role) {
+            this.name = name;
+            this.role = role;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getRole() {
+            return role;
+        }
+
+        public void setRole(String role) {
+            this.role = role;
+        }
+
+        public int compareTo(Developer o) {
+            return name.compareTo(o.name);
+        }
+    }
+
+    public static class SuperDeveloper extends Developer {
+
+        public SuperDeveloper() {
+            super();
+        }
+
+        public SuperDeveloper(String string, String string2) {
+            super(string, string2);
+        }
+
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testNoJavaBeanSetRecursive() {
+        Set<Object> set = new HashSet<Object>(3);
+        set.add("aaa");
+        set.add(111);
+        Box box = new Box();
+        box.setId("id123");
+        box.setSet(set);
+        set.add(box);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(set);
+        // System.out.println(output);
+        // the order may differ on different JVMs
+        // String etalon = Util.getLocalResource("examples/set-bean-3.yaml");
+        // assertEquals(etalon, output);
+        assertTrue(output.contains("&id001 !!set"));
+        assertTrue(output.contains("? !!examples.collections.TypeSafeSetImplementationsTest$Box"));
+        assertTrue(output.contains("set: *id001"));
+        assertTrue(output.contains("111: null"));
+        // load
+        Set<Object> list2 = (Set<Object>) yaml.load(output);
+        assertEquals(3, list2.size());
+        assertTrue(list2.contains("aaa"));
+        assertTrue(list2.contains(111));
+    }
+
+    public static class Box {
+        private String id;
+        private Set<Object> set;
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public Set<Object> getSet() {
+            return set;
+        }
+
+        public void setSet(Set<Object> set) {
+            this.set = set;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testNoJavaBeanSet() {
+        Yaml yaml = new Yaml();
+        String output = Util.getLocalResource("examples/set-bean-4.yaml");
+        // System.out.println(output);
+        // load
+        Set<String> set = (Set<String>) yaml.load(output);
+        assertEquals(3, set.size());
+        assertTrue(set.contains("aaa"));
+        assertTrue(set.contains("bbb"));
+        assertTrue(set.contains("zzz"));
+        Iterator<String> iter = set.iterator();
+        assertEquals("bbb", iter.next());
+        assertEquals("aaa", iter.next());
+        assertEquals("zzz", iter.next());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testNoJavaBeanSet2() {
+        Yaml yaml = new Yaml();
+        String output = Util.getLocalResource("examples/set-bean-5.yaml");
+        // System.out.println(output);
+        // load and sort
+        Set<String> set = (Set<String>) yaml.load(output);
+        assertEquals(3, set.size());
+        assertTrue(set.contains("aaa"));
+        assertTrue(set.contains("bbb"));
+        assertTrue(set.contains("zzz"));
+        Iterator<String> iter = set.iterator();
+        assertEquals("aaa", iter.next());
+        assertEquals("bbb", iter.next());
+        assertEquals("zzz", iter.next());
+    }
+}
diff --git a/src/test/java/examples/jodatime/JodaTimeExampleTest.java b/src/test/java/examples/jodatime/JodaTimeExampleTest.java
new file mode 100644
index 0000000..0112608
--- /dev/null
+++ b/src/test/java/examples/jodatime/JodaTimeExampleTest.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.jodatime;
+
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.joda.time.DateMidnight;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class JodaTimeExampleTest extends TestCase {
+    private static final long timestamp = 1000000000000L;
+
+    public void testDump() {
+        DateTime time = new DateTime(timestamp, DateTimeZone.UTC);
+        Yaml yaml = new Yaml(new JodaTimeRepresenter());
+        String joda = yaml.dump(time);
+        String date = new Yaml().dump(new Date(timestamp));
+        assertEquals(date, joda);
+        assertEquals("2001-09-09T01:46:40Z\n", joda);
+    }
+
+    public void testLoad() {
+        Yaml yaml = new Yaml(new JodaTimeImplicitContructor());
+        DateTime time = (DateTime) yaml.load("2001-09-09T01:46:40Z");
+        assertEquals(new DateTime(timestamp, DateTimeZone.UTC), time);
+    }
+
+    /**
+     * test issue 109
+     */
+    public void test109() {
+        Date someDate = new DateMidnight(9, 2, 21, DateTimeZone.forID("Europe/Amsterdam")).toDate();
+        Yaml yaml = new Yaml();
+        String timestamp = yaml.dump(someDate);
+        assertEquals("0009-02-22T23:40:28Z\n", timestamp);
+        // System.out.println(timestamp);
+        Object o = yaml.load(timestamp);
+        assertEquals(someDate, o);
+    }
+
+    class JodaPropertyConstructor extends Constructor {
+        public JodaPropertyConstructor() {
+            yamlClassConstructors.put(NodeId.scalar, new TimeStampConstruct());
+        }
+
+        class TimeStampConstruct extends Constructor.ConstructScalar {
+            @Override
+            public Object construct(Node nnode) {
+                if (nnode.getTag().equals("tag:yaml.org,2002:timestamp")) {
+                    Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
+                    Date date = (Date) dateConstructor.construct(nnode);
+                    return new DateTime(date, DateTimeZone.UTC);
+                } else {
+                    return super.construct(nnode);
+                }
+            }
+        }
+    }
+
+    /**
+     * This class should be used if JodaTime may appear with a tag or as a
+     * JavaBean property
+     */
+    public class JodaTimeConstructor extends Constructor {
+        private final Construct javaDateConstruct;
+        private final Construct jodaDateConstruct;
+
+        public JodaTimeConstructor() {
+            javaDateConstruct = new ConstructYamlTimestamp();
+            jodaDateConstruct = new ConstructJodaTimestamp();
+            // Whenever we see an explicit timestamp tag, make a Joda Date
+            // instead
+            yamlConstructors.put(Tag.TIMESTAMP, jodaDateConstruct);
+            // See
+            // We need this to work around implicit construction.
+            yamlClassConstructors.put(NodeId.scalar, new TimeStampConstruct());
+        }
+
+        public class ConstructJodaTimestamp extends AbstractConstruct {
+            public Object construct(Node node) {
+                Date date = (Date) javaDateConstruct.construct(node);
+                return new DateTime(date, DateTimeZone.UTC);
+            }
+        }
+
+        class TimeStampConstruct extends Constructor.ConstructScalar {
+            @Override
+            public Object construct(Node nnode) {
+                if (nnode.getTag().equals(Tag.TIMESTAMP)) {
+                    return jodaDateConstruct.construct(nnode);
+                } else {
+                    return super.construct(nnode);
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/examples/jodatime/JodaTimeFlowStylesTest.java b/src/test/java/examples/jodatime/JodaTimeFlowStylesTest.java
new file mode 100644
index 0000000..901385a
--- /dev/null
+++ b/src/test/java/examples/jodatime/JodaTimeFlowStylesTest.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.jodatime;
+
+import java.util.Date;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class JodaTimeFlowStylesTest extends TestCase {
+    private static final long timestamp = 1000000000000L;
+
+    /**
+     * @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=128"></a>
+     */
+    public void testLoadBeanWithBlockFlow() {
+        MyBean bean = new MyBean();
+        bean.setId("id123");
+        DateTime etalon = new DateTime(timestamp, DateTimeZone.UTC);
+        bean.setDate(etalon);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml dumper = new Yaml(new JodaTimeRepresenter(), options);
+        // compare Nodes with flow style AUTO and flow style BLOCK
+        Node node1 = dumper.represent(bean);
+        DumperOptions options2 = new DumperOptions();
+        options2.setDefaultFlowStyle(FlowStyle.AUTO);
+        Yaml dumper2 = new Yaml(new JodaTimeRepresenter(), options2);
+        Node node2 = dumper2.represent(bean);
+        assertEquals(node2.toString(), node1.toString());
+        // compare Events with flow style AUTO and flow style BLOCK
+        List<Event> events1 = dumper.serialize(node1);
+        List<Event> events2 = dumper2.serialize(node2);
+        assertEquals(events2.size(), events1.size());
+        int i = 0;
+        for (Event etalonEvent : events2) {
+            assertEquals(etalonEvent, events1.get(i++));
+            if (etalonEvent instanceof ScalarEvent) {
+                ScalarEvent scalar = (ScalarEvent) etalonEvent;
+                if (scalar.getValue().equals("2001-09-09T01:46:40Z")) {
+                    assertTrue(scalar.getImplicit().canOmitTagInPlainScalar());
+                    assertFalse(scalar.getImplicit().canOmitTagInNonPlainScalar());
+                }
+            }
+        }
+        // Nodes and Events are the same. Only emitter may influence the output.
+        String doc1 = dumper.dump(bean);
+        // System.out.println(doc1);
+        /*
+         * 'date' must be used only with the explicit '!!timestamp' tag.
+         * Implicit tag will not work because 'date' is the JavaBean property
+         * and in this case the empty constructor of the class will be used.
+         * Since this constructor does not exist for JodaTime an exception will
+         * be thrown.
+         */
+        assertEquals("!!examples.jodatime.MyBean\ndate: 2001-09-09T01:46:40Z\nid: id123\n", doc1);
+        /*
+         * provided JodaTimeContructor will be ignored because 'date' is a
+         * JavaBean property and its class gets more priority then the implicit
+         * '!!timestamp' tag.
+         */
+        Yaml loader = new Yaml(new JodaTimeImplicitContructor());
+        try {
+            loader.load(doc1);
+        } catch (Exception e) {
+            assertTrue(
+                    "The error must indicate that JodaTime cannot be created from the scalar value.",
+                    e.getMessage()
+                            .contains(
+                                    "No String constructor found. Exception=org.joda.time.DateTime.<init>(java.lang.String)"));
+        }
+        // we have to provide a special way to create JodaTime instances from
+        // scalars
+        Yaml loader2 = new Yaml(new JodaPropertyConstructor());
+        MyBean parsed = (MyBean) loader2.load(doc1);
+        assertEquals(etalon, parsed.getDate());
+    }
+
+    /**
+     * !!timestamp must be used, without it the implicit tag will be ignored
+     * because 'date' is the JavaBean property.
+     * 
+     * Since the timestamp contains ':' character it cannot use plain scalar
+     * style in the FLOW mapping style. Emitter suggests single quoted scalar
+     * style and that is why the explicit '!!timestamp' is present in the YAML
+     * document.
+     * 
+     * @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=128"></a>
+     * 
+     */
+    public void testLoadBeanWithAutoFlow() {
+        MyBean bean = new MyBean();
+        bean.setId("id123");
+        DateTime etalon = new DateTime(timestamp, DateTimeZone.UTC);
+        bean.setDate(etalon);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.AUTO);
+        Yaml dumper = new Yaml(new JodaTimeRepresenter(), options);
+        String doc = dumper.dump(bean);
+        // System.out.println(doc);
+        assertEquals(
+                "!!examples.jodatime.MyBean {date: !!timestamp '2001-09-09T01:46:40Z', id: id123}\n",
+                doc);
+        Yaml loader = new Yaml(new JodaTimeImplicitContructor());
+        MyBean parsed = (MyBean) loader.load(doc);
+        assertEquals(etalon, parsed.getDate());
+    }
+
+    private class JodaPropertyConstructor extends Constructor {
+        public JodaPropertyConstructor() {
+            yamlClassConstructors.put(NodeId.scalar, new TimeStampConstruct());
+        }
+
+        class TimeStampConstruct extends Constructor.ConstructScalar {
+            @Override
+            public Object construct(Node nnode) {
+                if (nnode.getTag().equals(new Tag("tag:yaml.org,2002:timestamp"))) {
+                    Construct dateConstructor = yamlConstructors.get(Tag.TIMESTAMP);
+                    Date date = (Date) dateConstructor.construct(nnode);
+                    return new DateTime(date, DateTimeZone.UTC);
+                } else {
+                    return super.construct(nnode);
+                }
+            }
+
+        }
+    }
+}
diff --git a/src/test/java/examples/jodatime/JodaTimeImplicitContructor.java b/src/test/java/examples/jodatime/JodaTimeImplicitContructor.java
new file mode 100644
index 0000000..e1af428
--- /dev/null
+++ b/src/test/java/examples/jodatime/JodaTimeImplicitContructor.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.jodatime;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * It works only when JodaTime is not a JavaBean property
+ */
+public class JodaTimeImplicitContructor extends Constructor {
+    public JodaTimeImplicitContructor() {
+        this.yamlConstructors.put(Tag.TIMESTAMP, new ConstructJodaTimestamp());
+    }
+
+    private class ConstructJodaTimestamp extends ConstructYamlTimestamp {
+        public Object construct(Node node) {
+            Date date = (Date) super.construct(node);
+            return new DateTime(date, DateTimeZone.UTC);
+        }
+    }
+}
diff --git a/src/test/java/examples/jodatime/JodaTimeRepresenter.java b/src/test/java/examples/jodatime/JodaTimeRepresenter.java
new file mode 100644
index 0000000..f7fd824
--- /dev/null
+++ b/src/test/java/examples/jodatime/JodaTimeRepresenter.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.jodatime;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.representer.Representer;
+
+class JodaTimeRepresenter extends Representer {
+    public JodaTimeRepresenter() {
+        multiRepresenters.put(DateTime.class, new RepresentJodaDateTime());
+    }
+
+    private class RepresentJodaDateTime extends RepresentDate {
+
+        public Node representData(Object data) {
+            DateTime date = (DateTime) data;
+            return super.representData(new Date(date.getMillis()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/examples/jodatime/MyBean.java b/src/test/java/examples/jodatime/MyBean.java
new file mode 100644
index 0000000..5957ae5
--- /dev/null
+++ b/src/test/java/examples/jodatime/MyBean.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.jodatime;
+
+import org.joda.time.DateTime;
+
+public class MyBean {
+    private String id;
+    private DateTime date;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public DateTime getDate() {
+        return date;
+    }
+
+    public void setDate(DateTime date) {
+        this.date = date;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/examples/resolver/CustomIntResolver.java b/src/test/java/examples/resolver/CustomIntResolver.java
new file mode 100644
index 0000000..5244b1a
--- /dev/null
+++ b/src/test/java/examples/resolver/CustomIntResolver.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.resolver;
+
+import java.util.regex.Pattern;
+
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class CustomIntResolver extends Resolver {
+    public static final Pattern PURE_INT = Pattern.compile("^[0-9]+$");
+
+    /*
+     * do not resolve int if it has underscores
+     */
+    protected void addImplicitResolvers() {
+        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
+        addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789.");
+        // define simple int pattern
+        addImplicitResolver(Tag.INT, PURE_INT, "0123456789");
+        addImplicitResolver(Tag.MERGE, MERGE, "<");
+        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
+        addImplicitResolver(Tag.NULL, EMPTY, null);
+        addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
+    }
+}
diff --git a/src/test/java/examples/resolver/CustomResolver.java b/src/test/java/examples/resolver/CustomResolver.java
new file mode 100644
index 0000000..4e3f71c
--- /dev/null
+++ b/src/test/java/examples/resolver/CustomResolver.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.resolver;
+
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class CustomResolver extends Resolver {
+
+    /*
+     * do not resolve float and timestamp
+     */
+    protected void addImplicitResolvers() {
+        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
+        // addImplicitResolver(Tags.FLOAT, FLOAT, "-+0123456789.");
+        addImplicitResolver(Tag.INT, INT, "-+0123456789");
+        addImplicitResolver(Tag.MERGE, MERGE, "<");
+        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
+        addImplicitResolver(Tag.NULL, EMPTY, null);
+        // addImplicitResolver(Tags.TIMESTAMP, TIMESTAMP, "0123456789");
+    }
+}
diff --git a/src/test/java/examples/resolver/CustomResolverTest.java b/src/test/java/examples/resolver/CustomResolverTest.java
new file mode 100644
index 0000000..e647a25
--- /dev/null
+++ b/src/test/java/examples/resolver/CustomResolverTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.resolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class CustomResolverTest extends TestCase {
+
+    public void testResolverToDump() {
+        Map<Object, Object> map = new HashMap<Object, Object>();
+        map.put("1.0", "2009-01-01");
+        Yaml yaml = new Yaml(new Constructor(), new Representer(), new DumperOptions(),
+                new CustomResolver());
+        String output = yaml.dump(map);
+        assertEquals("{1.0: 2009-01-01}\n", output);
+        assertEquals("Float and Date must be escaped.", "{'1.0': '2009-01-01'}\n",
+                new Yaml().dump(map));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testResolverToLoad() {
+        Yaml yaml = new Yaml(new Constructor(), new Representer(), new DumperOptions(),
+                new CustomResolver());
+        Map<Object, Object> map = (Map<Object, Object>) yaml.load("1.0: 2009-01-01");
+        assertEquals(1, map.size());
+        assertEquals("2009-01-01", map.get("1.0"));
+        // the default Resolver shall create Date and Double from the same YAML
+        // document
+        Yaml yaml2 = new Yaml();
+        Map<Object, Object> map2 = (Map<Object, Object>) yaml2.load("1.0: 2009-01-01");
+        assertEquals(1, map2.size());
+        assertFalse(map2.containsKey("1.0"));
+        assertTrue(map2.toString(), map2.containsKey(new Double(1.0)));
+    }
+}
diff --git a/src/test/java/examples/staticstate/JavaBeanWithStaticState.java b/src/test/java/examples/staticstate/JavaBeanWithStaticState.java
new file mode 100644
index 0000000..0514de0
--- /dev/null
+++ b/src/test/java/examples/staticstate/JavaBeanWithStaticState.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.staticstate;
+
+public class JavaBeanWithStaticState {
+    private String name;
+    private int age;
+    public static String color;// public field
+    private static String type;// private field with a getter
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public static String getType() {
+        return type;
+    }
+
+    public static void setType(String type) {
+        JavaBeanWithStaticState.type = type;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/examples/staticstate/StaticFieldsTest.java b/src/test/java/examples/staticstate/StaticFieldsTest.java
new file mode 100644
index 0000000..60068ce
--- /dev/null
+++ b/src/test/java/examples/staticstate/StaticFieldsTest.java
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.staticstate;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * Example with static fields
+ */
+public class StaticFieldsTest extends TestCase {
+    public void testAsJavaBean() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setName("Bahrack");
+        bean.setAge(-47);
+        JavaBeanWithStaticState.setType("Represent");
+        JavaBeanWithStaticState.color = "Black";
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(bean);
+        // System.out.println(output);
+        assertEquals("!!examples.staticstate.JavaBeanWithStaticState {age: -47, name: Bahrack}\n",
+                output);
+        // parse back to instance
+        JavaBeanWithStaticState bean2 = (JavaBeanWithStaticState) yaml.load(output);
+        assertEquals(-47, bean2.getAge());
+        assertEquals("Bahrack", bean2.getName());
+    }
+
+    public void testCustomDump() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setName("Lui");
+        bean.setAge(25);
+        JavaBeanWithStaticState.setType("Represent");
+        JavaBeanWithStaticState.color = "Black";
+        Yaml yaml = new Yaml(new MyRepresenter(), new DumperOptions());
+        String output = yaml.dump(bean);
+        // System.out.println(output);
+        assertEquals(
+                "!!examples.staticstate.JavaBeanWithStaticState {age: 25, name: Lui, color: Black,\n  type: Represent}\n",
+                output);
+    }
+
+    public void testCustomLoad() {
+        Yaml yaml = new Yaml(new MyConstructor());
+        String output = "!!examples.staticstate.JavaBeanWithStaticState {age: 25, name: Lui, color: Oranje,\n  type: King}\n";
+        JavaBeanWithStaticState bean2 = (JavaBeanWithStaticState) yaml.load(output);
+        assertEquals(25, bean2.getAge());
+        assertEquals("Lui", bean2.getName());
+        assertEquals("Oranje", JavaBeanWithStaticState.color);
+        assertEquals("King", JavaBeanWithStaticState.getType());
+    }
+
+    private class MyRepresenter extends Representer {
+        @Override
+        protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
+            MappingNode node = super.representJavaBean(properties, javaBean);
+            if (javaBean instanceof JavaBeanWithStaticState) {
+                List<NodeTuple> value = node.getValue();
+                value.add(new NodeTuple(representData("color"),
+                        representData(JavaBeanWithStaticState.color)));
+                value.add(new NodeTuple(representData("type"),
+                        representData(JavaBeanWithStaticState.getType())));
+            }
+            return node;
+        }
+    }
+
+    private class MyConstructor extends Constructor {
+        protected Object constructObject(Node node) {
+            if (node.getType().isAssignableFrom(JavaBeanWithStaticState.class)) {
+                MappingNode beanNode = (MappingNode) node;
+                List<NodeTuple> value = beanNode.getValue();
+                List<NodeTuple> removed = new ArrayList<NodeTuple>();
+                for (NodeTuple tuple : value) {
+                    ScalarNode keyNode = (ScalarNode) tuple.getKeyNode();
+                    if (keyNode.getValue().equals("color")) {
+                        ScalarNode valueNode = (ScalarNode) tuple.getValueNode();
+                        JavaBeanWithStaticState.color = valueNode.getValue();
+                    } else if (keyNode.getValue().equals("type")) {
+                        ScalarNode valueNode = (ScalarNode) tuple.getValueNode();
+                        JavaBeanWithStaticState.setType(valueNode.getValue());
+                    } else
+                        removed.add(tuple);
+                }
+                beanNode.setValue(removed);
+                JavaBeanWithStaticState bean = (JavaBeanWithStaticState) super
+                        .constructObject(beanNode);
+
+                return bean;
+            } else {
+                return super.constructObject(node);
+            }
+        }
+    }
+}
diff --git a/src/test/java/examples/staticstate/StaticFieldsWrapperTest.java b/src/test/java/examples/staticstate/StaticFieldsWrapperTest.java
new file mode 100644
index 0000000..7555258
--- /dev/null
+++ b/src/test/java/examples/staticstate/StaticFieldsWrapperTest.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.staticstate;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * Example: using wrapper object for static fields
+ */
+public class StaticFieldsWrapperTest extends TestCase {
+
+    /**
+     * use wrapper with global tag
+     */
+    public void testWrapper() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setName("Bahrack");
+        bean.setAge(-47);
+        JavaBeanWithStaticState.setType("Type3");
+        JavaBeanWithStaticState.color = "Violet";
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(new Wrapper(bean));
+        // System.out.println(output);
+        assertEquals(
+                "!!examples.staticstate.Wrapper {age: -47, color: Violet, name: Bahrack, type: Type3}\n",
+                output);
+        // parse back to instance
+        Wrapper wrapper = (Wrapper) yaml.load(output);
+        JavaBeanWithStaticState bean2 = wrapper.createBean();
+        assertEquals(-47, bean2.getAge());
+        assertEquals("Bahrack", bean2.getName());
+    }
+
+    /**
+     * use wrapper with local tag
+     */
+    public void testLocalTag() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setName("Bahrack");
+        bean.setAge(-47);
+        JavaBeanWithStaticState.setType("Type3");
+        JavaBeanWithStaticState.color = "Violet";
+        Representer repr = new Representer();
+        repr.addClassTag(Wrapper.class, new Tag("!mybean"));
+        Yaml yaml = new Yaml(repr);
+        String output = yaml.dump(new Wrapper(bean));
+        // System.out.println(output);
+        assertEquals("!mybean {age: -47, color: Violet, name: Bahrack, type: Type3}\n", output);
+        // parse back to instance
+        Constructor constr = new Constructor();
+        TypeDescription description = new TypeDescription(Wrapper.class, new Tag("!mybean"));
+        constr.addTypeDescription(description);
+        yaml = new Yaml(constr);
+        Wrapper wrapper = (Wrapper) yaml.load(output);
+        JavaBeanWithStaticState bean2 = wrapper.createBean();
+        assertEquals(-47, bean2.getAge());
+        assertEquals("Bahrack", bean2.getName());
+    }
+
+    /**
+     * use wrapper with no tag
+     */
+    public void testRootBean() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setName("Bahrack");
+        bean.setAge(-47);
+        JavaBeanWithStaticState.setType("Type3");
+        JavaBeanWithStaticState.color = "Violet";
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(new Wrapper(bean));
+        // System.out.println(output);
+        assertEquals("age: -47\ncolor: Violet\nname: Bahrack\ntype: Type3\n", output);
+        // parse back to instance
+        Yaml loader = new Yaml();
+        Wrapper wrapper = loader.loadAs(output, Wrapper.class);
+        JavaBeanWithStaticState bean2 = wrapper.createBean();
+        assertEquals(-47, bean2.getAge());
+        assertEquals("Bahrack", bean2.getName());
+    }
+
+}
diff --git a/src/test/java/examples/staticstate/Wrapper.java b/src/test/java/examples/staticstate/Wrapper.java
new file mode 100644
index 0000000..b5c5bbb
--- /dev/null
+++ b/src/test/java/examples/staticstate/Wrapper.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package examples.staticstate;
+
+public class Wrapper {
+    private String name;
+    private int age;
+    private String color;
+    private String type;
+
+    public JavaBeanWithStaticState createBean() {
+        JavaBeanWithStaticState bean = new JavaBeanWithStaticState();
+        bean.setAge(age);
+        bean.setName(name);
+        JavaBeanWithStaticState.color = color;
+        JavaBeanWithStaticState.setType(type);
+        return bean;
+    }
+
+    public Wrapper() {
+        color = JavaBeanWithStaticState.color;
+        type = JavaBeanWithStaticState.getType();
+    }
+
+    public Wrapper(JavaBeanWithStaticState bean) {
+        this();
+        name = bean.getName();
+        age = bean.getAge();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    public String getColor() {
+        return color;
+    }
+
+    public void setColor(String color) {
+        this.color = color;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/pyyaml/AnInstance.java b/src/test/java/org/pyyaml/AnInstance.java
new file mode 100644
index 0000000..ddff4f1
--- /dev/null
+++ b/src/test/java/org/pyyaml/AnInstance.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+public class AnInstance {
+    private Object foo;
+    private Object bar;
+
+    public AnInstance() {
+    }
+
+    public AnInstance(Object foo, Object bar) {
+        this.foo = foo;
+        this.bar = bar;
+    }
+
+    public Object getFoo() {
+        return foo;
+    }
+
+    public void setFoo(Object foo) {
+        this.foo = foo;
+    }
+
+    public Object getBar() {
+        return bar;
+    }
+
+    public void setBar(Object bar) {
+        this.bar = bar;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/pyyaml/CanonicalException.java b/src/test/java/org/pyyaml/CanonicalException.java
new file mode 100644
index 0000000..3eedb88
--- /dev/null
+++ b/src/test/java/org/pyyaml/CanonicalException.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class CanonicalException extends YAMLException {
+
+    private static final long serialVersionUID = -6489045150083747626L;
+
+    public CanonicalException(String message) {
+        super(message);
+    }
+
+    public CanonicalException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/src/test/java/org/pyyaml/CanonicalLoader.java b/src/test/java/org/pyyaml/CanonicalLoader.java
new file mode 100644
index 0000000..e2d0444
--- /dev/null
+++ b/src/test/java/org/pyyaml/CanonicalLoader.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Iterator;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class CanonicalLoader extends Yaml {
+    @Override
+    public Object load(Reader yaml) {
+        try {
+            int ch = yaml.read();
+            StringBuilder buffer = new StringBuilder();
+            while (ch != -1) {
+                buffer.append((char) ch);
+                ch = yaml.read();
+            }
+            Composer composer = new Composer(new CanonicalParser(buffer.toString()), resolver);
+            constructor.setComposer(composer);
+            return constructor.getSingleData(Object.class);
+        } catch (IOException e) {
+            throw new YAMLException(e);
+        }
+    }
+
+    public Iterable<Object> loadAll(Reader yaml) {
+        try {
+            int ch = yaml.read();
+            StringBuilder buffer = new StringBuilder();
+            while (ch != -1) {
+                buffer.append((char) ch);
+                ch = yaml.read();
+            }
+            Composer composer = new Composer(new CanonicalParser(buffer.toString()), resolver);
+            this.constructor.setComposer(composer);
+            Iterator<Object> result = new Iterator<Object>() {
+                public boolean hasNext() {
+                    return constructor.checkData();
+                }
+
+                public Object next() {
+                    return constructor.getData();
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+            return new YamlIterable(result);
+        } catch (IOException e) {
+            throw new YAMLException(e);
+        }
+    }
+
+    private class YamlIterable implements Iterable<Object> {
+        private Iterator<Object> iterator;
+
+        public YamlIterable(Iterator<Object> iterator) {
+            this.iterator = iterator;
+        }
+
+        public Iterator<Object> iterator() {
+            return iterator;
+        }
+
+    }
+}
diff --git a/src/test/java/org/pyyaml/CanonicalParser.java b/src/test/java/org/pyyaml/CanonicalParser.java
new file mode 100644
index 0000000..eccd964
--- /dev/null
+++ b/src/test/java/org/pyyaml/CanonicalParser.java
@@ -0,0 +1,190 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.util.ArrayList;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.tokens.AliasToken;
+import org.yaml.snakeyaml.tokens.AnchorToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.TagToken;
+import org.yaml.snakeyaml.tokens.Token;
+
+public class CanonicalParser implements Parser {
+    private ArrayList<Event> events;
+    private boolean parsed;
+    private CanonicalScanner scanner;
+
+    public CanonicalParser(String data) {
+        events = new ArrayList<Event>();
+        parsed = false;
+        scanner = new CanonicalScanner(data);
+    }
+
+    // stream: STREAM-START document* STREAM-END
+    private void parseStream() {
+        scanner.getToken(Token.ID.StreamStart);
+        events.add(new StreamStartEvent(null, null));
+        while (!scanner.checkToken(Token.ID.StreamEnd)) {
+            if (scanner.checkToken(Token.ID.Directive, Token.ID.DocumentStart)) {
+                parseDocument();
+            } else {
+                throw new CanonicalException("document is expected, got " + scanner.tokens.get(0));
+            }
+        }
+        scanner.getToken(Token.ID.StreamEnd);
+        events.add(new StreamEndEvent(null, null));
+    }
+
+    // document: DIRECTIVE? DOCUMENT-START node
+    private void parseDocument() {
+        if (scanner.checkToken(Token.ID.Directive)) {
+            scanner.getToken(Token.ID.Directive);
+        }
+        scanner.getToken(Token.ID.DocumentStart);
+        events.add(new DocumentStartEvent(null, null, true, Version.V1_1, null));
+        parseNode();
+        events.add(new DocumentEndEvent(null, null, true));
+    }
+
+    // node: ALIAS | ANCHOR? TAG? (SCALAR|sequence|mapping)
+    private void parseNode() {
+        if (scanner.checkToken(Token.ID.Alias)) {
+            AliasToken token = (AliasToken) scanner.getToken();
+            events.add(new AliasEvent(token.getValue(), null, null));
+        } else {
+            String anchor = null;
+            if (scanner.checkToken(Token.ID.Anchor)) {
+                AnchorToken token = (AnchorToken) scanner.getToken();
+                anchor = token.getValue();
+            }
+            String tag = null;
+            if (scanner.checkToken(Token.ID.Tag)) {
+                TagToken token = (TagToken) scanner.getToken();
+                tag = token.getValue().getHandle() + token.getValue().getSuffix();
+            }
+            if (scanner.checkToken(Token.ID.Scalar)) {
+                ScalarToken token = (ScalarToken) scanner.getToken();
+                events.add(new ScalarEvent(anchor, tag, new ImplicitTuple(false, false), token
+                        .getValue(), null, null, null));
+            } else if (scanner.checkToken(Token.ID.FlowSequenceStart)) {
+                events.add(new SequenceStartEvent(anchor, tag, false, null, null, null));
+                parseSequence();
+            } else if (scanner.checkToken(Token.ID.FlowMappingStart)) {
+                events.add(new MappingStartEvent(anchor, tag, false, null, null, null));
+                parseMapping();
+            } else {
+                throw new CanonicalException("SCALAR, '[', or '{' is expected, got "
+                        + scanner.tokens.get(0));
+            }
+        }
+    }
+
+    // sequence: SEQUENCE-START (node (ENTRY node)*)? ENTRY? SEQUENCE-END
+    private void parseSequence() {
+        scanner.getToken(Token.ID.FlowSequenceStart);
+        if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
+            parseNode();
+            while (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
+                scanner.getToken(Token.ID.FlowEntry);
+                if (!scanner.checkToken(Token.ID.FlowSequenceEnd)) {
+                    parseNode();
+                }
+            }
+        }
+        scanner.getToken(Token.ID.FlowSequenceEnd);
+        events.add(new SequenceEndEvent(null, null));
+    }
+
+    // mapping: MAPPING-START (map_entry (ENTRY map_entry)*)? ENTRY? MAPPING-END
+    private void parseMapping() {
+        scanner.getToken(Token.ID.FlowMappingStart);
+        if (!scanner.checkToken(Token.ID.FlowMappingEnd)) {
+            parseMapEntry();
+            while (!scanner.checkToken(Token.ID.FlowMappingEnd)) {
+                scanner.getToken(Token.ID.FlowEntry);
+                if (!scanner.checkToken(Token.ID.FlowMappingEnd)) {
+                    parseMapEntry();
+                }
+            }
+        }
+        scanner.getToken(Token.ID.FlowMappingEnd);
+        events.add(new MappingEndEvent(null, null));
+    }
+
+    // map_entry: KEY node VALUE node
+    private void parseMapEntry() {
+        scanner.getToken(Token.ID.Key);
+        parseNode();
+        scanner.getToken(Token.ID.Value);
+        parseNode();
+    }
+
+    public void parse() {
+        parseStream();
+        parsed = true;
+    }
+
+    public Event getEvent() {
+        if (!parsed) {
+            parse();
+        }
+        return events.remove(0);
+    }
+
+    /**
+     * Check the type of the next event.
+     */
+    public boolean checkEvent(Event.ID choice) {
+        if (!parsed) {
+            parse();
+        }
+        if (!events.isEmpty()) {
+            if (events.get(0).is(choice)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the next event.
+     */
+    public Event peekEvent() {
+        if (!parsed) {
+            parse();
+        }
+        if (events.isEmpty()) {
+            return null;
+        } else {
+            return events.get(0);
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/CanonicalScanner.java b/src/test/java/org/pyyaml/CanonicalScanner.java
new file mode 100644
index 0000000..0bb8cc6
--- /dev/null
+++ b/src/test/java/org/pyyaml/CanonicalScanner.java
@@ -0,0 +1,306 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.scanner.Scanner;
+import org.yaml.snakeyaml.scanner.ScannerImpl;
+import org.yaml.snakeyaml.tokens.AliasToken;
+import org.yaml.snakeyaml.tokens.AnchorToken;
+import org.yaml.snakeyaml.tokens.DirectiveToken;
+import org.yaml.snakeyaml.tokens.DocumentStartToken;
+import org.yaml.snakeyaml.tokens.FlowEntryToken;
+import org.yaml.snakeyaml.tokens.FlowMappingEndToken;
+import org.yaml.snakeyaml.tokens.FlowMappingStartToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceEndToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceStartToken;
+import org.yaml.snakeyaml.tokens.KeyToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.StreamEndToken;
+import org.yaml.snakeyaml.tokens.StreamStartToken;
+import org.yaml.snakeyaml.tokens.TagToken;
+import org.yaml.snakeyaml.tokens.TagTuple;
+import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.tokens.ValueToken;
+
+public class CanonicalScanner implements Scanner {
+    private static final String DIRECTIVE = "%YAML 1.1";
+    private final static Map<Character, Integer> QUOTE_CODES = ScannerImpl.ESCAPE_CODES;
+
+    private final static Map<Character, String> QUOTE_REPLACES = ScannerImpl.ESCAPE_REPLACEMENTS;
+
+    private String data;
+    private int index;
+    public ArrayList<Token> tokens;
+    private boolean scanned;
+    private Mark mark;
+
+    public CanonicalScanner(String data) {
+        this.data = data + "\0";
+        this.index = 0;
+        this.tokens = new ArrayList<Token>();
+        this.scanned = false;
+        this.mark = new Mark("test", 0, 0, 0, data, 0);
+    }
+
+    public boolean checkToken(Token.ID... choices) {
+        if (!scanned) {
+            scan();
+        }
+        if (!tokens.isEmpty()) {
+            if (choices.length == 0) {
+                return true;
+            }
+            Token first = this.tokens.get(0);
+            for (Token.ID choice : choices) {
+                if (first.getTokenId() == choice) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public Token peekToken() {
+        if (!scanned) {
+            scan();
+        }
+        if (!tokens.isEmpty()) {
+            return this.tokens.get(0);
+        }
+        return null;
+    }
+
+    public Token getToken() {
+        if (!scanned) {
+            scan();
+        }
+        return this.tokens.remove(0);
+    }
+
+    public Token getToken(Token.ID choice) {
+        Token token = getToken();
+        if (choice != null && token.getTokenId() != choice) {
+            throw new CanonicalException("unexpected token " + token);
+        }
+        return token;
+    }
+
+    private void scan() {
+        this.tokens.add(new StreamStartToken(mark, mark));
+        boolean stop = false;
+        while (!stop) {
+            findToken();
+            char ch = data.charAt(index);
+            switch (ch) {
+            case '\0':
+                tokens.add(new StreamEndToken(mark, mark));
+                stop = true;
+                break;
+
+            case '%':
+                tokens.add(scanDirective());
+                break;
+
+            case '-':
+                if ("---".equals(data.substring(index, index + 3))) {
+                    index += 3;
+                    tokens.add(new DocumentStartToken(mark, mark));
+                }
+                break;
+
+            case '[':
+                index++;
+                tokens.add(new FlowSequenceStartToken(mark, mark));
+                break;
+
+            case '{':
+                index++;
+                tokens.add(new FlowMappingStartToken(mark, mark));
+                break;
+
+            case ']':
+                index++;
+                tokens.add(new FlowSequenceEndToken(mark, mark));
+                break;
+
+            case '}':
+                index++;
+                tokens.add(new FlowMappingEndToken(mark, mark));
+                break;
+
+            case '?':
+                index++;
+                tokens.add(new KeyToken(mark, mark));
+                break;
+
+            case ':':
+                index++;
+                tokens.add(new ValueToken(mark, mark));
+                break;
+
+            case ',':
+                index++;
+                tokens.add(new FlowEntryToken(mark, mark));
+                break;
+
+            case '*':
+                tokens.add(scanAlias());
+                break;
+
+            case '&':
+                tokens.add(scanAlias());
+                break;
+
+            case '!':
+                tokens.add(scanTag());
+                break;
+
+            case '"':
+                tokens.add(scanScalar());
+                break;
+
+            default:
+                throw new CanonicalException("invalid token");
+            }
+        }
+        scanned = true;
+    }
+
+    private Token scanDirective() {
+        String chunk1 = data.substring(index, index + DIRECTIVE.length());
+        char chunk2 = data.charAt(index + DIRECTIVE.length());
+        if (DIRECTIVE.equals(chunk1) && "\n\0".indexOf(chunk2) != -1) {
+            index += DIRECTIVE.length();
+            List<Integer> implicit = new ArrayList<Integer>(2);
+            implicit.add(new Integer(1));
+            implicit.add(new Integer(1));
+            return new DirectiveToken<Integer>("YAML", implicit, mark, mark);
+        } else {
+            throw new CanonicalException("invalid directive");
+        }
+    }
+
+    private Token scanAlias() {
+        boolean isTokenClassAlias;
+        if (data.charAt(index) == '*') {
+            isTokenClassAlias = true;
+        } else {
+            isTokenClassAlias = false;
+        }
+        index++;
+        int start = index;
+        while (", \n\0".indexOf(data.charAt(index)) == -1) {
+            index++;
+        }
+        String value = data.substring(start, index);
+        Token token;
+        if (isTokenClassAlias) {
+            token = new AliasToken(value, mark, mark);
+        } else {
+            token = new AnchorToken(value, mark, mark);
+        }
+        return token;
+    }
+
+    private Token scanTag() {
+        index++;
+        int start = index;
+        while (" \n\0".indexOf(data.charAt(index)) == -1) {
+            index++;
+        }
+        String value = data.substring(start, index);
+        if (value.length() == 0) {
+            value = "!";
+        } else if (value.charAt(0) == '!') {
+            value = Tag.PREFIX + value.substring(1);
+        } else if (value.charAt(0) == '<' && value.charAt(value.length() - 1) == '>') {
+            value = value.substring(1, value.length() - 1);
+        } else {
+            value = "!" + value;
+        }
+        return new TagToken(new TagTuple("", value), mark, mark);
+    }
+
+    private Token scanScalar() {
+        index++;
+        StringBuilder chunks = new StringBuilder();
+        int start = index;
+        boolean ignoreSpaces = false;
+        while (data.charAt(index) != '"') {
+            if (data.charAt(index) == '\\') {
+                ignoreSpaces = false;
+                chunks.append(data.substring(start, index));
+                index++;
+                char ch = data.charAt(index);
+                index++;
+                if (ch == '\n') {
+                    ignoreSpaces = true;
+                } else if (QUOTE_CODES.keySet().contains(ch)) {
+                    int length = QUOTE_CODES.get(ch);
+                    int code = Integer.parseInt(data.substring(index, index + length), 16);
+                    chunks.append(String.valueOf((char) code));
+                    index += length;
+                } else {
+                    if (!QUOTE_REPLACES.keySet().contains(ch)) {
+                        throw new CanonicalException("invalid escape code");
+                    }
+                    chunks.append(QUOTE_REPLACES.get(ch));
+                }
+                start = index;
+            } else if (data.charAt(index) == '\n') {
+                chunks.append(data.substring(start, index));
+                chunks.append(" ");
+                index++;
+                start = index;
+                ignoreSpaces = true;
+            } else if (ignoreSpaces && data.charAt(index) == ' ') {
+                index++;
+                start = index;
+            } else {
+                ignoreSpaces = false;
+                index++;
+            }
+        }
+        chunks.append(data.substring(start, index));
+        index++;
+        return new ScalarToken(chunks.toString(), mark, mark, false);
+    }
+
+    private void findToken() {
+        boolean found = false;
+        while (!found) {
+            while (" \t".indexOf(data.charAt(index)) != -1) {
+                index++;
+            }
+            if (data.charAt(index) == '#') {
+                while (data.charAt(index) != '\n') {
+                    index++;
+                }
+            }
+            if (data.charAt(index) == '\n') {
+                index++;
+            } else {
+                found = true;
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyCanonicalTest.java b/src/test/java/org/pyyaml/PyCanonicalTest.java
new file mode 100644
index 0000000..fc6a9e3
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyCanonicalTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.tokens.Token;
+
+/**
+ * imported from PyYAML
+ */
+public class PyCanonicalTest extends PyImportTest {
+
+    public void testCanonicalScanner() throws IOException {
+        File[] files = getStreamsByExtension(".canonical");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            InputStream input = new FileInputStream(files[i]);
+            List<Token> tokens = canonicalScan(input);
+            input.close();
+            assertFalse(tokens.isEmpty());
+        }
+    }
+
+    private List<Token> canonicalScan(InputStream input) throws IOException {
+        int ch = input.read();
+        StringBuilder buffer = new StringBuilder();
+        while (ch != -1) {
+            buffer.append((char) ch);
+            ch = input.read();
+        }
+        CanonicalScanner scanner = new CanonicalScanner(buffer.toString());
+        List<Token> result = new ArrayList<Token>();
+        while (scanner.peekToken() != null) {
+            result.add(scanner.getToken());
+        }
+        return result;
+    }
+
+    public void testCanonicalParser() throws IOException {
+        File[] files = getStreamsByExtension(".canonical");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            InputStream input = new FileInputStream(files[i]);
+            List<Event> tokens = canonicalParse(input);
+            input.close();
+            assertFalse(tokens.isEmpty());
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyEmitterTest.java b/src/test/java/org/pyyaml/PyEmitterTest.java
new file mode 100644
index 0000000..8ca80d3
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyEmitterTest.java
@@ -0,0 +1,292 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.emitter.EventConstructor;
+import org.yaml.snakeyaml.events.CollectionStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.NodeEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+/**
+ * imported from PyYAML
+ */
+public class PyEmitterTest extends PyImportTest {
+    public void testEmitterOnData() {
+        _testEmitter(".data", false);
+    }
+
+    public void testEmitterOnCanonicalNormally() {
+        _testEmitter(".canonical", false);
+    }
+
+    public void testEmitterOnCanonicalCanonically() {
+        _testEmitter(".canonical", true);
+    }
+
+    private void _testEmitter(String mask, boolean canonical) {
+        File[] files = getStreamsByExtension(mask, true);
+        assertTrue("No test files found.", files.length > 0);
+        for (File file : files) {
+            // if (!file.getName().contains("spec-06-01.canonical")) {
+            // continue;
+            // }
+            try {
+                InputStream input = new FileInputStream(file);
+                List<Event> events = parse(input);
+                input.close();
+                //
+                StringWriter stream = new StringWriter();
+                DumperOptions options = new DumperOptions();
+                options.setCanonical(canonical);
+                Emitter emitter = new Emitter(stream, options);
+                for (Event event : events) {
+                    emitter.emit(event);
+                }
+                //
+                String data = stream.toString();
+                List<Event> newEvents = new ArrayList<Event>();
+                StreamReader reader = new StreamReader(data);
+                Parser parser = new ParserImpl(reader);
+                while (parser.peekEvent() != null) {
+                    Event event = parser.getEvent();
+                    newEvents.add(event);
+                }
+                // check
+                assertEquals(events.size(), newEvents.size());
+                Iterator<Event> iter1 = events.iterator();
+                Iterator<Event> iter2 = newEvents.iterator();
+                while (iter1.hasNext()) {
+                    Event event = iter1.next();
+                    Event newEvent = iter2.next();
+                    assertEquals(event.getClass().getName(), newEvent.getClass().getName());
+                    if (event instanceof NodeEvent) {
+                        NodeEvent e1 = (NodeEvent) event;
+                        NodeEvent e2 = (NodeEvent) newEvent;
+                        assertEquals(e1.getAnchor(), e2.getAnchor());
+                    }
+                    if (event instanceof CollectionStartEvent) {
+                        CollectionStartEvent e1 = (CollectionStartEvent) event;
+                        CollectionStartEvent e2 = (CollectionStartEvent) newEvent;
+                        assertEquals(e1.getTag(), e2.getTag());
+                    }
+                    if (event instanceof ScalarEvent) {
+                        ScalarEvent e1 = (ScalarEvent) event;
+                        ScalarEvent e2 = (ScalarEvent) newEvent;
+                        if (e1.getImplicit().bothFalse() && e2.getImplicit().bothFalse()) {
+                            assertEquals(e1.getTag(), e2.getTag());
+                        }
+                        assertEquals(e1.getValue(), e2.getValue());
+                    }
+                }
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void testEmitterStyles() {
+        File[] canonicalFiles = getStreamsByExtension(".canonical", false);
+        assertTrue("No test files found.", canonicalFiles.length > 0);
+        File[] dataFiles = getStreamsByExtension(".data", true);
+        assertTrue("No test files found.", dataFiles.length > 0);
+        List<File> allFiles = new ArrayList<File>(Arrays.asList(canonicalFiles));
+        allFiles.addAll(Arrays.asList(dataFiles));
+        for (File file : allFiles) {
+            try {
+                List<Event> events = new ArrayList<Event>();
+                InputStream input = new FileInputStream(file);
+                StreamReader reader = new StreamReader(new UnicodeReader(input));
+                Parser parser = new ParserImpl(reader);
+                while (parser.peekEvent() != null) {
+                    Event event = parser.getEvent();
+                    events.add(event);
+                }
+                input.close();
+                //
+                for (Boolean flowStyle : new Boolean[] { Boolean.FALSE, Boolean.TRUE }) {
+                    for (DumperOptions.ScalarStyle style : DumperOptions.ScalarStyle.values()) {
+                        List<Event> styledEvents = new ArrayList<Event>();
+                        for (Event event : events) {
+                            if (event instanceof ScalarEvent) {
+                                ScalarEvent scalar = (ScalarEvent) event;
+                                event = new ScalarEvent(scalar.getAnchor(), scalar.getTag(),
+                                        scalar.getImplicit(), scalar.getValue(),
+                                        scalar.getStartMark(), scalar.getEndMark(), style.getChar());
+                            } else if (event instanceof SequenceStartEvent) {
+                                SequenceStartEvent seqStart = (SequenceStartEvent) event;
+                                event = new SequenceStartEvent(seqStart.getAnchor(),
+                                        seqStart.getTag(), seqStart.getImplicit(),
+                                        seqStart.getStartMark(), seqStart.getEndMark(), flowStyle);
+                            } else if (event instanceof MappingStartEvent) {
+                                MappingStartEvent mapStart = (MappingStartEvent) event;
+                                event = new MappingStartEvent(mapStart.getAnchor(),
+                                        mapStart.getTag(), mapStart.getImplicit(),
+                                        mapStart.getStartMark(), mapStart.getEndMark(), flowStyle);
+                            }
+                            styledEvents.add(event);
+                        }
+                        // emit
+                        String data = emit(styledEvents);
+                        List<Event> newEvents = parse(data);
+                        assertEquals("Events must not change. File: " + file, events.size(),
+                                newEvents.size());
+                        Iterator<Event> oldIter = events.iterator();
+                        Iterator<Event> newIter = newEvents.iterator();
+                        while (oldIter.hasNext()) {
+                            Event event = oldIter.next();
+                            Event newEvent = newIter.next();
+                            assertEquals(event.getClass(), newEvent.getClass());
+                            if (event instanceof NodeEvent) {
+                                assertEquals(((NodeEvent) event).getAnchor(),
+                                        ((NodeEvent) newEvent).getAnchor());
+                            }
+                            if (event instanceof CollectionStartEvent) {
+                                assertEquals(((CollectionStartEvent) event).getTag(),
+                                        ((CollectionStartEvent) newEvent).getTag());
+                            }
+                            if (event instanceof ScalarEvent) {
+                                ScalarEvent scalarOld = (ScalarEvent) event;
+                                ScalarEvent scalarNew = (ScalarEvent) newEvent;
+                                if (scalarOld.getImplicit().bothFalse()
+                                        && scalarNew.getImplicit().bothFalse()) {
+                                    assertEquals(scalarOld.getTag(), scalarNew.getTag());
+                                }
+                                assertEquals(scalarOld.getValue(), scalarNew.getValue());
+                            }
+                        }
+                    }
+                }
+
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private String emit(List<Event> events) throws IOException {
+        StringWriter writer = new StringWriter();
+        Emitter emitter = new Emitter(writer, new DumperOptions());
+        for (Event event : events) {
+            emitter.emit(event);
+        }
+        return writer.toString();
+    }
+
+    private List<Event> parse(String data) {
+        ParserImpl parser = new ParserImpl(new StreamReader(data));
+        List<Event> newEvents = new ArrayList<Event>();
+        while (parser.peekEvent() != null) {
+            newEvents.add(parser.getEvent());
+        }
+        return newEvents;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testEmitterEvents() {
+        File[] files = getStreamsByExtension(".events", false);
+        assertTrue("No test files found.", files.length > 0);
+        for (File file : files) {
+            // if (!file.getName().contains("spec-06-01.canonical")) {
+            // continue;
+            // }
+            try {
+                List<Event> events = new ArrayList<Event>();
+                String content = getResource(file.getName());
+                events = (List<Event>) load(new EventConstructor(), content);
+                //
+                StringWriter stream = new StringWriter();
+                Emitter emitter = new Emitter(stream, new DumperOptions());
+                for (Event event : events) {
+                    emitter.emit(event);
+                }
+                //
+                String data = stream.toString();
+                List<Event> newEvents = new ArrayList<Event>();
+                StreamReader reader = new StreamReader(data);
+                Parser parser = new ParserImpl(reader);
+                while (parser.peekEvent() != null) {
+                    Event event = parser.getEvent();
+                    newEvents.add(event);
+                }
+                // check
+                assertEquals(events.size(), newEvents.size());
+                Iterator<Event> iter1 = events.iterator();
+                Iterator<Event> iter2 = newEvents.iterator();
+                while (iter1.hasNext()) {
+                    Event event = iter1.next();
+                    Event newEvent = iter2.next();
+                    assertEquals(event.getClass().getName(), newEvent.getClass().getName());
+                    if (event instanceof NodeEvent) {
+                        NodeEvent e1 = (NodeEvent) event;
+                        NodeEvent e2 = (NodeEvent) newEvent;
+                        assertEquals(e1.getAnchor(), e2.getAnchor());
+                    }
+                    if (event instanceof CollectionStartEvent) {
+                        CollectionStartEvent e1 = (CollectionStartEvent) event;
+                        CollectionStartEvent e2 = (CollectionStartEvent) newEvent;
+                        assertEquals(e1.getTag(), e2.getTag());
+                    }
+                    if (event instanceof ScalarEvent) {
+                        ScalarEvent e1 = (ScalarEvent) event;
+                        ScalarEvent e2 = (ScalarEvent) newEvent;
+                        if (e1.getImplicit().canOmitTagInPlainScalar() == e2.getImplicit()
+                                .canOmitTagInPlainScalar()
+                                && e1.getImplicit().canOmitTagInNonPlainScalar() == e2
+                                        .getImplicit().canOmitTagInNonPlainScalar()) {
+
+                        } else {
+                            if ((e1.getTag() == null || e2.getTag() == null)
+                                    || e1.getTag().equals(e2.getTag())) {
+                            } else {
+                                System.out.println("tag1: " + e1.getTag());
+                                System.out.println("tag2: " + e2.getTag());
+                                fail("in file: " + file);
+                            }
+                        }
+                        assertEquals(e1.getValue(), e2.getValue());
+                    }
+                }
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyErrorsTest.java b/src/test/java/org/pyyaml/PyErrorsTest.java
new file mode 100644
index 0000000..1495584
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyErrorsTest.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.emitter.EventConstructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.events.Event;
+
+/**
+ * imported from PyYAML
+ */
+public class PyErrorsTest extends PyImportTest {
+    private boolean skip(String filename) {
+        List<String> failures = new ArrayList<String>();
+        // in python list cannot be a key in a dictionary.
+        failures.add("unacceptable-key.loader-error");
+        for (String name : failures) {
+            if (name.equals(filename)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void testLoaderErrors() throws FileNotFoundException {
+        File[] files = getStreamsByExtension(".loader-error");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            if (skip(files[i].getName())) {
+                continue;
+            }
+            try {
+                InputStream input = new FileInputStream(files[i]);
+                for (Object document : loadAll(input)) {
+                    assertNotNull("File " + files[i], document);
+                }
+                input.close();
+                fail("Loading must fail for " + files[i].getAbsolutePath());
+                // System.err.println("Loading must fail for " +
+                // files[i].getAbsolutePath());
+            } catch (Exception e) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    public void testLoaderStringErrors() throws FileNotFoundException {
+        File[] files = getStreamsByExtension(".loader-error");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            if (skip(files[i].getName())) {
+                continue;
+            }
+            try {
+                String content = getResource(files[i].getName());
+                for (Object document : loadAll(content.trim())) {
+                    assertNotNull(document);
+                }
+                fail("Loading must fail for " + files[i].getAbsolutePath());
+                // System.err.println("Loading must fail for " +
+                // files[i].getAbsolutePath());
+            } catch (Exception e) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    public void testLoaderSingleErrors() throws FileNotFoundException {
+        File[] files = getStreamsByExtension(".single-loader-error");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            try {
+                String content = getResource(files[i].getName());
+                load(content.trim());
+                fail("Loading must fail for " + files[i].getAbsolutePath());
+                // multiple documents must not be accepted
+                System.err.println("Loading must fail for " + files[i].getAbsolutePath());
+            } catch (YAMLException e) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testEmitterErrors() {
+        File[] files = getStreamsByExtension(".emitter-error");
+        assertTrue("No test files found.", files.length > 0);
+        for (int i = 0; i < files.length; i++) {
+            String content = getResource(files[i].getName());
+            List<Event> document = (List<Event>) load(new EventConstructor(), content.trim());
+            Writer writer = new StringWriter();
+            Emitter emitter = new Emitter(writer, new DumperOptions());
+            try {
+                for (Event event : document) {
+                    emitter.emit(event);
+                }
+                fail("Loading must fail for " + files[i].getAbsolutePath());
+                // System.err.println("Loading must fail for " +
+                // files[i].getAbsolutePath());
+            } catch (Exception e) {
+                assertTrue(true);
+            }
+        }
+    }
+
+    // testDumperErrors() is implemented in SerializerTest.java
+}
diff --git a/src/test/java/org/pyyaml/PyImportTest.java b/src/test/java/org/pyyaml/PyImportTest.java
new file mode 100644
index 0000000..966e5e7
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyImportTest.java
@@ -0,0 +1,135 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+public abstract class PyImportTest extends TestCase {
+    public static final String PATH = "pyyaml";
+
+    protected Object load(String data) {
+        Yaml yaml = new Yaml();
+        return yaml.load(data);
+    }
+
+    protected Object load(Constructor loader, String data) {
+        Yaml yaml = new Yaml(loader);
+        return yaml.load(data);
+    }
+
+    protected Iterable<Object> loadAll(InputStream data) {
+        Yaml yaml = new Yaml();
+        return yaml.loadAll(data);
+    }
+
+    protected Iterable<Object> loadAll(String data) {
+        Yaml yaml = new Yaml();
+        return yaml.loadAll(data);
+    }
+
+    protected Iterable<Object> loadAll(Constructor loader, String data) {
+        Yaml yaml = new Yaml(loader);
+        return yaml.loadAll(data);
+    }
+
+    protected String getResource(String theName) {
+        String content;
+        content = Util.getLocalResource(PATH + File.separator + theName);
+        return content;
+    }
+
+    protected File[] getStreamsByExtension(String extention) {
+        return getStreamsByExtension(extention, false);
+    }
+
+    protected File[] getStreamsByExtension(String extention, boolean onlyIfCanonicalPresent) {
+        File file = new File("src/test/resources/pyyaml");
+        assertTrue("Folder not found: " + file.getAbsolutePath(), file.exists());
+        assertTrue(file.isDirectory());
+        return file.listFiles(new PyFilenameFilter(extention, onlyIfCanonicalPresent));
+    }
+
+    protected File getFileByName(String name) {
+        File file = new File("src/test/resources/pyyaml/" + name);
+        assertTrue("Folder not found: " + file.getAbsolutePath(), file.exists());
+        assertTrue(file.isFile());
+        return file;
+    }
+
+    protected List<Event> canonicalParse(InputStream input2) throws IOException {
+        StreamReader reader = new StreamReader(new UnicodeReader(input2));
+        StringBuilder buffer = new StringBuilder();
+        while (reader.peek() != '\0') {
+            buffer.append(reader.peek());
+            reader.forward();
+        }
+        CanonicalParser parser = new CanonicalParser(buffer.toString());
+        List<Event> result = new ArrayList<Event>();
+        while (parser.peekEvent() != null) {
+            result.add(parser.getEvent());
+        }
+        input2.close();
+        return result;
+    }
+
+    protected List<Event> parse(InputStream input) throws IOException {
+        StreamReader reader = new StreamReader(new UnicodeReader(input));
+        Parser parser = new ParserImpl(reader);
+        List<Event> result = new ArrayList<Event>();
+        while (parser.peekEvent() != null) {
+            result.add(parser.getEvent());
+        }
+        input.close();
+        return result;
+    }
+
+    private class PyFilenameFilter implements FilenameFilter {
+        private String extension;
+        private boolean onlyIfCanonicalPresent;
+
+        public PyFilenameFilter(String extension, boolean onlyIfCanonicalPresent) {
+            this.extension = extension;
+            this.onlyIfCanonicalPresent = onlyIfCanonicalPresent;
+        }
+
+        public boolean accept(File dir, String name) {
+            int position = name.lastIndexOf('.');
+            String canonicalFileName = name.substring(0, position) + ".canonical";
+            File canonicalFile = new File(dir, canonicalFileName);
+            if (onlyIfCanonicalPresent && !canonicalFile.exists()) {
+                return false;
+            } else {
+                return name.endsWith(extension);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyMarkTest.java b/src/test/java/org/pyyaml/PyMarkTest.java
new file mode 100644
index 0000000..3f6f8f9
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyMarkTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import org.yaml.snakeyaml.error.Mark;
+
+/**
+ * imported from PyYAML
+ */
+public class PyMarkTest extends PyImportTest {
+
+    public void testMarks() {
+        String content = getResource("test_mark.marks");
+        String[] inputs = content.split("---\n");
+        for (int i = 1; i < inputs.length; i++) {
+            String input = inputs[i];
+            int index = 0;
+            int line = 0;
+            int column = 0;
+            while (input.charAt(index) != '*') {
+                if (input.charAt(index) != '\n') {
+                    line += 1;
+                    column = 0;
+                } else {
+                    column += 1;
+                }
+                index += 1;
+            }
+            Mark mark = new Mark("testMarks", index, line, column, input, index);
+            String snippet = mark.get_snippet(2, 79);
+            assertTrue("Must only have one '\n'.", snippet.indexOf("\n") > -1);
+            assertEquals("Must only have only one '\n'.", snippet.indexOf("\n"),
+                    snippet.lastIndexOf("\n"));
+            String[] lines = snippet.split("\n");
+            String data = lines[0];
+            String pointer = lines[1];
+            assertTrue("Mark must be restricted: " + data, data.length() < 82);
+            int dataPosition = data.indexOf("*");
+            int pointerPosition = pointer.indexOf("^");
+            assertEquals("Pointer should coincide with '*':\n " + snippet, dataPosition,
+                    pointerPosition);
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyReaderTest.java b/src/test/java/org/pyyaml/PyReaderTest.java
new file mode 100644
index 0000000..3b25189
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyReaderTest.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.reader.ReaderException;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+
+/**
+ * imported from PyYAML
+ */
+public class PyReaderTest extends PyImportTest {
+
+    public void testReaderUnicodeErrors() throws IOException {
+        File[] inputs = getStreamsByExtension(".stream-error");
+        for (int i = 0; i < inputs.length; i++) {
+            InputStream input = new FileInputStream(inputs[i]);
+            StreamReader stream = new StreamReader(new UnicodeReader(input));
+            try {
+                while (stream.peek() != '\u0000') {
+                    stream.forward();
+                }
+                fail("Invalid stream must not be accepted: " + inputs[i].getAbsolutePath()
+                        + "; encoding=" + stream.getEncoding());
+            } catch (ReaderException e) {
+                assertTrue(e.toString(),
+                        e.toString().contains(" special characters are not allowed"));
+            } catch (YAMLException e) {
+                assertTrue(e.toString(), e.toString().contains("MalformedInputException"));
+            } finally {
+                input.close();
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyRecursiveTest.java b/src/test/java/org/pyyaml/PyRecursiveTest.java
new file mode 100644
index 0000000..da353df
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyRecursiveTest.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+
+public class PyRecursiveTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testDict() {
+        Map<AnInstance, AnInstance> value = new HashMap<AnInstance, AnInstance>();
+        AnInstance instance = new AnInstance(value, value);
+        value.put(instance, instance);
+        Yaml yaml = new Yaml();
+        String output1 = yaml.dump(value);
+        assertTrue(output1.contains("!!org.pyyaml.AnInstance"));
+        assertTrue(output1.contains("&id001"));
+        assertTrue(output1.contains("&id002"));
+        assertTrue(output1.contains("*id001"));
+        assertTrue(output1.contains("*id002"));
+        assertTrue(output1.contains("foo"));
+        assertTrue(output1.contains("bar"));
+        Map<AnInstance, AnInstance> value2 = (Map<AnInstance, AnInstance>) yaml.load(output1);
+        assertEquals(value.size(), value2.size());
+        for (AnInstance tmpInstance : value2.values()) {
+            assertSame(tmpInstance.getBar(), tmpInstance.getFoo());
+            assertSame(tmpInstance.getBar(), value2);
+            assertSame(tmpInstance, value2.get(tmpInstance));
+        }
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testDictSafeConstructor() {
+        Map value = new TreeMap();
+        value.put("abc", "www");
+        value.put("qwerty", value);
+        Yaml yaml = new Yaml(new SafeConstructor());
+        String output1 = yaml.dump(value);
+        assertEquals("&id001\nabc: www\nqwerty: *id001\n", output1);
+        Map value2 = (Map) yaml.load(output1);
+        assertEquals(2, value2.size());
+        assertEquals("www", value2.get("abc"));
+        assertTrue(value2.get("qwerty") instanceof Map);
+        Map value3 = (Map) value2.get("qwerty");
+        assertTrue(value3.get("qwerty") instanceof Map);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testList() {
+        List value = new ArrayList();
+        value.add(value);
+        value.add("test");
+        value.add(new Integer(1));
+
+        Yaml yaml = new Yaml();
+        String output1 = yaml.dump(value);
+        assertEquals("&id001\n- *id001\n- test\n- 1\n", output1);
+        List value2 = (List) yaml.load(output1);
+        assertEquals(3, value2.size());
+        assertEquals(value.size(), value2.size());
+        assertSame(value2, value2.get(0));
+        // we expect self-reference as 1st element of the list
+        // let's remove self-reference and check other "simple" members of the
+        // list. otherwise assertEquals will lead us to StackOverflow
+        value.remove(0);
+        value2.remove(0);
+        assertEquals(value, value2);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testListSafeConstructor() {
+        List value = new ArrayList();
+        value.add(value);
+        value.add("test");
+        value.add(new Integer(1));
+
+        Yaml yaml = new Yaml(new SafeConstructor());
+        String output1 = yaml.dump(value);
+        assertEquals("&id001\n- *id001\n- test\n- 1\n", output1);
+        List value2 = (List) yaml.load(output1);
+        assertEquals(3, value2.size());
+        assertEquals(value.size(), value2.size());
+        assertSame(value2, value2.get(0));
+        // we expect self-reference as 1st element of the list
+        // let's remove self-reference and check other "simple" members of the
+        // list. otherwise assertEquals will lead us to StackOverflow
+        value.remove(0);
+        value2.remove(0);
+        assertEquals(value, value2);
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testSet() {
+        Set value = new HashSet();
+        value.add(new AnInstance(value, value));
+        Yaml yaml = new Yaml();
+        String output1 = yaml.dump(value);
+        Set<AnInstance> value2 = (Set<AnInstance>) yaml.load(output1);
+
+        assertEquals(value.size(), value2.size());
+        for (AnInstance tmpInstance : value2) {
+            assertSame(tmpInstance.getBar(), tmpInstance.getFoo());
+            assertSame(tmpInstance.getBar(), value2);
+        }
+    }
+
+    public void testSet2() {
+        Set<Object> set = new HashSet<Object>(3);
+        set.add("aaa");
+        set.add(111);
+        set.add(set);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(set);
+            fail("Java does not allow a recursive set to be a key for a map.");
+        } catch (StackOverflowError e) {
+            // ignore
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyStructureTest.java b/src/test/java/org/pyyaml/PyStructureTest.java
new file mode 100644
index 0000000..0d33464
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyStructureTest.java
@@ -0,0 +1,302 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.CollectionStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+/**
+ * imported from PyYAML
+ */
+public class PyStructureTest extends PyImportTest {
+
+    private void compareEvents(List<Event> events1, List<Event> events2, boolean full) {
+        assertEquals(events1.size(), events2.size());
+        Iterator<Event> iter1 = events1.iterator();
+        Iterator<Event> iter2 = events2.iterator();
+        while (iter1.hasNext()) {
+            Event event1 = iter1.next();
+            Event event2 = iter2.next();
+            assertEquals(event1.getClass(), event2.getClass());
+            if (event1 instanceof AliasEvent && full) {
+                assertEquals(((AliasEvent) event1).getAnchor(), ((AliasEvent) event2).getAnchor());
+            }
+            if (event1 instanceof CollectionStartEvent) {
+                String tag1 = ((CollectionStartEvent) event1).getTag();
+                String tag2 = ((CollectionStartEvent) event1).getTag();
+                if (tag1 != null && !"!".equals(tag1) && tag2 != null && !"!".equals(tag1)) {
+                    assertEquals(tag1, tag2);
+                }
+            }
+            if (event1 instanceof ScalarEvent) {
+                ScalarEvent scalar1 = (ScalarEvent) event1;
+                ScalarEvent scalar2 = (ScalarEvent) event2;
+                if (scalar1.getImplicit().bothFalse() && scalar2.getImplicit().bothFalse()) {
+                    assertEquals(scalar1.getTag(), scalar2.getTag());
+                }
+                assertEquals(scalar1.getValue(), scalar2.getValue());
+            }
+        }
+    }
+
+    public void testParser() {
+        File[] files = getStreamsByExtension(".data", true);
+        assertTrue("No test files found.", files.length > 0);
+        for (File file : files) {
+            if (!file.getName().contains("scan-line-b")) {
+                continue;
+            }
+            try {
+                InputStream input = new FileInputStream(file);
+                List<Event> events1 = parse(input);
+                input.close();
+                assertFalse(events1.isEmpty());
+                int index = file.getAbsolutePath().lastIndexOf('.');
+                String canonicalName = file.getAbsolutePath().substring(0, index) + ".canonical";
+                File canonical = new File(canonicalName);
+                List<Event> events2 = canonicalParse(new FileInputStream(canonical));
+                assertFalse(events2.isEmpty());
+                compareEvents(events1, events2, false);
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void testParserOnCanonical() {
+        File[] canonicalFiles = getStreamsByExtension(".canonical", false);
+        assertTrue("No test files found.", canonicalFiles.length > 0);
+        for (File file : canonicalFiles) {
+            try {
+                InputStream input = new FileInputStream(file);
+                List<Event> events1 = parse(input);
+                input.close();
+                assertFalse(events1.isEmpty());
+                List<Event> events2 = canonicalParse(new FileInputStream(file));
+                assertFalse(events2.isEmpty());
+                compareEvents(events1, events2, true);
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private void compareNodes(Node node1, Node node2) {
+        assertEquals(node1.getClass(), node2.getClass());
+        if (node1 instanceof ScalarNode) {
+            ScalarNode scalar1 = (ScalarNode) node1;
+            ScalarNode scalar2 = (ScalarNode) node2;
+            assertEquals(scalar1.getTag(), scalar2.getTag());
+            assertEquals(scalar1.getValue(), scalar2.getValue());
+        } else {
+            if (node1 instanceof SequenceNode) {
+                SequenceNode seq1 = (SequenceNode) node1;
+                SequenceNode seq2 = (SequenceNode) node2;
+                assertEquals(seq1.getTag(), seq2.getTag());
+                assertEquals(seq1.getValue().size(), seq2.getValue().size());
+                Iterator<Node> iter2 = seq2.getValue().iterator();
+                for (Node child1 : seq1.getValue()) {
+                    Node child2 = iter2.next();
+                    compareNodes(child1, child2);
+                }
+            } else {
+                MappingNode seq1 = (MappingNode) node1;
+                MappingNode seq2 = (MappingNode) node2;
+                assertEquals(seq1.getTag(), seq2.getTag());
+                assertEquals(seq1.getValue().size(), seq2.getValue().size());
+                Iterator<NodeTuple> iter2 = seq2.getValue().iterator();
+                for (NodeTuple child1 : seq1.getValue()) {
+                    NodeTuple child2 = iter2.next();
+                    compareNodes(child1.getKeyNode(), child2.getKeyNode());
+                    compareNodes(child1.getValueNode(), child2.getValueNode());
+                }
+            }
+        }
+    }
+
+    public void testComposer() {
+        File[] files = getStreamsByExtension(".data", true);
+        assertTrue("No test files found.", files.length > 0);
+        for (File file : files) {
+            try {
+                InputStream input = new FileInputStream(file);
+                List<Node> events1 = compose_all(input);
+                input.close();
+                int index = file.getAbsolutePath().lastIndexOf('.');
+                String canonicalName = file.getAbsolutePath().substring(0, index) + ".canonical";
+                File canonical = new File(canonicalName);
+                InputStream input2 = new FileInputStream(canonical);
+                List<Node> events2 = canonical_compose_all(input2);
+                input2.close();
+                assertEquals(events1.size(), events2.size());
+                Iterator<Node> iter1 = events1.iterator();
+                Iterator<Node> iter2 = events2.iterator();
+                while (iter1.hasNext()) {
+                    compareNodes(iter1.next(), iter2.next());
+                }
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private List<Node> compose_all(InputStream file) {
+        Composer composer = new Composer(new ParserImpl(new StreamReader(new UnicodeReader(file))),
+                new Resolver());
+        List<Node> documents = new ArrayList<Node>();
+        while (composer.checkNode()) {
+            documents.add(composer.getNode());
+        }
+        return documents;
+    }
+
+    private List<Node> canonical_compose_all(InputStream file) {
+        StreamReader reader = new StreamReader(new UnicodeReader(file));
+        StringBuilder buffer = new StringBuilder();
+        while (reader.peek() != '\0') {
+            buffer.append(reader.peek());
+            reader.forward();
+        }
+        CanonicalParser parser = new CanonicalParser(buffer.toString());
+        Composer composer = new Composer(parser, new Resolver());
+        List<Node> documents = new ArrayList<Node>();
+        while (composer.checkNode()) {
+            documents.add(composer.getNode());
+        }
+        return documents;
+    }
+
+    class CanonicalLoader extends Yaml {
+        public CanonicalLoader() {
+            super(new MyConstructor());
+        }
+
+        @Override
+        public Iterable<Object> loadAll(Reader yaml) {
+            StreamReader reader = new StreamReader(yaml);
+            StringBuilder buffer = new StringBuilder();
+            while (reader.peek() != '\0') {
+                buffer.append(reader.peek());
+                reader.forward();
+            }
+            CanonicalParser parser = new CanonicalParser(buffer.toString());
+            Composer composer = new Composer(parser, resolver);
+            this.constructor.setComposer(composer);
+            Iterator<Object> result = new Iterator<Object>() {
+                public boolean hasNext() {
+                    return constructor.checkData();
+                }
+
+                public Object next() {
+                    return constructor.getData();
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+            return new YamlIterable(result);
+        }
+
+        private class YamlIterable implements Iterable<Object> {
+            private Iterator<Object> iterator;
+
+            public YamlIterable(Iterator<Object> iterator) {
+                this.iterator = iterator;
+            }
+
+            public Iterator<Object> iterator() {
+                return iterator;
+            }
+
+        }
+
+    }
+
+    private class MyConstructor extends Constructor {
+        public MyConstructor() {
+            this.yamlConstructors.put(null, new ConstructUndefined());
+        }
+
+        private class ConstructUndefined extends AbstractConstruct {
+            public Object construct(Node node) {
+                return constructScalar((ScalarNode) node);
+            }
+        }
+    }
+
+    public void testConstructor() {
+        File[] files = getStreamsByExtension(".data", true);
+        assertTrue("No test files found.", files.length > 0);
+        Yaml myYaml = new Yaml(new MyConstructor());
+        Yaml canonicalYaml = new CanonicalLoader();
+        for (File file : files) {
+            try {
+                InputStream input = new FileInputStream(file);
+                Iterable<Object> documents1 = myYaml.loadAll(input);
+                int index = file.getAbsolutePath().lastIndexOf('.');
+                String canonicalName = file.getAbsolutePath().substring(0, index) + ".canonical";
+                File canonical = new File(canonicalName);
+                InputStream input2 = new FileInputStream(canonical);
+                Iterable<Object> documents2 = canonicalYaml.loadAll(input2);
+                input2.close();
+                Iterator<Object> iter2 = documents2.iterator();
+                for (Object object1 : documents1) {
+                    Object object2 = iter2.next();
+                    if (object2 != null) {
+                        assertFalse(System.identityHashCode(object1) == System
+                                .identityHashCode(object2));
+                    }
+                    assertEquals("" + object1, object1, object2);
+                }
+                input.close();
+            } catch (Exception e) {
+                System.out.println("Failed File: " + file);
+                // fail("Failed File: " + file + "; " + e.getMessage());
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/pyyaml/PyTokensTest.java b/src/test/java/org/pyyaml/PyTokensTest.java
new file mode 100644
index 0000000..646c4c2
--- /dev/null
+++ b/src/test/java/org/pyyaml/PyTokensTest.java
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.pyyaml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.reader.UnicodeReader;
+import org.yaml.snakeyaml.scanner.Scanner;
+import org.yaml.snakeyaml.scanner.ScannerImpl;
+import org.yaml.snakeyaml.tokens.AliasToken;
+import org.yaml.snakeyaml.tokens.AnchorToken;
+import org.yaml.snakeyaml.tokens.BlockEndToken;
+import org.yaml.snakeyaml.tokens.BlockEntryToken;
+import org.yaml.snakeyaml.tokens.BlockMappingStartToken;
+import org.yaml.snakeyaml.tokens.BlockSequenceStartToken;
+import org.yaml.snakeyaml.tokens.DirectiveToken;
+import org.yaml.snakeyaml.tokens.DocumentEndToken;
+import org.yaml.snakeyaml.tokens.DocumentStartToken;
+import org.yaml.snakeyaml.tokens.FlowEntryToken;
+import org.yaml.snakeyaml.tokens.FlowMappingEndToken;
+import org.yaml.snakeyaml.tokens.FlowMappingStartToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceEndToken;
+import org.yaml.snakeyaml.tokens.FlowSequenceStartToken;
+import org.yaml.snakeyaml.tokens.KeyToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.StreamEndToken;
+import org.yaml.snakeyaml.tokens.StreamStartToken;
+import org.yaml.snakeyaml.tokens.TagToken;
+import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.tokens.ValueToken;
+
+/**
+ * imported from PyYAML
+ */
+public class PyTokensTest extends PyImportTest {
+
+    public void testTokens() throws FileNotFoundException {
+        Map<Class<?>, String> replaces = new HashMap<Class<?>, String>();
+        replaces.put(DirectiveToken.class, "%");
+        replaces.put(DocumentStartToken.class, "---");
+        replaces.put(DocumentEndToken.class, "...");
+        replaces.put(AliasToken.class, "*");
+        replaces.put(AnchorToken.class, "&");
+        replaces.put(TagToken.class, "!");
+        replaces.put(ScalarToken.class, "_");
+        replaces.put(BlockSequenceStartToken.class, "[[");
+        replaces.put(BlockMappingStartToken.class, "{{");
+        replaces.put(BlockEndToken.class, "]}");
+        replaces.put(FlowSequenceStartToken.class, "[");
+        replaces.put(FlowSequenceEndToken.class, "]");
+        replaces.put(FlowMappingStartToken.class, "{");
+        replaces.put(FlowMappingEndToken.class, "}");
+        replaces.put(BlockEntryToken.class, ",");
+        replaces.put(FlowEntryToken.class, ",");
+        replaces.put(KeyToken.class, "?");
+        replaces.put(ValueToken.class, ":");
+        //
+        File[] tokensFiles = getStreamsByExtension(".tokens");
+        assertTrue("No test files found.", tokensFiles.length > 0);
+        for (int i = 0; i < tokensFiles.length; i++) {
+            String name = tokensFiles[i].getName();
+            int position = name.lastIndexOf('.');
+            String dataName = name.substring(0, position) + ".data";
+            //
+            String tokenFileData = getResource(name);
+            String[] split = tokenFileData.split("\\s+");
+            List<String> tokens2 = new ArrayList<String>();
+            for (int j = 0; j < split.length; j++) {
+                tokens2.add(split[j]);
+            }
+            //
+            List<String> tokens1 = new ArrayList<String>();
+            StreamReader reader = new StreamReader(new UnicodeReader(new FileInputStream(
+                    getFileByName(dataName))));
+            Scanner scanner = new ScannerImpl(reader);
+            try {
+                while (scanner.checkToken(new Token.ID[0])) {
+                    Token token = scanner.getToken();
+                    if (!(token instanceof StreamStartToken || token instanceof StreamEndToken)) {
+                        String replacement = replaces.get(token.getClass());
+                        tokens1.add(replacement);
+                    }
+                }
+                assertEquals(tokenFileData, tokens1.size(), tokens2.size());
+                assertEquals(tokens1, tokens2);
+            } catch (RuntimeException e) {
+                System.out.println("File name: \n" + tokensFiles[i].getName());
+                String data = getResource(tokensFiles[i].getName());
+                System.out.println("Data: \n" + data);
+                System.out.println("Tokens:");
+                for (String token : tokens1) {
+                    System.out.println(token);
+                }
+                fail("Cannot scan: " + tokensFiles[i]);
+            }
+        }
+    }
+
+    public void testScanner() throws IOException {
+        File[] files = getStreamsByExtension(".data", true);
+        assertTrue("No test files found.", files.length > 0);
+        for (File file : files) {
+            List<String> tokens = new ArrayList<String>();
+            InputStream input = new FileInputStream(file);
+            StreamReader reader = new StreamReader(new UnicodeReader(input));
+            Scanner scanner = new ScannerImpl(reader);
+            try {
+                while (scanner.checkToken(new Token.ID[0])) {
+                    Token token = scanner.getToken();
+                    tokens.add(token.getClass().getName());
+                }
+            } catch (RuntimeException e) {
+                System.out.println("File name: \n" + file.getName());
+                String data = getResource(file.getName());
+                System.out.println("Data: \n" + data);
+                System.out.println("Tokens:");
+                for (String token : tokens) {
+                    System.out.println(token);
+                }
+                fail("Cannot scan: " + file + "; " + e.getMessage());
+            } finally {
+                input.close();
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Address.java b/src/test/java/org/yaml/snakeyaml/Address.java
new file mode 100644
index 0000000..0413a17
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Address.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public class Address {
+    public String lines;
+    public String city;
+    public String state;
+    public String postal;
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/BinaryBean.java b/src/test/java/org/yaml/snakeyaml/BinaryBean.java
new file mode 100644
index 0000000..589e800
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/BinaryBean.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public class BinaryBean {
+    byte[] data;
+    int id;
+
+    public byte[] getData() {
+        return data;
+    }
+
+    public void setData(byte[] data) {
+        this.data = data;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/BinaryJavaBeanTest.java b/src/test/java/org/yaml/snakeyaml/BinaryJavaBeanTest.java
new file mode 100644
index 0000000..77815fb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/BinaryJavaBeanTest.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import junit.framework.TestCase;
+
+public class BinaryJavaBeanTest extends TestCase {
+    public void testBeanTest() {
+        BinaryBean bean = new BinaryBean();
+        bean.setId(1);
+        byte[] bytes = new byte[] { 1, 7, 9, 31, 65 };
+        bean.setData(bytes);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(bean);
+        String etalon = "!!org.yaml.snakeyaml.BinaryBean\ndata: !!binary |-\n  AQcJH0E=\nid: 1\n";
+        assertEquals(etalon, output);
+        // load
+        BinaryBean bean2 = (BinaryBean) yaml.load(output);
+        assertEquals(1, bean2.getId());
+        assertEquals(new String(bytes), new String(bean2.getData()));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_1Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_1Test.java
new file mode 100644
index 0000000..6bc4d1c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_1Test.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test Chapter 2.1 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Chapter2_1Test extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_1() {
+        YamlDocument document = new YamlDocument("example2_1.yaml");
+        List<String> list = (List<String>) document.getNativeData();
+        assertEquals(3, list.size());
+        assertEquals("Mark McGwire", list.get(0));
+        assertEquals("Sammy Sosa", list.get(1));
+        assertEquals("Ken Griffey", list.get(2));
+        assertEquals("[Mark McGwire, Sammy Sosa, Ken Griffey]\n", document.getPresentation());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_2() {
+        YamlDocument document = new YamlDocument("example2_2.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(3, map.size());
+        assertEquals("Expect 65 to be a Integer", Integer.class, map.get("hr").getClass());
+        assertEquals(new Integer(65), map.get("hr"));
+        assertEquals(new Float(0.278), new Float("0.278"));
+        assertEquals("Expect 0.278 to be a Float", Double.class, map.get("avg").getClass());
+        assertEquals(new Double(0.278), map.get("avg"));
+        assertEquals("Expect 147 to be an Integer", Integer.class, map.get("rbi").getClass());
+        assertEquals(new Integer(147), map.get("rbi"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_3() {
+        YamlDocument document = new YamlDocument("example2_3.yaml");
+        Map<String, List<String>> map = (Map<String, List<String>>) document.getNativeData();
+        assertEquals(2, map.size());
+        List<String> list1 = map.get("american");
+        assertEquals(3, list1.size());
+        assertEquals("Boston Red Sox", list1.get(0));
+        assertEquals("Detroit Tigers", list1.get(1));
+        assertEquals("New York Yankees", list1.get(2));
+        List<String> list2 = map.get("national");
+        assertEquals(3, list2.size());
+        assertEquals("New York Mets", list2.get(0));
+        assertEquals("Chicago Cubs", list2.get(1));
+        assertEquals("Atlanta Braves", list2.get(2));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_4() {
+        YamlDocument document = new YamlDocument("example2_4.yaml");
+        List<Map<String, Object>> list = (List<Map<String, Object>>) document.getNativeData();
+        assertEquals(2, list.size());
+        Map<String, Object> map1 = list.get(0);
+        assertEquals(3, map1.size());
+        assertEquals("Mark McGwire", map1.get("name"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_5() {
+        YamlDocument document = new YamlDocument("example2_5.yaml");
+        List<List<Object>> list = (List<List<Object>>) document.getNativeData();
+        assertEquals(3, list.size());
+        List<Object> list1 = list.get(0);
+        assertEquals(3, list1.size());
+        assertEquals("name", list1.get(0));
+        assertEquals("hr", list1.get(1));
+        assertEquals("avg", list1.get(2));
+        assertEquals(3, list.get(1).size());
+        assertEquals(3, list.get(2).size());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_6() {
+        YamlDocument document = new YamlDocument("example2_6.yaml");
+        Map<String, Map<String, Object>> map = (Map<String, Map<String, Object>>) document
+                .getNativeData();
+        assertEquals(2, map.size());
+        Map<String, Object> map1 = map.get("Mark McGwire");
+        assertEquals(2, map1.size());
+        Map<String, Object> map2 = map.get("Sammy Sosa");
+        assertEquals(2, map2.size());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_2Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_2Test.java
new file mode 100644
index 0000000..88b7ec9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_2Test.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test Chapter 2.2 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Chapter2_2Test extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_7() {
+        YamlStream resource = new YamlStream("example2_7.yaml");
+        List<Object> list = (List<Object>) resource.getNativeData();
+        assertEquals(2, list.size());
+        List<String> list1 = (List<String>) list.get(0);
+        assertEquals(3, list1.size());
+        assertEquals("Mark McGwire", list1.get(0));
+        assertEquals("Sammy Sosa", list1.get(1));
+        assertEquals("Ken Griffey", list1.get(2));
+        List<String> list2 = (List<String>) list.get(1);
+        assertEquals(2, list2.size());
+        assertEquals("Chicago Cubs", list2.get(0));
+        assertEquals("St Louis Cardinals", list2.get(1));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_8() {
+        YamlStream resource = new YamlStream("example2_8.yaml");
+        List<Object> list = (List<Object>) resource.getNativeData();
+        assertEquals(2, list.size());
+        Map<String, String> map1 = (Map<String, String>) list.get(0);
+        assertEquals(3, map1.size());
+        assertEquals(new Integer(72200), map1.get("time"));
+        assertEquals("Sammy Sosa", map1.get("player"));
+        assertEquals("strike (miss)", map1.get("action"));
+        Map<String, String> map2 = (Map<String, String>) list.get(1);
+        assertEquals(3, map2.size());
+        assertEquals(new Integer(72227), map2.get("time"));
+        assertEquals("Sammy Sosa", map2.get("player"));
+        assertEquals("grand slam", map2.get("action"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_9() {
+        YamlDocument document = new YamlDocument("example2_9.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(map.toString(), 2, map.size());
+        List<String> list1 = (List<String>) map.get("hr");
+        assertEquals(2, list1.size());
+        assertEquals("Mark McGwire", list1.get(0));
+        assertEquals("Sammy Sosa", list1.get(1));
+        List<String> list2 = (List<String>) map.get("rbi");
+        assertEquals(2, list2.size());
+        assertEquals("Sammy Sosa", list2.get(0));
+        assertEquals("Ken Griffey", list2.get(1));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_10() {
+        YamlDocument document = new YamlDocument("example2_10.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals("Examples 2.9 and 2.10 must be identical.",
+                new YamlDocument("example2_9.yaml").getNativeData(), map);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_11() {
+        YamlDocument document = new YamlDocument("example2_11.yaml");
+        Map<Object, Object> map = (Map<Object, Object>) document.getNativeData();
+        assertEquals(2, map.size());
+        for (Object key : map.keySet()) {
+            List<String> list = (List<String>) key;
+            assertEquals(2, list.size());
+        }
+    }
+
+    public void testExample_2_12() {
+        YamlDocument document = new YamlDocument("example2_12.yaml");
+        @SuppressWarnings("unchecked")
+        List<Map<Object, Object>> list = (List<Map<Object, Object>>) document.getNativeData();
+        assertEquals(3, list.size());
+        Map<Object, Object> map1 = (Map<Object, Object>) list.get(0);
+        assertEquals(2, map1.size());
+        assertEquals("Super Hoop", map1.get("item"));
+        Map<Object, Object> map2 = (Map<Object, Object>) list.get(1);
+        assertEquals(2, map2.size());
+        assertEquals("Basketball", map2.get("item"));
+        Map<Object, Object> map3 = (Map<Object, Object>) list.get(2);
+        assertEquals(2, map3.size());
+        assertEquals("Big Shoes", map3.get("item"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java
new file mode 100644
index 0000000..b4e671d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+
+/**
+ * Test Chapter 2.3 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Chapter2_3Test extends TestCase {
+
+    public void testExample_2_13() {
+        YamlDocument document = new YamlDocument("example2_13.yaml");
+        String data = (String) document.getNativeData();
+        assertEquals("\\//||\\/||\n// ||  ||__\n", data);
+    }
+
+    public void testExample_2_14() {
+        YamlDocument document = new YamlDocument("example2_14.yaml");
+        String data = (String) document.getNativeData();
+        assertEquals("Mark McGwire's year was crippled by a knee injury.", data);
+    }
+
+    public void testExample_2_15() {
+        String etalon = "Sammy Sosa completed another fine season with great stats.\n\n  63 Home Runs\n  0.288 Batting Average\n\nWhat a year!\n";
+        InputStream input = YamlDocument.class.getClassLoader().getResourceAsStream(
+                YamlDocument.ROOT + "example2_15.yaml");
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.FOLDED);
+        Yaml yaml = new Yaml(options);
+        String data = (String) yaml.load(input);
+        assertEquals(etalon, data);
+        //
+        String dumped = yaml.dump(data);
+        String etalonDumped = Util.getLocalResource("specification/example2_15_dumped.yaml");
+        assertEquals(etalonDumped, dumped);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_16() {
+        YamlDocument document = new YamlDocument("example2_16.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals(map.toString(), 3, map.size());
+        assertEquals("Mark McGwire", map.get("name"));
+        assertEquals("Mark set a major league home run record in 1998.\n",
+                map.get("accomplishment"));
+        assertEquals("65 Home Runs\n0.278 Batting Average\n", map.get("stats"));
+
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17() {
+        YamlDocument document = new YamlDocument("example2_17.yaml", false);
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals(map.toString(), 6, map.size());
+        assertEquals("Sosa did fine.\u263A", map.get("unicode"));
+        assertEquals("\b1998\t1999\t2000\n", map.get("control"));
+        assertEquals("\r\n is \r\n", map.get("hexesc"));
+        assertEquals("\"Howdy!\" he cried.", map.get("single"));
+        assertEquals(" # not a 'comment'.", map.get("quoted"));
+        assertEquals("|\\-*-/|", map.get("tie-fighter"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_unicode() {
+        YamlDocument document = new YamlDocument("example2_17_unicode.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals("Sosa did fine.\u263A", map.get("unicode"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_control() {
+        YamlDocument document = new YamlDocument("example2_17_control.yaml", false);
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals("\b1998\t1999\t2000\n", map.get("control"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_hexesc() {
+        YamlDocument document = new YamlDocument("example2_17_hexesc.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals("\r\n is \r\n", map.get("hexesc"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_single() {
+        YamlDocument document = new YamlDocument("example2_17_single.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals("\"Howdy!\" he cried.", map.get("single"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_quoted() {
+        YamlDocument document = new YamlDocument("example2_17_quoted.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals(" # not a 'comment'.", map.get("quoted"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_17_tie_fighter() {
+        YamlDocument document = new YamlDocument("example2_17_tie_fighter.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals("|\\-*-/|", map.get("tie-fighter"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_18() {
+        YamlDocument document = new YamlDocument("example2_18.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals(map.toString(), 2, map.size());
+        assertEquals("This unquoted scalar spans many lines.", map.get("plain"));
+        assertEquals("So does this quoted scalar.\n", map.get("quoted"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java
new file mode 100644
index 0000000..3d36caa
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_4Test.java
@@ -0,0 +1,182 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Test Chapter 2.4 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Chapter2_4Test extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_19() {
+        YamlDocument document = new YamlDocument("example2_19.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(5, map.size());
+        assertEquals("Expect 12345 to be an Integer.", Integer.class, map.get("canonical")
+                .getClass());
+        assertEquals(new Integer(12345), map.get("canonical"));
+        assertEquals(new Integer(12345), map.get("decimal"));
+        assertEquals(new Integer(3 * 3600 + 25 * 60 + 45), map.get("sexagesimal"));
+        assertEquals(new Integer(014), map.get("octal"));
+        assertEquals(new Integer(0xC), map.get("hexadecimal"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_20() {
+        YamlDocument document = new YamlDocument("example2_20.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(6, map.size());
+        assertEquals("Expect '1.23015e+3' to be a Double.", Double.class, map.get("canonical")
+                .getClass());
+        assertEquals(new Double(1230.15), map.get("canonical"));
+        assertEquals(new Double(12.3015e+02), map.get("exponential"));
+        assertEquals(new Double(20 * 60 + 30.15), map.get("sexagesimal"));
+        assertEquals(new Double(1230.15), map.get("fixed"));
+        assertEquals(Double.NEGATIVE_INFINITY, map.get("negative infinity"));
+        assertEquals(Double.NaN, map.get("not a number"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_21() {
+        YamlDocument document = new YamlDocument("example2_21.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(4, map.size());
+        assertNull("'~' must be parsed as 'null': " + map.get(null), map.get(null));
+        assertTrue((Boolean) map.get(Boolean.TRUE));
+        assertFalse((Boolean) map.get(Boolean.FALSE));
+        assertEquals("12345", map.get("string"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_22() {
+        YamlDocument document = new YamlDocument("example2_22.yaml");
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(4, map.size());
+        assertEquals("Expect '2001-12-15T02:59:43.1Z' to be a Date.", Date.class,
+                map.get("canonical").getClass());
+        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        cal.clear();
+        cal.set(Calendar.YEAR, 2001);
+        cal.set(Calendar.MONTH, 11); // Java's months are zero-based...
+        cal.set(Calendar.DAY_OF_MONTH, 15);
+        cal.set(Calendar.HOUR_OF_DAY, 2);
+        cal.set(Calendar.MINUTE, 59);
+        cal.set(Calendar.SECOND, 43);
+        cal.set(Calendar.MILLISECOND, 100);
+        Date date = cal.getTime();
+        assertEquals(date, map.get("canonical"));
+        assertEquals("Expect '2001-12-14t21:59:43.10-05:00' to be a Date.", Date.class,
+                map.get("iso8601").getClass());
+        assertEquals("Expect '2001-12-14 21:59:43.10 -5' to be a Date.", Date.class,
+                map.get("spaced").getClass());
+        assertEquals("Expect '2002-12-14' to be a Date.", Date.class, map.get("date").getClass());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_23_non_date() {
+        try {
+            YamlDocument document = new YamlDocument("example2_23_non_date.yaml");
+            Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+            assertEquals(1, map.size());
+            assertEquals("2002-04-28", map.get("not-date"));
+        } catch (RuntimeException e) {
+            fail("Cannot parse '!!str': 'not-date: !!str 2002-04-28'");
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_23_picture() {
+        YamlDocument document = new YamlDocument("example2_23_picture.yaml", false);
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(1, map.size());
+        byte[] picture = (byte[]) map.get("picture");
+        assertEquals((byte) 'G', picture[0]);
+        assertEquals((byte) 'I', picture[1]);
+        assertEquals((byte) 'F', picture[2]);
+    }
+
+    class SomethingConstructor extends Constructor {
+        public SomethingConstructor() {
+            this.yamlConstructors.put(new Tag("!something"), new ConstructSomething());
+        }
+
+        private class ConstructSomething extends AbstractConstruct {
+            public Object construct(Node node) {
+                // convert to upper case
+                String val = (String) constructScalar((ScalarNode) node);
+                return val.toUpperCase().replace('\n', ' ').trim();
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_23() {
+        YamlDocument document = new YamlDocument("example2_23.yaml", false,
+                new SomethingConstructor());
+        Map<String, Object> map = (Map<String, Object>) document.getNativeData();
+        assertEquals(3, map.size());
+        String special = (String) map.get("application specific tag");
+        assertEquals("THE SEMANTICS OF THE TAG ABOVE MAY BE DIFFERENT FOR DIFFERENT DOCUMENTS.",
+                special);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_25() {
+        YamlDocument document = new YamlDocument("example2_25.yaml");
+        Set<String> set = (Set<String>) document.getNativeData();
+        assertEquals(3, set.size());
+        assertTrue(set.contains("Mark McGwire"));
+        assertTrue(set.contains("Sammy Sosa"));
+        assertTrue(set.contains("Ken Griff"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_26() {
+        YamlDocument document = new YamlDocument("example2_26.yaml");
+        Map<String, String> map = (Map<String, String>) document.getNativeData();
+        assertEquals(3, map.size());
+        assertTrue(map instanceof LinkedHashMap);
+        assertEquals(new Integer(65), map.get("Mark McGwire"));
+        assertEquals(new Integer(63), map.get("Sammy Sosa"));
+        assertEquals(new Integer(58), map.get("Ken Griffy"));
+        List<String> list = new ArrayList<String>();
+        for (String key : map.keySet()) {
+            list.add(key);
+        }
+        assertEquals("Mark McGwire", list.get(0));
+        assertEquals("Sammy Sosa", list.get(1));
+        assertEquals("Ken Griffy", list.get(2));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_5Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_5Test.java
new file mode 100644
index 0000000..bfe5551
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_5Test.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test Chapter 2.5 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Chapter2_5Test extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testExample_2_28() {
+        YamlStream resource = new YamlStream("example2_28.yaml");
+        List<Object> list = (List<Object>) resource.getNativeData();
+        assertEquals(3, list.size());
+        Map<String, Object> data0 = (Map<String, Object>) list.get(0);
+        Date date = (Date) data0.get("Time");
+        assertEquals("Date: " + date, 1006545702000L, date.getTime());
+        assertEquals("ed", data0.get("User"));
+        assertEquals("This is an error message for the log file", data0.get("Warning"));
+        //
+        Map<String, Object> data1 = (Map<String, Object>) list.get(1);
+        Date date1 = (Date) data1.get("Time");
+        assertTrue("Date: " + date1, date1.after(date));
+        assertEquals("ed", data1.get("User"));
+        assertEquals("A slightly different error message.", data1.get("Warning"));
+        //
+        Map<String, Object> data3 = (Map<String, Object>) list.get(2);
+        Date date3 = (Date) data3.get("Date");
+        assertTrue("Date: " + date3, date3.after(date1));
+        assertEquals("ed", data3.get("User"));
+        assertEquals("Unknown variable \"bar\"", data3.get("Fatal"));
+        List<Map<String, String>> list3 = (List<Map<String, String>>) data3.get("Stack");
+        Map<String, String> map1 = list3.get(0);
+        assertEquals("TopClass.py", map1.get("file"));
+        assertEquals(new Integer(23), map1.get("line"));
+        assertEquals("x = MoreObject(\"345\\n\")\n", map1.get("code"));
+        Map<String, String> map2 = list3.get(1);
+        assertEquals("MoreClass.py", map2.get("file"));
+        assertEquals(new Integer(58), map2.get("line"));
+        assertEquals("foo = bar", map2.get("code"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/CollectionWithBeanYamlTest.java b/src/test/java/org/yaml/snakeyaml/CollectionWithBeanYamlTest.java
new file mode 100644
index 0000000..7b30d7b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/CollectionWithBeanYamlTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+public class CollectionWithBeanYamlTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testYamlMap() {
+        Map<String, Bean> data = new TreeMap<String, Bean>();
+        data.put("gold1", new Bean());
+        data.put("gold2", new Bean());
+
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(data);
+        assertEquals(
+                "gold1: !!org.yaml.snakeyaml.CollectionWithBeanYamlTest$Bean {a: ''}\ngold2: !!org.yaml.snakeyaml.CollectionWithBeanYamlTest$Bean {a: ''}\n",
+                output);
+        Object o = yaml.load(output);
+
+        assertTrue(o instanceof Map);
+        Map<String, Bean> m = (Map<String, Bean>) o;
+        assertTrue(m.get("gold1") instanceof Bean);
+        assertTrue("" + m.get("gold2").getClass(), m.get("gold2") instanceof Bean);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testYamlList() {
+        List<Bean> data = new ArrayList<Bean>();
+        data.add(new Bean("1"));
+        data.add(new Bean("2"));
+
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(data);
+        assertEquals(
+                "- !!org.yaml.snakeyaml.CollectionWithBeanYamlTest$Bean {a: '1'}\n- !!org.yaml.snakeyaml.CollectionWithBeanYamlTest$Bean {a: '2'}\n",
+                output);
+        Object o = yaml.load(output);
+
+        assertTrue(o instanceof List);
+        List<Bean> m = (List<Bean>) o;
+        assertEquals(2, m.size());
+        assertTrue(m.get(0) instanceof Bean);
+        assertTrue(m.get(1) instanceof Bean);
+    }
+
+    public static class Bean {
+        private String a;
+
+        public Bean() {
+            a = "";
+        }
+
+        public Bean(String value) {
+            a = value;
+        }
+
+        public String getA() {
+            return a;
+        }
+
+        public void setA(String s) {
+            a = s;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/DumperOptionsTest.java b/src/test/java/org/yaml/snakeyaml/DumperOptionsTest.java
new file mode 100644
index 0000000..f36fb7b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/DumperOptionsTest.java
@@ -0,0 +1,473 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class DumperOptionsTest extends TestCase {
+
+    public void testDefaultStyle() {
+        DumperOptions options = new DumperOptions();
+        Yaml yaml = new Yaml(options);
+        assertEquals("abc\n", yaml.dump("abc"));
+        // string which looks like integer
+        assertEquals("'123'\n", yaml.dump("123"));
+        //
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        yaml = new Yaml(options);
+        assertEquals("\"123\"\n", yaml.dump("123"));
+        //
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
+        yaml = new Yaml(options);
+        assertEquals("'123'\n", yaml.dump("123"));
+        //
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
+        yaml = new Yaml(options);
+        assertEquals("'123'\n", yaml.dump("123"));
+        assertEquals("abc\n", yaml.dump("abc"));
+        // null check
+        try {
+            options.setDefaultScalarStyle(null);
+            fail("Null must not be accepted.");
+        } catch (NullPointerException e) {
+            assertEquals("Use ScalarStyle enum.", e.getMessage());
+        }
+    }
+
+    public void testDefaultFlowStyle() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        assertEquals("[1, 2, 3]\n", yaml.dump(list));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        yaml = new Yaml(options);
+        assertEquals("[1, 2, 3]\n", yaml.dump(list));
+        //
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setPrettyFlow(true);
+        yaml = new Yaml(options);
+        assertEquals("[\n  1,\n  2,\n  3]\n", yaml.dump(list));
+        //
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        yaml = new Yaml(options);
+        assertEquals("- 1\n- 2\n- 3\n", yaml.dump(list));
+        // null check
+        try {
+            options.setDefaultFlowStyle(null);
+            fail("Null must not be accepted.");
+        } catch (NullPointerException e) {
+            assertEquals("Use FlowStyle enum.", e.getMessage());
+        }
+    }
+
+    public void testDefaultFlowStyleNested() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put("a", "b");
+        map.put("c", list);
+        String result = yaml.dump(map);
+        assertEquals("a: b\nc: [1, 2, 3]\n", result);
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        yaml = new Yaml(options);
+        assertEquals("{a: b, c: [1, 2, 3]}\n", yaml.dump(map));
+        //
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setPrettyFlow(true);
+        yaml = new Yaml(options);
+        result = yaml.dump(map);
+        assertEquals("{\n  a: b,\n  c: [\n    1,\n    2,\n    3]\n  \n}\n", result);
+        //
+        options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        yaml = new Yaml(options);
+        assertEquals("a: b\nc:\n- 1\n- 2\n- 3\n", yaml.dump(map));
+    }
+
+    public void testCanonical() {
+        Yaml yaml = new Yaml();
+        assertEquals("123\n", yaml.dump(123));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setCanonical(true);
+        yaml = new Yaml(options);
+        assertEquals("---\n!!int \"123\"\n", yaml.dump(123));
+        //
+        options = new DumperOptions();
+        options.setCanonical(false);
+        yaml = new Yaml(options);
+        assertEquals("123\n", yaml.dump(123));
+    }
+
+    public void testIndent() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        DumperOptions options = new DumperOptions();
+        options.setCanonical(true);
+        yaml = new Yaml(options);
+        assertEquals("---\n!!seq [\n  !!int \"1\",\n  !!int \"2\",\n]\n", yaml.dump(list));
+        //
+        options.setIndent(4);
+        yaml = new Yaml(options);
+        assertEquals("---\n!!seq [\n    !!int \"1\",\n    !!int \"2\",\n]\n", yaml.dump(list));
+        //
+        try {
+            options.setIndent(0);
+            fail();
+        } catch (YAMLException e) {
+            assertTrue(true);
+        }
+        try {
+            options.setIndent(-2);
+            fail();
+        } catch (YAMLException e) {
+            assertTrue(true);
+        }
+        try {
+            options.setIndent(11);
+            fail();
+        } catch (YAMLException e) {
+            assertTrue(true);
+        }
+        //
+        assertTrue(Emitter.MIN_INDENT > 0);
+        assertTrue(Emitter.MIN_INDENT < Emitter.MAX_INDENT);
+        assertTrue(Emitter.MAX_INDENT < 20);
+    }
+
+    public void testLineBreak() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        DumperOptions options = new DumperOptions();
+        options.setCanonical(true);
+        yaml = new Yaml(options);
+        assertEquals("---\n!!seq [\n  !!int \"1\",\n  !!int \"2\",\n]\n", yaml.dump(list));
+        //
+        options.setLineBreak(DumperOptions.LineBreak.WIN);
+        yaml = new Yaml(options);
+        String output = yaml.dump(list);
+        assertEquals("---\r\n!!seq [\r\n  !!int \"1\",\r\n  !!int \"2\",\r\n]\r\n", output);
+        // null check
+        try {
+            options.setLineBreak(null);
+            fail("Null must not be accepted.");
+        } catch (NullPointerException e) {
+            assertEquals("Specify line break.", e.getMessage());
+        }
+    }
+
+    public void testLineBreakForPlatform() {
+        DumperOptions.LineBreak lineBreak = DumperOptions.LineBreak.getPlatformLineBreak();
+        assertEquals("Line break must match platform's default.",
+                System.getProperty("line.separator"), lineBreak.getString());
+        //
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        DumperOptions options = new DumperOptions();
+        options.setLineBreak(DumperOptions.LineBreak.getPlatformLineBreak());
+        yaml = new Yaml(options);
+        assertEquals("[1, 2]", yaml.dump(list).trim());
+    }
+
+    public void testLineBreakForPlatformUnix() {
+        System.setProperty("line.separator", "\n");
+        assertEquals("\n", System.getProperty("line.separator"));
+        DumperOptions.LineBreak lineBreak = DumperOptions.LineBreak.getPlatformLineBreak();
+        assertEquals("Line break must match platform's default.",
+                System.getProperty("line.separator"), lineBreak.getString());
+        assertEquals("Unknown Line break must match UNIX line break.", "\n", lineBreak.getString());
+    }
+
+    public void testLineBreakForPlatformMac() {
+        System.setProperty("line.separator", "\r");
+        assertEquals("\r", System.getProperty("line.separator"));
+        DumperOptions.LineBreak lineBreak = DumperOptions.LineBreak.getPlatformLineBreak();
+        assertEquals("Line break must match platform's default.",
+                System.getProperty("line.separator"), lineBreak.getString());
+        assertEquals("Unknown Line break must match UNIX line break.", "\r", lineBreak.getString());
+    }
+
+    public void testLineBreakForPlatformWin() {
+        System.setProperty("line.separator", "\r\n");
+        assertEquals("\r\n", System.getProperty("line.separator"));
+        DumperOptions.LineBreak lineBreak = DumperOptions.LineBreak.getPlatformLineBreak();
+        assertEquals("Line break must match platform's default.",
+                System.getProperty("line.separator"), lineBreak.getString());
+        assertEquals("Unknown Line break must match UNIX line break.", "\r\n",
+                lineBreak.getString());
+    }
+
+    public void testLineBreakForPlatformUnknown() {
+        System.setProperty("line.separator", "\n\r");
+        assertEquals("\n\r", System.getProperty("line.separator"));
+        DumperOptions.LineBreak lineBreak = DumperOptions.LineBreak.getPlatformLineBreak();
+        assertEquals("Unknown Line break must match UNIX line break.", "\n", lineBreak.getString());
+    }
+
+    public void testExplicitStart() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        assertEquals("[1, 2, 3]\n", yaml.dump(list));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setExplicitStart(true);
+        yaml = new Yaml(options);
+        assertEquals("--- [1, 2, 3]\n", yaml.dump(list));
+        //
+        options.setExplicitEnd(true);
+        yaml = new Yaml(options);
+        assertEquals("--- [1, 2, 3]\n...\n", yaml.dump(list));
+    }
+
+    public void testVersion() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        assertEquals("[1, 2, 3]\n", yaml.dump(list));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setVersion(DumperOptions.Version.V1_1);
+        yaml = new Yaml(options);
+        assertEquals("%YAML 1.1\n--- [1, 2, 3]\n", yaml.dump(list));
+        //
+        options.setVersion(DumperOptions.Version.V1_0);
+        yaml = new Yaml(options);
+        assertEquals("%YAML 1.0\n--- [1, 2, 3]\n", yaml.dump(list));
+        //
+        assertEquals("Version: 1.1", DumperOptions.Version.V1_1.toString());
+    }
+
+    public void testTags() {
+        Yaml yaml = new Yaml();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        assertEquals("[1, 2, 3]\n", yaml.dump(list));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        Map<String, String> tags = new LinkedHashMap<String, String>();
+        tags.put("!foo!", "bar");
+        options.setTags(tags);
+        yaml = new Yaml(options);
+        assertEquals("%TAG !foo! bar\n--- [1, 2, 3]\n", yaml.dump(list));
+        //
+        options = new DumperOptions();
+        tags.put("!yaml!", Tag.PREFIX);
+        yaml = new Yaml(options);
+        assertEquals("foo\n", yaml.dump("foo"));
+    }
+
+    public void testAllowUnicode() {
+        Yaml yaml = new Yaml();
+        assertEquals("out: " + yaml.dump("\u00DCber"), "\u00DCber\n", yaml.dump("\u00DCber"));
+        //
+        DumperOptions options = new DumperOptions();
+        options = new DumperOptions();
+        options.setAllowUnicode(false);
+        yaml = new Yaml(options);
+        assertEquals("\"\\xdcber\"\n", yaml.dump("\u00DCber"));
+    }
+
+    public void testToString() {
+        DumperOptions.ScalarStyle scalarStyle = DumperOptions.ScalarStyle.LITERAL;
+        assertEquals("Scalar style: '|'", scalarStyle.toString());
+        //
+        DumperOptions.FlowStyle flowStyle = DumperOptions.FlowStyle.BLOCK;
+        assertEquals("Flow style: 'false'", flowStyle.toString());
+        //
+        DumperOptions.LineBreak lb = DumperOptions.LineBreak.UNIX;
+        assertEquals("Line break: UNIX", lb.toString());
+    }
+
+    public void testWithRepresenter() {
+        Representer representer = new Representer();
+        DumperOptions options = new DumperOptions();
+        options.setIndent(4);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(representer, options);
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(1);
+        list.add(2);
+        list.add(3);
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put("a", "b");
+        map.put("c", list);
+        assertEquals("a: b\nc:\n- 1\n- 2\n- 3\n", yaml.dump(map));
+    }
+
+    public void testSplitLinesDoubleQuoted() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default)
+        assertTrue(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("\"1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888\\\n  \\ 9999999999 0000000000\"\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("\"1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000\"\n", output);
+    }
+
+    public void testSplitLinesSingleQuoted() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default)
+        assertTrue(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("'1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888\n  9999999999 0000000000'\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("'1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000'\n", output);
+    }
+
+    public void testSplitLinesFolded() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.FOLDED);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default)
+        assertTrue(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals(">-\n  1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888\n  9999999999 0000000000\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals(">-\n  1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000\n", output);
+    }
+
+    public void testSplitLinesLiteral() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.LITERAL);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default) -- split lines does not apply to literal style
+        assertTrue(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("|-\n  1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000\n", output);
+    }
+
+    public void testSplitLinesPlain() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.PLAIN);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default) -- split lines does not apply to plain style
+        assertTrue(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        assertEquals("1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000\n", output);
+    }
+
+    public void testSetIndicatorIndentNegative() {
+        DumperOptions options = new DumperOptions();
+        try {
+        options.setIndicatorIndent(-1);
+            fail("Negative indent must not be accepted.");
+        } catch (YAMLException e) {
+            assertEquals("Indicator indent must be non-negative.", e.getMessage());
+        }
+    }
+
+    public void testSetIndicatorIndentTooBig() {
+        DumperOptions options = new DumperOptions();
+        try {
+            options.setIndicatorIndent(100);
+            fail("Negative indent must not be accepted.");
+        } catch (YAMLException e) {
+            assertEquals("Indicator indent must be at most Emitter.MAX_INDENT-1: 9", e.getMessage());
+        }
+    }
+
+    public void testCreateUnknownStyle() {
+        try {
+            DumperOptions.ScalarStyle.createStyle(' ');
+            fail("Negative indent must not be accepted.");
+        } catch (YAMLException e) {
+            assertEquals("Unknown scalar style character:  ", e.getMessage());
+        }
+    }
+
+    public void testCreateStyle() {
+        assertEquals(DumperOptions.ScalarStyle.DOUBLE_QUOTED, DumperOptions.ScalarStyle.createStyle('"'));
+        assertEquals(DumperOptions.ScalarStyle.SINGLE_QUOTED, DumperOptions.ScalarStyle.createStyle('\''));
+        assertEquals(DumperOptions.ScalarStyle.LITERAL, DumperOptions.ScalarStyle.createStyle('|'));
+        assertEquals(DumperOptions.ScalarStyle.FOLDED, DumperOptions.ScalarStyle.createStyle('>'));
+        assertEquals(DumperOptions.ScalarStyle.PLAIN, DumperOptions.ScalarStyle.createStyle(null));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/EnumBean.java b/src/test/java/org/yaml/snakeyaml/EnumBean.java
new file mode 100644
index 0000000..6365371
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/EnumBean.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.LinkedHashMap;
+
+public class EnumBean {
+    private int id;
+    private Suit suit;
+    private LinkedHashMap<Suit, Integer> map = new LinkedHashMap<Suit, Integer>();
+
+    public LinkedHashMap<Suit, Integer> getMap() {
+        return map;
+    }
+
+    public void setMap(LinkedHashMap<Suit, Integer> map) {
+        this.map = map;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public Suit getSuit() {
+        return suit;
+    }
+
+    public void setSuit(Suit suit) {
+        this.suit = suit;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/EnumTest.java b/src/test/java/org/yaml/snakeyaml/EnumTest.java
new file mode 100644
index 0000000..3587fac
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/EnumTest.java
@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class EnumTest extends TestCase {
+
+    // Dumping
+    public void testDumpEnum() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(Suit.CLUBS);
+        assertEquals("!!org.yaml.snakeyaml.Suit 'CLUBS'\n", output);
+    }
+
+    public void testDumpOverriddenToString() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(DumperOptions.FlowStyle.BLOCK);
+        assertEquals("!!org.yaml.snakeyaml.DumperOptions$FlowStyle 'BLOCK'\n", output);
+    }
+
+    public void testDumpEnumArray() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(Suit.values());
+        assertEquals(
+                "- !!org.yaml.snakeyaml.Suit 'CLUBS'\n- !!org.yaml.snakeyaml.Suit 'DIAMONDS'\n- !!org.yaml.snakeyaml.Suit 'HEARTS'\n- !!org.yaml.snakeyaml.Suit 'SPADES'\n",
+                output);
+    }
+
+    public void testDumpEnumList() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        List<Suit> list = Arrays.asList(Suit.values());
+        String output = yaml.dump(list);
+        assertEquals(
+                "- !!org.yaml.snakeyaml.Suit 'CLUBS'\n- !!org.yaml.snakeyaml.Suit 'DIAMONDS'\n- !!org.yaml.snakeyaml.Suit 'HEARTS'\n- !!org.yaml.snakeyaml.Suit 'SPADES'\n",
+                output);
+    }
+
+    public void testDumpEnumListNoAnchor() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        List<Suit> list = new ArrayList<Suit>(3);
+        list.add(Suit.CLUBS);
+        list.add(Suit.DIAMONDS);
+        list.add(Suit.CLUBS);
+        String output = yaml.dump(list);
+        assertEquals(
+                "- !!org.yaml.snakeyaml.Suit 'CLUBS'\n- !!org.yaml.snakeyaml.Suit 'DIAMONDS'\n- !!org.yaml.snakeyaml.Suit 'CLUBS'\n",
+                output);
+    }
+
+    public void testDumpEnumMap() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        Map<String, Suit> map = new LinkedHashMap<String, Suit>();
+        map.put("c", Suit.CLUBS);
+        map.put("d", Suit.DIAMONDS);
+        String output = yaml.dump(map);
+        assertEquals(
+                "c: !!org.yaml.snakeyaml.Suit 'CLUBS'\nd: !!org.yaml.snakeyaml.Suit 'DIAMONDS'\n",
+                output);
+    }
+
+    public void testDumpEnumMap2() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        Map<Suit, Integer> map = new EnumMap<Suit, Integer>(Suit.class);
+        map.put(Suit.CLUBS, 0);
+        map.put(Suit.DIAMONDS, 123);
+        String output = yaml.dump(map);
+        assertEquals(
+                "!!org.yaml.snakeyaml.Suit 'CLUBS': 0\n!!org.yaml.snakeyaml.Suit 'DIAMONDS': 123\n",
+                output);
+    }
+
+    public void testDumpEnumBean() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        EnumBean bean = new EnumBean();
+        bean.setId(17);
+        bean.setSuit(Suit.SPADES);
+        LinkedHashMap<Suit, Integer> map = new LinkedHashMap<Suit, Integer>();
+        map.put(Suit.CLUBS, 1);
+        map.put(Suit.DIAMONDS, 2);
+        bean.setMap(map);
+        String output = yaml.dump(bean);
+        assertEquals(
+                "!!org.yaml.snakeyaml.EnumBean\nid: 17\nmap:\n  CLUBS: 1\n  DIAMONDS: 2\nsuit: SPADES\n",
+                output);
+    }
+
+    // Loading
+    public void testLoadEnum() {
+        Yaml yaml = new Yaml();
+        Suit suit = (Suit) yaml.load("!!org.yaml.snakeyaml.Suit 'CLUBS'\n");
+        assertEquals(Suit.CLUBS, suit);
+    }
+
+    public void testLoadOverridenToString() {
+        Yaml yaml = new Yaml();
+        assertEquals(DumperOptions.FlowStyle.BLOCK,
+                yaml.load("!!org.yaml.snakeyaml.DumperOptions$FlowStyle 'BLOCK'\n"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadEnumList() {
+        Yaml yaml = new Yaml();
+        List<Suit> list = (List<Suit>) yaml
+                .load("- !!org.yaml.snakeyaml.Suit 'CLUBS'\n- !!org.yaml.snakeyaml.Suit 'DIAMONDS'\n- !!org.yaml.snakeyaml.Suit 'HEARTS'\n- !!org.yaml.snakeyaml.Suit 'SPADES'");
+        assertEquals(4, list.size());
+        assertEquals(Suit.CLUBS, list.get(0));
+        assertEquals(Suit.DIAMONDS, list.get(1));
+        assertEquals(Suit.HEARTS, list.get(2));
+        assertEquals(Suit.SPADES, list.get(3));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadEnumMap() {
+        Yaml yaml = new Yaml();
+        Map<Integer, Suit> map = (Map<Integer, Suit>) yaml
+                .load("1: !!org.yaml.snakeyaml.Suit 'HEARTS'\n2: !!org.yaml.snakeyaml.Suit 'DIAMONDS'");
+        assertEquals(2, map.size());
+        assertEquals(Suit.HEARTS, map.get(1));
+        assertEquals(Suit.DIAMONDS, map.get(2));
+    }
+
+    public void testLoadEnumBean() {
+        Yaml yaml = new Yaml();
+        EnumBean bean = (EnumBean) yaml
+                .load("!!org.yaml.snakeyaml.EnumBean\nid: 174\nmap:\n  !!org.yaml.snakeyaml.Suit 'CLUBS': 1\n  !!org.yaml.snakeyaml.Suit 'DIAMONDS': 2\nsuit: CLUBS");
+
+        LinkedHashMap<Suit, Integer> map = new LinkedHashMap<Suit, Integer>();
+        map.put(Suit.CLUBS, 1);
+        map.put(Suit.DIAMONDS, 2);
+
+        assertEquals(Suit.CLUBS, bean.getSuit());
+        assertEquals(174, bean.getId());
+        assertEquals(map, bean.getMap());
+    }
+
+    public void testLoadEnumBean2() {
+        Constructor c = new Constructor();
+        TypeDescription td = new TypeDescription(EnumBean.class);
+        td.putMapPropertyType("map", Suit.class, Object.class);
+        c.addTypeDescription(td);
+        Yaml yaml = new Yaml(c);
+        EnumBean bean = (EnumBean) yaml
+                .load("!!org.yaml.snakeyaml.EnumBean\nid: 174\nmap:\n  CLUBS: 1\n  DIAMONDS: 2\nsuit: CLUBS");
+
+        LinkedHashMap<Suit, Integer> map = new LinkedHashMap<Suit, Integer>();
+        map.put(Suit.CLUBS, 1);
+        map.put(Suit.DIAMONDS, 2);
+
+        assertEquals(Suit.CLUBS, bean.getSuit());
+        assertEquals(174, bean.getId());
+        assertEquals(map, bean.getMap());
+    }
+
+    public void testLoadWrongEnum() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("1: !!org.yaml.snakeyaml.Suit 'HEARTS'\n2: !!org.yaml.snakeyaml.Suit 'KOSYR'");
+            fail("KOSYR is not Suit");
+        } catch (Exception e) {
+            assertTrue("KOSYR must be reported",
+                    e.getMessage().contains("Unable to find enum value 'KOSYR' for enum"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Example2_24Test.java b/src/test/java/org/yaml/snakeyaml/Example2_24Test.java
new file mode 100644
index 0000000..aa54464
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Example2_24Test.java
@@ -0,0 +1,247 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * Test Example 2.24 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Example2_24Test extends TestCase {
+    class MyConstructor extends Constructor {
+        public MyConstructor() {
+            this.yamlConstructors.put(new Tag("tag:clarkevans.com,2002:shape"),
+                    new ConstructShape());
+            this.yamlConstructors.put(new Tag("tag:clarkevans.com,2002:circle"),
+                    new ConstructCircle());
+            this.yamlConstructors.put(new Tag("tag:clarkevans.com,2002:line"), new ConstructLine());
+            this.yamlConstructors.put(new Tag("tag:clarkevans.com,2002:label"),
+                    new ConstructLabel());
+        }
+
+        private class ConstructShape extends AbstractConstruct {
+            @SuppressWarnings("unchecked")
+            public Object construct(Node node) {
+                SequenceNode snode = (SequenceNode) node;
+                List<Entity> values = (List<Entity>) constructSequence(snode);
+                Shape shape = new Shape(values);
+                return shape;
+            }
+        }
+
+        private class ConstructCircle extends AbstractConstruct {
+            @SuppressWarnings("unchecked")
+            public Object construct(Node node) {
+                MappingNode mnode = (MappingNode) node;
+                Map<Object, Object> values = constructMapping(mnode);
+                Circle circle = new Circle((Map<String, Integer>) values.get("center"),
+                        (Integer) values.get("radius"));
+                return circle;
+            }
+        }
+
+        private class ConstructLine extends AbstractConstruct {
+            @SuppressWarnings("unchecked")
+            public Object construct(Node node) {
+                MappingNode mnode = (MappingNode) node;
+                Map<Object, Object> values = constructMapping(mnode);
+                Line line = new Line((Map<String, Integer>) values.get("start"),
+                        (Map<String, Integer>) values.get("finish"));
+                return line;
+            }
+        }
+
+        private class ConstructLabel extends AbstractConstruct {
+            @SuppressWarnings("unchecked")
+            public Object construct(Node node) {
+                MappingNode mnode = (MappingNode) node;
+                Map<Object, Object> values = constructMapping(mnode);
+                Label label = new Label((Map<String, Integer>) values.get("start"),
+                        (Integer) values.get("color"), (String) values.get("text"));
+                return label;
+            }
+        }
+    }
+
+    class MyRepresenter extends Representer {
+        public MyRepresenter() {
+            this.representers.put(Shape.class, new RepresentShape());
+            this.representers.put(Circle.class, new RepresentCircle());
+            this.representers.put(Line.class, new RepresentLine());
+            this.representers.put(Label.class, new RepresentLabel());
+            this.representers.put(HexInteger.class, new RepresentHex());
+        }
+
+        private class RepresentShape implements Represent {
+            public Node representData(Object data) {
+                Shape shape = (Shape) data;
+                List<Entity> value = shape.getEntities();
+                return representSequence(new Tag("!shape"), value, Boolean.FALSE);
+            }
+        }
+
+        private class RepresentCircle implements Represent {
+            public Node representData(Object data) {
+                Circle circle = (Circle) data;
+                Map<String, Object> map = new TreeMap<String, Object>();
+                map.put("center", circle.getCenter());
+                map.put("radius", circle.getRadius());
+                return representMapping(new Tag("!circle"), map, Boolean.FALSE);
+            }
+        }
+
+        private class RepresentLine implements Represent {
+            public Node representData(Object data) {
+                Line line = (Line) data;
+                Map<String, Object> map = new TreeMap<String, Object>();
+                map.put("start", line.getStart());
+                map.put("finish", line.getFinish());
+                return representMapping(new Tag("!line"), map, Boolean.FALSE);
+            }
+        }
+
+        private class RepresentLabel implements Represent {
+            public Node representData(Object data) {
+                Label label = (Label) data;
+                Map<String, Object> map = new TreeMap<String, Object>();
+                map.put("start", label.getStart());
+                map.put("color", new HexInteger(label.getColor()));
+                map.put("text", label.getText());
+                return representMapping(new Tag("!label"), map, Boolean.FALSE);
+            }
+        }
+
+        private class RepresentHex implements Represent {
+            public Node representData(Object data) {
+                HexInteger hex = (HexInteger) data;
+                return representScalar(Tag.INT, "0x"
+                        + Integer.toHexString(hex.getColor()).toUpperCase(), null);
+            }
+        }
+    }
+
+    private class HexInteger {
+        private Integer color;
+
+        public HexInteger(Integer color) {
+            this.color = color;
+        }
+
+        public Integer getColor() {
+            return color;
+        }
+    }
+
+    private class Shape {
+        private List<Entity> entities;
+
+        public List<Entity> getEntities() {
+            return entities;
+        }
+
+        public Shape(List<Entity> entities) {
+            this.entities = entities;
+        }
+    }
+
+    private class Entity {
+    }
+
+    private class Circle extends Entity {
+        private Map<String, Integer> center;
+        private Integer radius;
+
+        public Circle(Map<String, Integer> center, Integer radius) {
+            this.center = center;
+            this.radius = radius;
+        }
+
+        public Map<String, Integer> getCenter() {
+            return center;
+        }
+
+        public Integer getRadius() {
+            return radius;
+        }
+    }
+
+    private class Line extends Entity {
+        private Map<String, Integer> start;
+        private Map<String, Integer> finish;
+
+        public Line(Map<String, Integer> start, Map<String, Integer> finish) {
+            this.start = start;
+            this.finish = finish;
+        }
+
+        public Map<String, Integer> getStart() {
+            return start;
+        }
+
+        public Map<String, Integer> getFinish() {
+            return finish;
+        }
+    }
+
+    private class Label extends Entity {
+        private Map<String, Integer> start;
+        private Integer color;
+        private String text;
+
+        public Label(Map<String, Integer> start, Integer color, String text) {
+            this.start = start;
+            this.color = color;
+            this.text = text;
+        }
+
+        public Map<String, Integer> getStart() {
+            return start;
+        }
+
+        public Integer getColor() {
+            return color;
+        }
+
+        public String getText() {
+            return text;
+        }
+    }
+
+    public void testExample_2_24() {
+        Yaml yaml = new Yaml(new MyConstructor());
+        Shape shape = (Shape) yaml.load(Util.getLocalResource("specification/example2_24.yaml"));
+        assertNotNull(shape);
+        yaml = new Yaml(new MyRepresenter());
+        String output = yaml.dump(shape);
+        String etalon = Util.getLocalResource("specification/example2_24_dumped.yaml");
+        assertEquals(etalon, output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Example2_27Test.java b/src/test/java/org/yaml/snakeyaml/Example2_27Test.java
new file mode 100644
index 0000000..e51be0f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Example2_27Test.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.Constructor;
+
+/**
+ * Test Example 2.27 from the YAML specification
+ * 
+ * @see <a href="http://yaml.org/spec/1.1/"></a>
+ */
+public class Example2_27Test extends TestCase {
+
+    public void testExample_2_27() {
+        Yaml yaml = new Yaml(new Constructor(Invoice.class));
+        Invoice invoice = (Invoice) yaml.load(Util
+                .getLocalResource("specification/example2_27.yaml"));
+        assertNotNull(invoice);
+        Person billTo = invoice.billTo;
+        assertEquals("Dumars", billTo.family);
+        yaml = new Yaml();
+        String output = yaml.dump(invoice);
+        String etalon = Util.getLocalResource("specification/example2_27_dumped.yaml");
+        assertEquals(etalon, output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/InputOutputExceptionTest.java b/src/test/java/org/yaml/snakeyaml/InputOutputExceptionTest.java
new file mode 100644
index 0000000..d309b54
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/InputOutputExceptionTest.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Writer;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class InputOutputExceptionTest extends TestCase {
+    public void testIOExceptionOnLoad() {
+        try {
+            new Yaml().load(new BrokenInputStream());
+            fail("Input must be broken.");
+        } catch (YAMLException e) {
+            assertTrue(e.getCause() instanceof IOException);
+            assertEquals("java.io.IOException: Broken 2", e.getMessage());
+        }
+    }
+
+    public void testIOExceptionOnDump() {
+        try {
+            new Yaml().dump("something", new BrokenWriter());
+            fail("Output must be broken.");
+        } catch (YAMLException e) {
+            assertTrue(e.getCause() instanceof IOException);
+            assertEquals("java.io.IOException: Broken 12", e.getMessage());
+        }
+    }
+
+    private static class BrokenInputStream extends InputStream {
+        @Override
+        public int read() throws IOException {
+            throw new IOException("Broken 1");
+        }
+
+        @Override
+        public int read(byte[] bytes, int i, int i1) throws IOException {
+            throw new IOException("Broken 2");
+        }
+
+        @Override
+        public void close() throws IOException {
+            throw new IOException("Broken 3");
+        }
+    }
+
+    private static class BrokenWriter extends Writer {
+        @Override
+        public void close() throws IOException {
+            throw new IOException("Broken 10");
+        }
+
+        @Override
+        public void flush() throws IOException {
+            throw new IOException("Broken 11");
+        }
+
+        @Override
+        public void write(char[] cbuf, int off, int len) throws IOException {
+            throw new IOException("Broken 12");
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Invoice.java b/src/test/java/org/yaml/snakeyaml/Invoice.java
new file mode 100644
index 0000000..0e80775
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Invoice.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.List;
+
+public class Invoice {
+    public Integer invoice; // invoice
+    public String date; // date
+    public Person billTo;// bill-to
+    public Person shipTo;// ship-to
+    public List<Product> product;
+    public Float tax;
+    public Float total;
+    public String comments;
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanTimeStampTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanTimeStampTest.java
new file mode 100644
index 0000000..90a456c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanTimeStampTest.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.sql.Date;
+import java.sql.Timestamp;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+
+public class JavaBeanTimeStampTest extends TestCase {
+    public void testLoadDefaultJavaSqlTimestamp() {
+        JavaBeanWithSqlTimestamp javaBeanToDump = new JavaBeanWithSqlTimestamp();
+        Timestamp stamp = new Timestamp(1000000000000L);
+        javaBeanToDump.setTimestamp(stamp);
+        Date date = new Date(1001376000000L);
+        javaBeanToDump.setDate(date);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String dumpStr = yaml.dump(javaBeanToDump);
+        assertEquals(
+                "!!org.yaml.snakeyaml.JavaBeanWithSqlTimestamp\ndate: 2001-09-25T00:00:00Z\ntimestamp: 2001-09-09T01:46:40Z\n",
+                dumpStr);
+        Yaml loader = new Yaml();
+        JavaBeanWithSqlTimestamp javaBeanToLoad = loader.loadAs(dumpStr,
+                JavaBeanWithSqlTimestamp.class);
+        assertEquals(stamp, javaBeanToLoad.getTimestamp());
+        assertEquals(date, javaBeanToLoad.getDate());
+    }
+
+    public void testLoadDefaultJavaSqlTimestampNoGlobalTag() {
+        JavaBeanWithSqlTimestamp javaBeanToDump = new JavaBeanWithSqlTimestamp();
+        Timestamp stamp = new Timestamp(1000000000000L);
+        javaBeanToDump.setTimestamp(stamp);
+        Date date = new Date(1001376000000L);
+        javaBeanToDump.setDate(date);
+        Yaml yaml = new Yaml();
+        String dumpStr = yaml.dumpAsMap(javaBeanToDump);
+        assertEquals("date: 2001-09-25T00:00:00Z\ntimestamp: 2001-09-09T01:46:40Z\n", dumpStr);
+        Yaml loader = new Yaml();
+        JavaBeanWithSqlTimestamp javaBeanToLoad = loader.loadAs(dumpStr,
+                JavaBeanWithSqlTimestamp.class);
+        assertEquals(stamp, javaBeanToLoad.getTimestamp());
+        assertEquals(date, javaBeanToLoad.getDate());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java
new file mode 100644
index 0000000..83b8d81
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValues.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+public class JavaBeanWithNullValues {
+    private String string;
+    private Integer integer;
+    private Float float1;
+    private Double double1;
+    private Long long1;
+    private Date date;
+    private java.sql.Date sqlDate;
+    private Timestamp timestamp;
+    private Boolean boolean1;
+
+    public JavaBeanWithNullValues() {
+    }
+
+    public String getString() {
+        return string;
+    }
+
+    public void setString(String string) {
+        this.string = string;
+    }
+
+    public Integer getInteger() {
+        return integer;
+    }
+
+    public void setInteger(Integer integer) {
+        this.integer = integer;
+    }
+
+    public Float getFloat1() {
+        return float1;
+    }
+
+    public void setFloat1(Float float1) {
+        this.float1 = float1;
+    }
+
+    public Double getDouble1() {
+        return double1;
+    }
+
+    public void setDouble1(Double double1) {
+        this.double1 = double1;
+    }
+
+    public Long getLong1() {
+        return long1;
+    }
+
+    public void setLong1(Long long1) {
+        this.long1 = long1;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+
+    public java.sql.Date getSqlDate() {
+        return sqlDate;
+    }
+
+    public void setSqlDate(java.sql.Date sqlDate) {
+        this.sqlDate = sqlDate;
+    }
+
+    public Timestamp getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public Boolean getBoolean1() {
+        return boolean1;
+    }
+
+    public void setBoolean1(Boolean boolean1) {
+        this.boolean1 = boolean1;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
new file mode 100644
index 0000000..dc63f48
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithNullValuesTest.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.sql.Timestamp;
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class JavaBeanWithNullValuesTest extends TestCase {
+    private Yaml loader;
+
+    @Override
+    protected void setUp() {
+        loader = new Yaml();
+    }
+
+    public void testNotNull() {
+        String dumpStr = dumpJavaBeanWithNullValues(false);
+        // System.out.println(dumpStr);
+        Yaml yaml = new Yaml();
+        JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+        assertNotNull(parsed.getString());
+        assertNotNull(parsed.getBoolean1());
+        assertNotNull(parsed.getDate());
+        assertNotNull(parsed.getDouble1());
+        assertNotNull(parsed.getFloat1());
+        assertNotNull(parsed.getInteger());
+        assertNotNull(parsed.getLong1());
+        assertNotNull(parsed.getSqlDate());
+        assertNotNull(parsed.getTimestamp());
+        //
+        parsed = loader.loadAs(dumpStr, JavaBeanWithNullValues.class);
+        assertNotNull(parsed.getString());
+        assertNotNull(parsed.getBoolean1());
+        assertNotNull(parsed.getDate());
+        assertNotNull(parsed.getDouble1());
+        assertNotNull(parsed.getFloat1());
+        assertNotNull(parsed.getInteger());
+        assertNotNull(parsed.getLong1());
+        assertNotNull(parsed.getSqlDate());
+        assertNotNull(parsed.getTimestamp());
+    }
+
+    public void testNull() {
+        String dumpStr = dumpJavaBeanWithNullValues(true);
+        Yaml yaml = new Yaml();
+        JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+        assertNull(parsed.getString());
+        //
+        parsed = loader.loadAs(dumpStr, JavaBeanWithNullValues.class);
+        assertNull(parsed.getString());
+    }
+
+    public void testNullStringAndBoolean() {
+        JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setExplicitStart(true);
+        options.setExplicitEnd(true);
+        Yaml yaml = new Yaml(new CustomRepresenter(), options);
+        javaBeanWithNullValues.setBoolean1(null);
+        javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setDouble1(1d);
+        javaBeanWithNullValues.setFloat1(1f);
+        javaBeanWithNullValues.setInteger(1);
+        javaBeanWithNullValues.setLong1(1l);
+        javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setString(null); // ok
+        javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+
+        String dumpStr = yaml.dump(javaBeanWithNullValues);
+        // System.out.println(dumpStr);
+        yaml = new Yaml();
+        JavaBeanWithNullValues parsed = (JavaBeanWithNullValues) yaml.load(dumpStr);
+        assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
+        assertNull(" expect null, got " + parsed.getString(), parsed.getString());
+    }
+
+    public void testNoRootTag() {
+        JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setExplicitStart(true);
+        options.setExplicitEnd(true);
+        Yaml yaml = new Yaml(new CustomRepresenter(), options);
+        javaBeanWithNullValues.setBoolean1(null);
+        javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setDouble1(1d);
+        javaBeanWithNullValues.setFloat1(1f);
+        javaBeanWithNullValues.setInteger(1);
+        javaBeanWithNullValues.setLong1(1l);
+        javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setString(null); // ok
+        javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+
+        String dumpStr = yaml.dumpAsMap(javaBeanWithNullValues);
+        // System.out.println(dumpStr);
+        assertFalse("No explicit root tag must be used.",
+                dumpStr.contains("JavaBeanWithNullValues"));
+        yaml = new Yaml(new CustomRepresenter(), options);
+        JavaBeanWithNullValues parsed = loader.loadAs(dumpStr, JavaBeanWithNullValues.class);
+        assertNull(" expect null, got " + parsed.getBoolean1(), parsed.getBoolean1());
+        assertNull(" expect null, got " + parsed.getString(), parsed.getString());
+        assertEquals(1d, parsed.getDouble1());
+        assertEquals(1f, parsed.getFloat1());
+        assertEquals(new Integer(1), parsed.getInteger());
+        assertEquals(new Long(1l), parsed.getLong1());
+    }
+
+    private String dumpJavaBeanWithNullValues(boolean nullValues) {
+        JavaBeanWithNullValues javaBeanWithNullValues = new JavaBeanWithNullValues();
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setExplicitStart(true);
+        options.setExplicitEnd(true);
+        Yaml yaml = new Yaml(new CustomRepresenter(), options);
+        if (nullValues) {
+            return yaml.dump(javaBeanWithNullValues);
+        }
+        javaBeanWithNullValues.setBoolean1(false);
+        javaBeanWithNullValues.setDate(new Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setDouble1(1d);
+        javaBeanWithNullValues.setFloat1(1f);
+        javaBeanWithNullValues.setInteger(1);
+        javaBeanWithNullValues.setLong1(1l);
+        javaBeanWithNullValues.setSqlDate(new java.sql.Date(System.currentTimeMillis()));
+        javaBeanWithNullValues.setString(""); // ok
+        javaBeanWithNullValues.setTimestamp(new Timestamp(System.currentTimeMillis()));
+        return yaml.dump(javaBeanWithNullValues);
+    }
+
+    public class CustomRepresenter extends Representer {
+        public CustomRepresenter() {
+            this.representers.put(Float.class, new RepresentFloat());
+            this.representers.put(Long.class, new RepresentLong());
+            this.representers.put(java.sql.Date.class, new RepresentDate());
+            this.representers.put(java.sql.Timestamp.class, new RepresentTime());
+        }
+
+        private class RepresentFloat implements Represent {
+            public Node representData(Object data) {
+                return representScalar(new Tag(Tag.PREFIX + "java.lang.Float"),
+                        ((Float) data).toString());
+            }
+        }
+
+        private class RepresentLong implements Represent {
+            public Node representData(Object data) {
+                return representScalar(new Tag(Tag.PREFIX + "java.lang.Long"),
+                        ((Long) data).toString());
+            }
+        }
+
+        private class RepresentDate implements Represent {
+            public Node representData(Object data) {
+                return representScalar(new Tag(Tag.PREFIX + "java.sql.Date"),
+                        ((java.sql.Date) data).toString());
+            }
+        }
+
+        private class RepresentTime implements Represent {
+            public Node representData(Object data) {
+                return representScalar(new Tag(Tag.PREFIX + "java.sql.Timestamp"),
+                        ((java.sql.Timestamp) data).toString());
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/JavaBeanWithSqlTimestamp.java b/src/test/java/org/yaml/snakeyaml/JavaBeanWithSqlTimestamp.java
new file mode 100644
index 0000000..ae31768
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/JavaBeanWithSqlTimestamp.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public class JavaBeanWithSqlTimestamp {
+    private java.sql.Timestamp timestamp;
+    private java.sql.Date date;
+
+    public java.sql.Timestamp getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(java.sql.Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public java.sql.Date getDate() {
+        return date;
+    }
+
+    public void setDate(java.sql.Date date) {
+        this.date = date;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/Person.java b/src/test/java/org/yaml/snakeyaml/Person.java
new file mode 100644
index 0000000..088130f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Person.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public class Person {
+    public String given;
+    public String family;
+    public Address address;
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/Product.java b/src/test/java/org/yaml/snakeyaml/Product.java
new file mode 100644
index 0000000..badb80d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Product.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public class Product {
+    public String sku;
+    public Integer quantity;
+    public String description;
+    public Float price;
+
+    @Override
+    public String toString() {
+        return "Product: " + sku;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/PropertyUtilsSharingTest.java b/src/test/java/org/yaml/snakeyaml/PropertyUtilsSharingTest.java
new file mode 100644
index 0000000..74eb4ba
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/PropertyUtilsSharingTest.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class PropertyUtilsSharingTest extends TestCase {
+
+    public void testYamlDefaults() {
+        Yaml yaml1 = new Yaml();
+        assertSame(yaml1.constructor.getPropertyUtils(), yaml1.representer.getPropertyUtils());
+
+        Yaml yaml2 = new Yaml(new Constructor());
+        assertSame(yaml2.constructor.getPropertyUtils(), yaml2.representer.getPropertyUtils());
+
+        Yaml yaml3 = new Yaml(new Representer());
+        assertSame(yaml3.constructor.getPropertyUtils(), yaml3.representer.getPropertyUtils());
+    }
+
+    public void testYamlConstructorWithPropertyUtils() {
+        Constructor constructor1 = new Constructor();
+        PropertyUtils pu = new PropertyUtils();
+        constructor1.setPropertyUtils(pu);
+        Yaml yaml = new Yaml(constructor1);
+        assertSame(pu, yaml.constructor.getPropertyUtils());
+        assertSame(pu, yaml.representer.getPropertyUtils());
+    }
+
+    public void testYamlRepresenterWithPropertyUtils() {
+        Representer representer2 = new Representer();
+        PropertyUtils pu = new PropertyUtils();
+        representer2.setPropertyUtils(pu);
+        Yaml yaml = new Yaml(representer2);
+        assertSame(pu, yaml.constructor.getPropertyUtils());
+        assertSame(pu, yaml.representer.getPropertyUtils());
+    }
+
+    @Test
+    public void testYamlConstructorANDRepresenterWithPropertyUtils() {
+        Constructor constructor = new Constructor();
+        PropertyUtils pu_c = new PropertyUtils();
+        constructor.setPropertyUtils(pu_c);
+        Representer representer = new Representer();
+        PropertyUtils pu_r = new PropertyUtils();
+        representer.setPropertyUtils(pu_r);
+        Yaml yaml = new Yaml(constructor, representer);
+        assertSame(pu_c, yaml.constructor.getPropertyUtils());
+        assertSame(pu_r, yaml.representer.getPropertyUtils());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Suit.java b/src/test/java/org/yaml/snakeyaml/Suit.java
new file mode 100644
index 0000000..8ae7f64
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Suit.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+public enum Suit {
+    CLUBS, DIAMONDS, HEARTS, SPADES
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/TypeDescriptionTest.java b/src/test/java/org/yaml/snakeyaml/TypeDescriptionTest.java
new file mode 100644
index 0000000..bb0e66b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/TypeDescriptionTest.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.ArrayTagsTest.CarWithArray;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class TypeDescriptionTest extends TestCase {
+
+    public void testSetTag() {
+        TypeDescription descr = new TypeDescription(TypeDescriptionTest.class);
+        descr.setTag("!bla");
+        assertEquals(new Tag("!bla"), descr.getTag());
+        descr.setTag(new Tag("!foo"));
+        assertEquals(new Tag("!foo"), descr.getTag());
+    }
+
+    public void testToString() {
+        TypeDescription carDescription = new TypeDescription(CarWithArray.class, "!car");
+        assertEquals(
+                "TypeDescription for class org.yaml.snakeyaml.constructor.ArrayTagsTest$CarWithArray (tag='!car')",
+                carDescription.toString());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/Util.java b/src/test/java/org/yaml/snakeyaml/Util.java
new file mode 100644
index 0000000..61d9fc3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/Util.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class Util {
+
+    public static String getLocalResource(String theName) {
+        try {
+            InputStream input;
+            input = YamlDocument.class.getClassLoader().getResourceAsStream(theName);
+            if (input == null) {
+                throw new RuntimeException("Can not find " + theName);
+            }
+            BufferedInputStream is = new BufferedInputStream(input);
+            StringBuilder buf = new StringBuilder(3000);
+            int i;
+            try {
+                while ((i = is.read()) != -1) {
+                    buf.append((char) i);
+                }
+            } finally {
+                is.close();
+            }
+            String resource = buf.toString();
+            // convert EOLs
+            String[] lines = resource.split("\\r?\\n");
+            StringBuilder buffer = new StringBuilder();
+            for (int j = 0; j < lines.length; j++) {
+                buffer.append(lines[j]);
+                buffer.append("\n");
+            }
+            return buffer.toString();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/YamlComposeTest.java b/src/test/java/org/yaml/snakeyaml/YamlComposeTest.java
new file mode 100644
index 0000000..c6ac047
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/YamlComposeTest.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+
+public class YamlComposeTest extends TestCase {
+
+    public void testComposeManyDocuments() {
+        try {
+            Yaml yaml = new Yaml();
+            yaml.compose(new StringReader("abc: 56\n---\n123\n---\n456"));
+            fail("YAML contans more then one document.");
+        } catch (Exception e) {
+            assertTrue("<<<" + e.getMessage() + ">>>",
+                    e.getMessage().startsWith("expected a single document in the stream"));
+        }
+    }
+
+    public void testComposeFromReader() {
+        Yaml yaml = new Yaml();
+        MappingNode node = (MappingNode) yaml.compose(new StringReader("abc: 56"));
+        ScalarNode node1 = (ScalarNode) node.getValue().get(0).getKeyNode();
+        assertEquals("abc", node1.getValue());
+        ScalarNode node2 = (ScalarNode) node.getValue().get(0).getValueNode();
+        assertEquals("56", node2.getValue());
+    }
+
+    public void testComposeAllFromReader() {
+        Yaml yaml = new Yaml();
+        boolean first = true;
+        for (Node node : yaml.composeAll(new StringReader("abc: 56\n---\n123\n---\n456"))) {
+            if (first) {
+                assertEquals(NodeId.mapping, node.getNodeId());
+            } else {
+                assertEquals(NodeId.scalar, node.getNodeId());
+            }
+            first = false;
+        }
+    }
+
+    public void testComposeAllOneDocument() {
+        Yaml yaml = new Yaml();
+        for (Node node : yaml.composeAll(new StringReader("6"))) {
+            assertEquals(NodeId.scalar, node.getNodeId());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/YamlDocument.java b/src/test/java/org/yaml/snakeyaml/YamlDocument.java
new file mode 100644
index 0000000..fbf5104
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/YamlDocument.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class YamlDocument {
+    public static final String ROOT = "specification/";
+    private String source;
+    private String presentation;
+    private Object nativeData;
+
+    public YamlDocument(String sourceName, boolean check, Constructor constructor) {
+        InputStream input = YamlDocument.class.getClassLoader().getResourceAsStream(
+                ROOT + sourceName);
+        if (constructor == null) {
+            constructor = new Constructor();
+        }
+        Yaml yaml = new Yaml(constructor);
+        nativeData = yaml.load(input);
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        Charset charset = Charset.forName("UTF-8");
+        yaml.dump(nativeData, new OutputStreamWriter(output, charset));
+        try {
+            presentation = output.toString(charset.name());
+            source = Util.getLocalResource(ROOT + sourceName);
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        // try to read generated presentation to prove that the presentation
+        // is identical to the source
+        Object result = yaml.load(presentation);
+        if (check && !nativeData.equals(result)) {
+            throw new RuntimeException("Generated presentation is not valid: " + presentation);
+        }
+    }
+
+    public YamlDocument(String sourceName, boolean check) {
+        this(sourceName, check, null);
+    }
+
+    public YamlDocument(String sourceName) {
+        this(sourceName, true);
+    }
+
+    public String getSource() {
+        return source;
+    }
+
+    public String getPresentation() {
+        return presentation;
+    }
+
+    public Object getNativeData() {
+        if (nativeData == null) {
+            throw new NullPointerException("No object is parsed.");
+        }
+        return nativeData;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/YamlParseTest.java b/src/test/java/org/yaml/snakeyaml/YamlParseTest.java
new file mode 100644
index 0000000..4de8db4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/YamlParseTest.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.events.*;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class YamlParseTest extends TestCase {
+
+    public void testParse() {
+        Yaml yaml = new Yaml();
+        Event e = null;
+        int counter = 0;
+        for (Event event : yaml.parse(new StringReader("abc: 56"))) {
+            if (e == null) {
+                assertTrue(event instanceof StreamStartEvent);
+            }
+            e = event;
+            counter++;
+        }
+        assertTrue(e instanceof StreamEndEvent);
+        assertEquals(8, counter);
+    }
+
+    public void testParseEvents() {
+        Yaml yaml = new Yaml();
+        Iterator<Event> events = yaml.parse(new StringReader("%YAML 1.1\n---\na")).iterator();
+        assertTrue(events.next() instanceof StreamStartEvent);
+        DocumentStartEvent documentStartEvent = (DocumentStartEvent) events.next();
+        assertTrue(documentStartEvent.getExplicit());
+        assertEquals(DumperOptions.Version.V1_1, documentStartEvent.getVersion());
+        Map<String, String> DEFAULT_TAGS = new HashMap<String, String>();
+        DEFAULT_TAGS.put("!", "!");
+        DEFAULT_TAGS.put("!!", Tag.PREFIX);
+        assertEquals(DEFAULT_TAGS, documentStartEvent.getTags());
+        ScalarEvent scalarEvent = (ScalarEvent) events.next();
+        assertNull(scalarEvent.getAnchor());
+        assertNull(scalarEvent.getTag());
+        assertEquals(new ImplicitTuple(true, false).toString(), scalarEvent.getImplicit().toString());
+        DocumentEndEvent documentEndEvent = (DocumentEndEvent) events.next();
+        assertFalse(documentEndEvent.getExplicit());
+        assertTrue("Unexpected event.", events.next() instanceof StreamEndEvent);
+        assertFalse(events.hasNext());
+    }
+
+    public void testParseManyDocuments() {
+        Yaml yaml = new Yaml();
+        Event e = null;
+        int counter = 0;
+        for (Event event : yaml.parse(new StringReader("abc: 56\n---\n4\n---\nqwe\n"))) {
+            if (e == null) {
+                assertTrue(event instanceof StreamStartEvent);
+            }
+            e = event;
+            counter++;
+        }
+        assertTrue(e instanceof StreamEndEvent);
+        assertEquals(14, counter);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/YamlStream.java b/src/test/java/org/yaml/snakeyaml/YamlStream.java
new file mode 100644
index 0000000..1ccd4e7
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/YamlStream.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.AssertionFailedError;
+
+public class YamlStream {
+    private List<Object> nativeData = new ArrayList<Object>();
+
+    public YamlStream(String sourceName) {
+        InputStream input = YamlDocument.class.getClassLoader().getResourceAsStream(
+                YamlDocument.ROOT + sourceName);
+        Yaml yaml = new Yaml();
+        for (Object document : yaml.loadAll(input)) {
+            nativeData.add(document);
+        }
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        yaml.dumpAll(nativeData.iterator(), new OutputStreamWriter(output));
+        String presentation;
+        try {
+            presentation = output.toString("UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        // try to read generated presentation to prove that the presentation
+        // is identical to the source
+        List<Object> parsedNativeData = new ArrayList<Object>();
+        for (Object document : yaml.loadAll(presentation)) {
+            parsedNativeData.add(document);
+        }
+        if (nativeData.getClass() != parsedNativeData.getClass()) {
+            throw new AssertionFailedError("Different class: " + parsedNativeData.getClass());
+        }
+        if (nativeData.size() != parsedNativeData.size()) {
+            throw new AssertionFailedError("Different size.");
+        }
+        Iterator<Object> piterator = parsedNativeData.iterator();
+        Iterator<Object> niterator = nativeData.iterator();
+        while (piterator.hasNext()) {
+            Object obj1 = niterator.next();
+            Object obj2 = piterator.next();
+            if (obj1 instanceof Map) {
+                @SuppressWarnings("unchecked")
+                Map<Object, Object> map1 = (Map<Object, Object>) obj1;
+                @SuppressWarnings("unchecked")
+                Map<Object, Object> map2 = (Map<Object, Object>) obj2;
+                if (!map1.keySet().equals(map2.keySet())) {
+                    throw new AssertionFailedError("Keyset: " + map1.keySet() + "; but was: "
+                            + map2.keySet());
+                }
+                for (Iterator<Object> iterator = map1.keySet().iterator(); iterator.hasNext();) {
+                    Object key = iterator.next();
+                    Object o1 = map1.get(key);
+                    Object o2 = map2.get(key);
+                    if (!o1.equals(o2)) {
+                        throw new AssertionFailedError("Values: " + o1 + "; but was: " + o2);
+                    }
+                }
+            }
+            if (!obj1.equals(obj2)) {
+                throw new AssertionFailedError("Expected: " + obj1 + "; but was: " + obj2);
+            }
+        }
+        if (!parsedNativeData.equals(nativeData)) {
+            throw new AssertionFailedError("Generated presentation is not the same: "
+                    + presentation);
+        }
+    }
+
+    public List<Object> getNativeData() {
+        return nativeData;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/YamlTest.java b/src/test/java/org/yaml/snakeyaml/YamlTest.java
new file mode 100644
index 0000000..8f71b66
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/YamlTest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class YamlTest extends TestCase {
+
+    public void testSetNoName() {
+        Yaml yaml = new Yaml();
+        assertTrue(yaml.toString().matches("Yaml:\\d+"));
+    }
+
+    public void testSetName() {
+        Yaml yaml = new Yaml();
+        yaml.setName("REST");
+        assertEquals("REST", yaml.getName());
+        assertEquals("REST", yaml.toString());
+    }
+
+    /**
+     * Check that documents are parsed only when they are asked to be loaded.
+     */
+    public void testOneDocument() {
+        Yaml yaml = new Yaml();
+        String doc = "--- a\n--- [:]";
+        Iterator<Object> loaded = yaml.loadAll(doc).iterator();
+        assertTrue(loaded.hasNext());
+        Object obj1 = loaded.next();
+        assertEquals("a", obj1);
+        assertTrue(loaded.hasNext());
+        try {
+            loaded.next();
+            fail("Second document is invalid");
+        } catch (Exception e) {
+            assertEquals("while parsing a flow node\n" + " in 'reader', line 2, column 6:\n"
+                    + "    --- [:]\n" + "         ^\n"
+                    + "expected the node content, but found Value\n"
+                    + " in 'reader', line 2, column 6:\n" + "    --- [:]\n" + "         ^\n",
+                    e.getMessage());
+        }
+    }
+
+    public void testOnlyOneDocument() {
+        Yaml yaml = new Yaml();
+        String doc = "--- a\n--- b";
+        try {
+            yaml.load(doc);
+            fail("It must be only one document.");
+        } catch (YAMLException e) {
+            assertEquals("expected a single document in the stream\n"
+                    + " in 'string', line 1, column 5:\n" + "    --- a\n" + "        ^\n"
+                    + "but found another document\n" + " in 'string', line 2, column 1:\n"
+                    + "    --- b\n" + "    ^\n", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/BooleanArr.java b/src/test/java/org/yaml/snakeyaml/array/BooleanArr.java
new file mode 100644
index 0000000..e465950
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/BooleanArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class BooleanArr {
+    private boolean[] bools;
+
+    public BooleanArr() {
+    }
+
+    public BooleanArr(boolean[] bools) {
+        this.bools = bools;
+    }
+
+    public String toString() {
+        return Arrays.toString(bools);
+    }
+
+    public boolean[] getBools() {
+        return bools;
+    }
+
+    public void setBools(boolean[] bools) {
+        this.bools = bools;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/ByteArr.java b/src/test/java/org/yaml/snakeyaml/array/ByteArr.java
new file mode 100644
index 0000000..bf8aac5
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/ByteArr.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class ByteArr {
+
+    private byte[] bytes;
+
+    public ByteArr() {
+    }
+
+    public ByteArr(byte[] bytes) {
+        this.bytes = bytes;
+    }
+
+    public String toString() {
+        return Arrays.toString(bytes);
+    }
+
+    public byte[] getBytes() {
+        return bytes;
+    }
+
+    public void setBytes(byte[] bytes) {
+        this.bytes = bytes;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/CharArr.java b/src/test/java/org/yaml/snakeyaml/array/CharArr.java
new file mode 100644
index 0000000..7f309ee
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/CharArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class CharArr {
+    private char[] chars;
+
+    public CharArr() {
+    }
+
+    public CharArr(char[] chars) {
+        this.chars = chars;
+    }
+
+    public String toString() {
+        return Arrays.toString(chars);
+    }
+
+    public char[] getChars() {
+        return chars;
+    }
+
+    public void setChars(char[] chars) {
+        this.chars = chars;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/DoubleArr.java b/src/test/java/org/yaml/snakeyaml/array/DoubleArr.java
new file mode 100644
index 0000000..6b134a3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/DoubleArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class DoubleArr {
+    private double[] doubles;
+
+    public DoubleArr() {
+    }
+
+    public DoubleArr(double[] doubles) {
+        this.doubles = doubles;
+    }
+
+    public String toString() {
+        return Arrays.toString(doubles);
+    }
+
+    public double[] getDoubles() {
+        return doubles;
+    }
+
+    public void setDoubles(double[] doubles) {
+        this.doubles = doubles;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/FloatArr.java b/src/test/java/org/yaml/snakeyaml/array/FloatArr.java
new file mode 100644
index 0000000..01f5338
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/FloatArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class FloatArr {
+    private float[] floats;
+
+    public FloatArr() {
+    }
+
+    public FloatArr(float[] floats) {
+        this.floats = floats;
+    }
+
+    public String toString() {
+        return Arrays.toString(floats);
+    }
+
+    public float[] getFloats() {
+        return floats;
+    }
+
+    public void setFloats(float[] floats) {
+        this.floats = floats;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/IntArr.java b/src/test/java/org/yaml/snakeyaml/array/IntArr.java
new file mode 100644
index 0000000..8ed8e2a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/IntArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class IntArr {
+    private int[] ints;
+
+    public IntArr() {
+    }
+
+    public IntArr(int[] ints) {
+        this.ints = ints;
+    }
+
+    public String toString() {
+        return Arrays.toString(ints);
+    }
+
+    public int[] getInts() {
+        return ints;
+    }
+
+    public void setInts(int[] ints) {
+        this.ints = ints;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/LongArr.java b/src/test/java/org/yaml/snakeyaml/array/LongArr.java
new file mode 100644
index 0000000..c20adfe
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/LongArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class LongArr {
+    private long[] longs;
+
+    public LongArr() {
+    }
+
+    public LongArr(long[] longs) {
+        this.longs = longs;
+    }
+
+    public String toString() {
+        return Arrays.toString(longs);
+    }
+
+    public long[] getLongs() {
+        return longs;
+    }
+
+    public void setLongs(long[] longs) {
+        this.longs = longs;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/PrimitiveArrayTest.java b/src/test/java/org/yaml/snakeyaml/array/PrimitiveArrayTest.java
new file mode 100644
index 0000000..09b1d53
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/PrimitiveArrayTest.java
@@ -0,0 +1,255 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Assert;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.ConstructorException;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class PrimitiveArrayTest extends TestCase {
+
+    private final String pkg = "!!org.yaml.snakeyaml.array";
+
+    private final byte[] bytes = new byte[] { 1, 2, 3 };
+    private final short[] shorts = new short[] { 300, 301, 302 };
+    private final int[] ints = new int[] { 40000, 40001, 40002 };
+    private final long[] longs = new long[] { 5000000000L, 5000000001L };
+    private final float[] floats = new float[] { 0.1f, 3.1415f };
+    private final double[] doubles = new double[] { 50.0001, 2150.0002 };
+    private final char[] chars = new char[] { 'a', 'b', 'c', 'd', 'e' };
+    private final boolean[] bools = new boolean[] { true, false };
+
+    public void testValidConstructor() {
+        String testInput = "- " + pkg + ".ByteArr [ " + Arrays.toString(bytes) + " ]\n" + "- "
+                + pkg + ".ShortArr [ " + Arrays.toString(shorts) + " ]\n" + "- " + pkg
+                + ".IntArr [ " + Arrays.toString(ints) + " ]\n" + "- " + pkg + ".LongArr [ "
+                + Arrays.toString(longs) + " ]\n" + "- " + pkg + ".FloatArr [ "
+                + Arrays.toString(floats) + " ]\n" + "- " + pkg + ".DoubleArr [ "
+                + Arrays.toString(doubles) + " ]\n" + "- " + pkg + ".CharArr [ "
+                + Arrays.toString(chars) + " ]\n" + "- " + pkg + ".BooleanArr [ "
+                + Arrays.toString(bools) + " ]\n";
+
+        Yaml yaml = new Yaml();
+        List<Object> wrappers = (List<Object>) yaml.load(testInput);
+
+        Assert.assertArrayEquals(bytes, ((ByteArr) wrappers.get(0)).getBytes());
+        Assert.assertArrayEquals(shorts, ((ShortArr) wrappers.get(1)).getShorts());
+        Assert.assertArrayEquals(ints, ((IntArr) wrappers.get(2)).getInts());
+        Assert.assertArrayEquals(longs, ((LongArr) wrappers.get(3)).getLongs());
+        Assert.assertArrayEquals(floats, ((FloatArr) wrappers.get(4)).getFloats(), 0.001f);
+        Assert.assertArrayEquals(doubles, ((DoubleArr) wrappers.get(5)).getDoubles(), 0.001);
+        Assert.assertArrayEquals(chars, ((CharArr) wrappers.get(6)).getChars());
+        assertArrayEquals(bools, ((BooleanArr) wrappers.get(7)).getBools());
+    }
+
+    /*
+     * For some reason, every other assertArrayEquals specialization is provided
+     * by org.junit.Assert, but not this one.
+     */
+    private void assertArrayEquals(boolean[] expected, boolean[] actuals) {
+        assertEquals("Arrays differ in length", expected.length, actuals.length);
+        for (int i = 0; i < expected.length; ++i) {
+            if (expected[i] != actuals[i]) {
+                fail("Arrays first differ at " + i + "; expected " + expected[i] + " but got "
+                        + actuals[i]);
+            }
+        }
+    }
+
+    private void tryInvalid(String t, Class<?> expectedException) {
+        Yaml yaml = new Yaml();
+        try {
+            Object loaded = yaml.load(t);
+            fail("Expected " + expectedException.getCanonicalName() + " but loaded = \"" + loaded
+                    + "\"");
+        } catch (YAMLException e) {
+            assertEquals(expectedException, e.getCause().getClass());
+        }
+    }
+
+    public void testInvalidConstructors() {
+        // Loading a character as any primitive other than 'char' is a
+        // NumberFormatException
+        tryInvalid(pkg + ".ByteArr [ [ 'a' ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".ShortArr [ [ 'a' ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".IntArr [ [ 'a' ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".LongArr [ [ 'a' ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".FloatArr [ [ 'a' ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".DoubleArr [ [ 'a' ] ]", NumberFormatException.class);
+
+        // Exception: because of how boolean construction works, constructing a
+        // boolean from 'a'
+        // results in null.
+        tryInvalid(pkg + ".BooleanArr [ [ 'a' ] ]", NullPointerException.class);
+
+        // Loading a floating-point number as a character is a YAMLException
+        tryInvalid(pkg + ".CharArr [ [ 1.2 ] ]", YAMLException.class);
+
+        // Loading a String as a Character is a YAMLException
+        tryInvalid(pkg + ".CharArr [ [ 'abcd' ] ]", YAMLException.class);
+
+    }
+
+    public void testTruncation() {
+        // Loading floating-point numbers as integer types is disallowed,
+        // because that's a number-format problem.
+        tryInvalid(pkg + ".ByteArr [ [ 3.14 ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".ShortArr [ [ 3.14 ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".IntArr [ [ 3.14 ] ]", NumberFormatException.class);
+        tryInvalid(pkg + ".LongArr [ [ 3.14 ] ]", NumberFormatException.class);
+    }
+
+    public void testPromotion() {
+        Yaml yaml = new Yaml();
+
+        // Loading integer numbers as floating-point types is allowed...
+        Assert.assertArrayEquals(new float[] { 3, 5 },
+                ((FloatArr) yaml.load(pkg + ".FloatArr [ [ 3, 5 ] ] ")).getFloats(), 0.001f);
+
+        Assert.assertArrayEquals(new double[] { 3, 5 },
+                ((DoubleArr) yaml.load(pkg + ".DoubleArr [ [ 3, 5 ] ] ")).getDoubles(), 0.001f);
+    }
+
+    public void testStringCharArray() {
+        Yaml yaml = new Yaml();
+
+        try {
+            yaml.load(pkg + ".CharArr [ [ abcd ] ]");
+            fail("Expected exception.");
+        } catch (Exception e) {
+            assertEquals(ConstructorException.class, e.getClass());
+            assertEquals("Invalid node Character: 'abcd'; length: 4", e.getCause().getMessage());
+        }
+    }
+
+    private static Object cycle(Object in) {
+        Yaml yaml = new Yaml();
+        String dumped = yaml.dump(in);
+        // System.out.println ( dumped );
+        return yaml.load(dumped);
+    }
+
+    /**
+     * All kinds of primitive arrays should be able to cycle from (Java array)
+     * to (YAML string) to (Java array) again, and they should be exactly the
+     * same before and after.
+     */
+    public void testCycle() {
+        ByteArr byteArr = new ByteArr(bytes);
+        Assert.assertArrayEquals(byteArr.getBytes(), ((ByteArr) cycle(byteArr)).getBytes());
+
+        ShortArr shortArr = new ShortArr(shorts);
+        Assert.assertArrayEquals(shortArr.getShorts(), ((ShortArr) cycle(shortArr)).getShorts());
+
+        IntArr intArr = new IntArr(ints);
+        Assert.assertArrayEquals(intArr.getInts(), ((IntArr) cycle(intArr)).getInts());
+
+        LongArr longArr = new LongArr(longs);
+        Assert.assertArrayEquals(longArr.getLongs(), ((LongArr) cycle(longArr)).getLongs());
+
+        FloatArr floatArr = new FloatArr(floats);
+        Assert.assertArrayEquals(floatArr.getFloats(), ((FloatArr) cycle(floatArr)).getFloats(),
+                0.001f);
+
+        DoubleArr doubleArr = new DoubleArr(doubles);
+        Assert.assertArrayEquals(doubleArr.getDoubles(),
+                ((DoubleArr) cycle(doubleArr)).getDoubles(), 0.001);
+
+        CharArr charArr = new CharArr(chars);
+        Assert.assertArrayEquals(charArr.getChars(), ((CharArr) cycle(charArr)).getChars());
+
+        BooleanArr boolArr = new BooleanArr(bools);
+        assertArrayEquals(boolArr.getBools(), ((BooleanArr) cycle(boolArr)).getBools());
+    }
+
+    public void testMultiDimensional() {
+        Array2D two = new Array2D();
+        two.setLongs(new long[][] { { 1, 2, 3 }, { 4, 5, 6 } });
+        assertTrue(Arrays.deepEquals(two.getLongs(), ((Array2D) cycle(two)).getLongs()));
+
+        Array3D three = new Array3D();
+        three.setLongs(new long[][][] { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 } },
+                { { 9, 10, 11, 12 }, { 13, 14, 15, 16 } } });
+        assertTrue(Arrays.deepEquals(three.getLongs(), ((Array3D) cycle(three)).getLongs()));
+
+        // Object with an array of Objects which each have an array of
+        // primitives.
+        ArrayLongArr four = new ArrayLongArr();
+        four.setContents(new LongArr[] { new LongArr(new long[] { 1, 2, 3, 4 }),
+                new LongArr(new long[] { 5, 6, 7, 8 }) });
+        Object result = cycle(four);
+        assertEquals(four, (ArrayLongArr) result);
+    }
+
+    public static class Array2D {
+        private long[][] longs;
+
+        public long[][] getLongs() {
+            return longs;
+        }
+
+        public void setLongs(long[][] longs) {
+            this.longs = longs;
+        }
+    }
+
+    public static class Array3D {
+        private long[][][] longs;
+
+        public long[][][] getLongs() {
+            return longs;
+        }
+
+        public void setLongs(long[][][] longs) {
+            this.longs = longs;
+        }
+    }
+
+    public static class ArrayLongArr {
+        private LongArr[] contents;
+
+        public LongArr[] getContents() {
+            return contents;
+        }
+
+        public void setContents(LongArr[] contents) {
+            this.contents = contents;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ArrayLongArr))
+                return false;
+
+            ArrayLongArr other = ((ArrayLongArr) obj);
+            if (contents.length != other.getContents().length)
+                return false;
+            for (int i = 0; i < contents.length; ++i) {
+                if (!Arrays.equals(contents[i].getLongs(), other.getContents()[i].getLongs())) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/array/ShortArr.java b/src/test/java/org/yaml/snakeyaml/array/ShortArr.java
new file mode 100644
index 0000000..bd09bbf
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/array/ShortArr.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.array;
+
+import java.util.Arrays;
+
+public class ShortArr {
+    private short[] shorts;
+
+    public ShortArr() {
+    }
+
+    public ShortArr(short[] shorts) {
+        this.shorts = shorts;
+    }
+
+    public String toString() {
+        return Arrays.toString(shorts);
+    }
+
+    public short[] getShorts() {
+        return shorts;
+    }
+
+    public void setShorts(short[] shorts) {
+        this.shorts = shorts;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/composer/ComposerImplTest.java b/src/test/java/org/yaml/snakeyaml/composer/ComposerImplTest.java
new file mode 100644
index 0000000..038da90
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/composer/ComposerImplTest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.composer;
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+
+public class ComposerImplTest extends TestCase {
+
+    public void testGetNode() {
+        String data = "american:\n  - Boston Red Sox";
+        Yaml yaml = new Yaml();
+        Node node = yaml.compose(new StringReader(data));
+        assertNotNull(node);
+        assertTrue(node instanceof MappingNode);
+        String data2 = "---\namerican:\n- Boston Red Sox";
+        Node node2 = yaml.compose(new StringReader(data2));
+        assertNotNull(node2);
+        assertFalse(node.equals(node2));
+    }
+
+    public void testComposeBean() {
+        String data = "!!org.yaml.snakeyaml.composer.ComposerImplTest$BeanToCompose {name: Bill, age: 18}";
+        Yaml yaml = new Yaml();
+        Node node = yaml.compose(new StringReader(data));
+        assertNotNull(node);
+        assertTrue(node instanceof MappingNode);
+        assertEquals(
+                "tag:yaml.org,2002:org.yaml.snakeyaml.composer.ComposerImplTest$BeanToCompose",
+                node.getTag().getValue());
+        assertEquals(NodeId.mapping, node.getNodeId());
+        assertEquals(Object.class, node.getType());
+    }
+
+    public static class BeanToCompose {
+        private String name;
+        private int age;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getAge() {
+            return age;
+        }
+
+        public void setAge(int age) {
+            this.age = age;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/AbstractConstructTest.java b/src/test/java/org/yaml/snakeyaml/constructor/AbstractConstructTest.java
new file mode 100644
index 0000000..d0c5893
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/AbstractConstructTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+import java.util.ArrayList;
+
+public class AbstractConstructTest extends TestCase {
+
+    public void testNotRecursive() {
+        AbstractConstruct abstractConstruct = new AbstractConstruct() {
+            public Object construct(Node node) {
+                return null;
+            }
+        };
+        Node node = new SequenceNode(Tag.SEQ, true, new ArrayList<Node>(), null, null, false);
+        try {
+            abstractConstruct.construct2ndStep(node, "");
+            fail();
+        } catch (YAMLException e) {
+            assertEquals("Unexpected recursive structure for Node: <org.yaml.snakeyaml.nodes.SequenceNode (tag=tag:yaml.org,2002:seq, value=[])>", e.getMessage());
+        }
+    }
+
+    public void testRecursive() {
+        AbstractConstruct abstractConstruct = new AbstractConstruct() {
+
+            public Object construct(Node node) {
+                return null;
+            }
+        };
+        Node node = new SequenceNode(Tag.SEQ, true, new ArrayList<Node>(), null, null, false);
+        node.setTwoStepsConstruction(true);
+        try {
+            abstractConstruct.construct2ndStep(node, "");
+            fail();
+        } catch (IllegalStateException e) {
+            assertEquals("Not Implemented in org.yaml.snakeyaml.constructor.AbstractConstructTest$2", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ArrayTagsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ArrayTagsTest.java
new file mode 100644
index 0000000..ce4e250
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ArrayTagsTest.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class ArrayTagsTest extends TestCase {
+
+    public void testDefaultRepresenter() {
+        CarWithArray car = new CarWithArray();
+        car.setPlate("12-XP-F4");
+        Wheel[] wheels = new Wheel[5];
+        for (int i = 1; i < 6; i++) {
+            Wheel wheel = new Wheel();
+            wheel.setId(i);
+            wheels[i - 1] = wheel;
+        }
+        car.setWheels(wheels);
+        assertEquals(Util.getLocalResource("constructor/cararray-with-tags-flow-auto.yaml"),
+                new Yaml().dump(car));
+    }
+
+    public void testFlowBlock() {
+        CarWithArray car = new CarWithArray();
+        car.setPlate("12-XP-F4");
+        Wheel[] wheels = new Wheel[5];
+        for (int i = 1; i < 6; i++) {
+            Wheel wheel = new Wheel();
+            wheel.setId(i);
+            wheels[i - 1] = wheel;
+        }
+        car.setWheels(wheels);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        assertEquals(Util.getLocalResource("constructor/cararray-with-tags.yaml"), yaml.dump(car));
+    }
+
+    public void testLoadClassTag() {
+        Constructor constructor = new Constructor();
+        constructor.addTypeDescription(new TypeDescription(Car.class, "!car"));
+        Yaml yaml = new Yaml(constructor);
+        Car car = (Car) yaml.load(Util.getLocalResource("constructor/car-without-tags.yaml"));
+        assertEquals("12-XP-F4", car.getPlate());
+        List<Wheel> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+    }
+
+    public void testNullDescription() {
+        Constructor constructor = new Constructor();
+        try {
+            constructor.addTypeDescription(null);
+            fail("Description is required.");
+        } catch (Exception e) {
+            assertEquals("TypeDescription is required.", e.getMessage());
+        }
+    }
+
+    public void testLoadClassNoRoot() {
+        Constructor constructor = new Constructor(new TypeDescription(CarWithArray.class));
+        Yaml yaml = new Yaml(constructor);
+        CarWithArray car = (CarWithArray) yaml.load(Util
+                .getLocalResource("constructor/car-no-root-class.yaml"));
+        assertEquals("12-XP-F4", car.getPlate());
+        Wheel[] wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.length);
+    }
+
+    public static class CarWithArray {
+        private String plate;
+        private Wheel[] wheels;
+
+        public String getPlate() {
+            return plate;
+        }
+
+        public void setPlate(String plate) {
+            this.plate = plate;
+        }
+
+        public Wheel[] getWheels() {
+            return wheels;
+        }
+
+        public void setWheels(Wheel[] wheels) {
+            this.wheels = wheels;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/AtomicJavaBean.java b/src/test/java/org/yaml/snakeyaml/constructor/AtomicJavaBean.java
new file mode 100644
index 0000000..946ff25
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/AtomicJavaBean.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class AtomicJavaBean {
+    private float amount;
+    private AtomicLong atomic;
+
+    public float getAmount() {
+        return amount;
+    }
+
+    public void setAmount(float amount) {
+        this.amount = amount;
+    }
+
+    public AtomicLong getAtomic() {
+        return atomic;
+    }
+
+    public void setAtomic(AtomicLong atomic) {
+        this.atomic = atomic;
+    }
+
+    @Override
+    public String toString() {
+        return "AtomicJavaBean";
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
new file mode 100644
index 0000000..d36c733
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/BeanConstructorTest.java
@@ -0,0 +1,250 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.math.BigInteger;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class BeanConstructorTest extends TestCase {
+
+    public void testPrimitivesConstructor() {
+        Yaml yaml = new Yaml(new Constructor(TestBean1.class));
+        String document = Util.getLocalResource("constructor/test-primitives1.yaml");
+        TestBean1 result = (TestBean1) yaml.load(document);
+        assertNotNull(result);
+        assertEquals(new Byte((byte) 1), result.getByteClass());
+        assertEquals((byte) -3, result.getBytePrimitive());
+        assertEquals(new Short((short) 0), result.getShortClass());
+        assertEquals((short) -13, result.getShortPrimitive());
+        assertEquals(new Integer(5), result.getInteger());
+        assertEquals(17, result.getIntPrimitive());
+        assertEquals("the text", result.getText());
+        assertEquals("13", result.getId());
+        assertEquals(new Long(11111111111L), result.getLongClass());
+        assertEquals(9999999999L, result.getLongPrimitive());
+        assertEquals(Boolean.TRUE, result.getBooleanClass());
+        assertTrue(result.isBooleanPrimitive());
+        assertEquals(Character.valueOf('2'), result.getCharClass());
+        assertEquals('#', result.getCharPrimitive());
+        assertEquals(new BigInteger("1234567890123456789012345678901234567890"),
+                result.getBigInteger());
+        assertEquals(new Float(2), result.getFloatClass());
+        assertEquals(new Float(3.1416), result.getFloatPrimitive());
+        assertEquals(new Double(4), result.getDoubleClass());
+        assertEquals(new Double(11200), result.getDoublePrimitive());
+        assertEquals(1199836800000L, result.getDate().getTime());
+        assertEquals("public", result.publicField);
+        //
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yamlToDump = new Yaml(options);
+        String output = yamlToDump.dump(result);
+        TestBean1 result2 = (TestBean1) yaml.load(output);
+        assertNotNull(result2);
+        TestBean1 result3 = (TestBean1) new Yaml().load(output);
+        assertNotNull(result3);
+    }
+
+    public void testNoClassConstructor() {
+        try {
+            new Yaml(new Constructor((Class<? extends Object>) null));
+            fail("Class must be provided.");
+        } catch (NullPointerException e) {
+            assertEquals("Root class must be provided.", e.getMessage());
+        }
+    }
+
+    public void testNoClassConstructorString() throws ClassNotFoundException {
+        try {
+            new Yaml(new Constructor((String) null));
+            fail("Class must be provided.");
+        } catch (NullPointerException e) {
+            assertEquals("Root type must be provided.", e.getMessage());
+        }
+    }
+
+    public void testNoClassConstructorEmptyString() throws ClassNotFoundException {
+        try {
+            new Yaml(new Constructor(" "));
+            fail("Class must be provided.");
+        } catch (YAMLException e) {
+            assertEquals("Root type must be provided.", e.getMessage());
+        }
+    }
+
+    public void testCharacter() {
+        Yaml yaml = new Yaml(new Constructor(TestBean1.class));
+        String document = "charClass: id";
+        try {
+            yaml.load(document);
+            fail("Only one char must be allowed.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(),
+                    e.getMessage().contains("Invalid node Character: 'id'; length: 2"));
+        }
+        document = "charClass: #";
+        TestBean1 bean = (TestBean1) yaml.load(document);
+        assertNull("Null must be accepted.", bean.getCharClass());
+        document = "charClass: ''";
+        bean = (TestBean1) yaml.load(document);
+        assertNull("Null must be accepted.", bean.getCharClass());
+        document = "charClass:\n";
+        bean = (TestBean1) yaml.load(document);
+        assertNull("Null must be accepted.", bean.getCharClass());
+        document = "charClass: 1\n";
+        bean = (TestBean1) yaml.load(document);
+        assertEquals(Character.valueOf('1'), bean.getCharClass());
+    }
+
+    public void testNoEmptyConstructor() {
+        Yaml yaml = new Yaml(new Constructor(TestBean2.class));
+        String document = "text: qwerty";
+        try {
+            yaml.load(document);
+            fail("No empty constructor available");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("NoSuchMethodException"));
+        }
+        TestBean2 bean = new TestBean2();
+        assertEquals("", bean.getText());
+    }
+
+    private class TestBean2 {
+        private String text;
+
+        public TestBean2() {
+            setText("");
+        }
+
+        public String getText() {
+            return text;
+        }
+
+        public void setText(String text) {
+            this.text = text;
+        }
+    }
+
+    public void testPrivateMethod() {
+        // TODO: Are we sure no private ????
+        Yaml yaml = new Yaml(new Constructor(TestBean2.class));
+        String document = "text: qwerty";
+        try {
+            yaml.load(document);
+            fail("Private method cannot be called.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("NoSuchMethodException"));
+        }
+    }
+
+    public void testKeyNotScalar() {
+        Yaml yaml = new Yaml(new Constructor(TestBean1.class));
+        String document = "[1, 2]: qwerty";
+        try {
+            yaml.load(document);
+            fail("Keys must be scalars.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Keys must be scalars but found"));
+        }
+    }
+
+    public void testInvalidKey() {
+        Yaml yaml = new Yaml(new Constructor(TestBean1.class));
+        String document = "something: qwerty";
+        try {
+            yaml.load(document);
+            fail("Non-existing property must fail.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(),
+                    e.getMessage().contains("Unable to find property 'something'"));
+        }
+    }
+
+    public void testStaticField() {
+        Yaml yaml = new Yaml(new Constructor(TestBean1.class));
+        String document = "staticInteger: 123";
+        try {
+            yaml.load(document);
+            fail("Staic variables must not be used.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(),
+                    e.getMessage().contains("Unable to find property 'staticInteger'"));
+        }
+    }
+
+    public void testScalarContructor() {
+        Yaml yaml = new Yaml(new Constructor(Parent1.class));
+        String document = "id: 123\nchild: 25";
+        Parent1 parent = (Parent1) yaml.load(document);
+        assertEquals("123", parent.getId());
+        Child1 child = parent.getChild();
+        assertEquals(new Integer(25), child.getCode());
+    }
+
+    public void testScalarContructorException() {
+        Yaml yaml = new Yaml(new Constructor(ExceptionParent.class));
+        String document = "id: 123\nchild: 25";
+        try {
+            yaml.load(document);
+            fail("ExceptionParent should not be created.");
+        } catch (Exception e) {
+            assertTrue(
+                    e.getMessage(),
+                    e.getMessage().contains(
+                            "Can't construct a java object for scalar tag:yaml.org,2002:int"));
+        }
+    }
+
+    static public class ExceptionParent {
+        private String id;
+        private ExceptionChild child;
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public ExceptionChild getChild() {
+            return child;
+        }
+
+        public void setChild(ExceptionChild child) {
+            this.child = child;
+        }
+
+    }
+
+    public static class ExceptionChild {
+        private Integer code;
+
+        public ExceptionChild(Integer code) {
+            throw new RuntimeException("ExceptionChild cannot be created.");
+        }
+
+        public Integer getCode() {
+            return code;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalBeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalBeanConstructorTest.java
new file mode 100644
index 0000000..e5f0fb4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalBeanConstructorTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class BigDecimalBeanConstructorTest extends TestCase {
+
+    public void testRepresentor() {
+        BigDecimalJavaBean bean = new BigDecimalJavaBean();
+        bean.setAmount(1.5f);
+        bean.setNumber(new BigDecimal("3.1416"));
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(bean);
+        String className = this.getClass().getPackage().getName();
+        assertEquals("!!" + className + ".BigDecimalJavaBean {amount: 1.5, number: 3.1416}\n",
+                output);
+    }
+
+    public void testConstructor() {
+        String className = "!!" + this.getClass().getPackage().getName()
+                + ".BigDecimalJavaBean {amount: 1.5, number: 3.1416}";
+        Yaml yaml = new Yaml();
+        BigDecimalJavaBean bean = (BigDecimalJavaBean) yaml.load(className);
+        assertNotNull(bean);
+        assertTrue(1.5 - bean.getAmount() < 0.0000001);
+        assertTrue((new BigDecimal("3.1416")).add(bean.getNumber().negate()).doubleValue() < 0.0000001);
+    }
+
+    public void testConstructorAtomic() {
+        String className = "!!" + this.getClass().getPackage().getName()
+                + ".AtomicJavaBean {amount: 1.5, atomic: 0}";
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(className);
+            fail("AtomicLong is not supported.");
+        } catch (Exception e) {
+            assertEquals("argument type mismatch", e.getCause().getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalJavaBean.java b/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalJavaBean.java
new file mode 100644
index 0000000..516f4cd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/BigDecimalJavaBean.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.math.BigDecimal;
+
+public class BigDecimalJavaBean {
+    private BigDecimal number;
+    private float amount;
+
+    public BigDecimal getNumber() {
+        return number;
+    }
+
+    public void setNumber(BigDecimal number) {
+        this.number = number;
+    }
+
+    public float getAmount() {
+        return amount;
+    }
+
+    public void setAmount(float amount) {
+        this.amount = amount;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/Car.java b/src/test/java/org/yaml/snakeyaml/constructor/Car.java
new file mode 100644
index 0000000..d19a713
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/Car.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.List;
+
+public class Car {
+    private String plate;
+    private List<Wheel> wheels;
+
+    public String getPlate() {
+        return plate;
+    }
+
+    public void setPlate(String plate) {
+        this.plate = plate;
+    }
+
+    public List<Wheel> getWheels() {
+        return wheels;
+    }
+
+    public void setWheels(List<Wheel> wheels) {
+        this.wheels = wheels;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/Child1.java b/src/test/java/org/yaml/snakeyaml/constructor/Child1.java
new file mode 100644
index 0000000..592265c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/Child1.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+public class Child1 {
+    private Integer code;
+
+    public Child1(Integer code) {
+        this.code = code;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java
new file mode 100644
index 0000000..b14c5e7
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ClassTagsTest extends TestCase {
+
+    public void testDefaultRepresenter() {
+        Car car = new Car();
+        car.setPlate("12-XP-F4");
+        List<Wheel> wheels = new ArrayList<Wheel>();
+        for (int i = 1; i < 6; i++) {
+            Wheel wheel = new Wheel();
+            wheel.setId(i);
+            wheels.add(wheel);
+        }
+        car.setWheels(wheels);
+        assertEquals(Util.getLocalResource("constructor/car-with-tags.yaml"), new Yaml().dump(car));
+    }
+
+    public void testDumpClassTag() {
+        Car car = new Car();
+        car.setPlate("12-XP-F4");
+        List<Wheel> wheels = new ArrayList<Wheel>();
+        for (int i = 1; i < 6; i++) {
+            Wheel wheel = new Wheel();
+            wheel.setId(i);
+            wheels.add(wheel);
+        }
+        car.setWheels(wheels);
+        Representer representer = new Representer();
+        representer.addClassTag(Car.class, new Tag("!car"));
+        representer.addClassTag(Wheel.class, Tag.MAP);
+        Yaml yaml = new Yaml(representer);
+        String output = yaml.dump(car);
+        assertEquals(Util.getLocalResource("constructor/car-without-tags.yaml"), output);
+    }
+
+    public void testLoadUnknounClassTag() {
+        try {
+            Yaml yaml = new Yaml();
+            yaml.load(Util.getLocalResource("constructor/car-without-tags.yaml"));
+            fail("Must fail because of unknown tag: !car");
+        } catch (YAMLException e) {
+            assertTrue(e.getMessage().contains("Invalid tag: !car"));
+        }
+
+    }
+
+    public void testLoadClassTag() {
+        Constructor constructor = new Constructor();
+        constructor.addTypeDescription(new TypeDescription(Car.class, "!car"));
+        Yaml yaml = new Yaml(constructor);
+        String source = Util.getLocalResource("constructor/car-without-tags.yaml");
+        Car car = (Car) yaml.load(source);
+        assertEquals("12-XP-F4", car.getPlate());
+        List<Wheel> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+    }
+
+    public void testNullDescription() {
+        Constructor constructor = new Constructor();
+        try {
+            constructor.addTypeDescription(null);
+            fail("Description is required.");
+        } catch (Exception e) {
+            assertEquals("TypeDescription is required.", e.getMessage());
+        }
+    }
+
+    public void testLoadClassNoRoot() {
+        Constructor constructor = new Constructor(new TypeDescription(Car.class));
+        Yaml yaml = new Yaml(constructor);
+        Car car = (Car) yaml.load(Util.getLocalResource("constructor/car-no-root-class.yaml"));
+        assertEquals("12-XP-F4", car.getPlate());
+        List<Wheel> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ConstructorMappingTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorMappingTest.java
new file mode 100644
index 0000000..7d6b1f3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorMappingTest.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class ConstructorMappingTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testGetDefaultMap() {
+        String data = "{ one: 1, two: 2, three: 3 }";
+        Map<Object, Object> map = (Map<Object, Object>) construct(new CustomConstructor(), data);
+        assertNotNull(map);
+        assertTrue(map.getClass().toString(), map instanceof TreeMap);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testGetArrayList() {
+        String data = "{ one: 1, two: 2, three: 3 }";
+        Map<Object, Object> map = (Map<Object, Object>) construct(data);
+        assertNotNull(map);
+        assertTrue(map.getClass().toString(), map instanceof LinkedHashMap);
+    }
+
+    private Object construct(String data) {
+        return construct(new Constructor(), data);
+    }
+
+    private Object construct(Constructor constructor, String data) {
+        StreamReader reader = new StreamReader(data);
+        Parser parser = new ParserImpl(reader);
+        Resolver resolver = new Resolver();
+        Composer composer = new Composer(parser, resolver);
+        constructor.setComposer(composer);
+        return constructor.getSingleData(Object.class);
+    }
+
+    class CustomConstructor extends Constructor {
+        @Override
+        protected Map<Object, Object> createDefaultMap() {
+            return new TreeMap<Object, Object>();
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ConstructorSequenceTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorSequenceTest.java
new file mode 100644
index 0000000..2641201
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorSequenceTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class ConstructorSequenceTest extends TestCase {
+
+    public void testGetList() {
+        String data = "[ 1, 2, 3 ]";
+        List<Object> list = construct(new CustomConstructor(), data);
+        assertNotNull(list);
+        assertTrue(list.getClass().toString(), list instanceof ArrayList<?>);
+    }
+
+    public void testGetArrayList() {
+        String data = "[ 1, 2, 3 ]";
+        List<Object> list = construct(data);
+        assertNotNull(list);
+        assertTrue(list.getClass().toString(), list instanceof ArrayList<?>);
+    }
+
+    public void testDumpList() {
+        List<Integer> l = new ArrayList<Integer>(2);
+        l.add(1);
+        l.add(2);
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(l);
+        assertEquals("[1, 2]\n", result);
+    }
+
+    public void testDumpListSameIntegers() {
+        List<Integer> l = new ArrayList<Integer>(2);
+        l.add(1);
+        l.add(1);
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(l);
+        assertEquals("[1, 1]\n", result);
+    }
+
+    private List<Object> construct(String data) {
+        return construct(new Constructor(), data);
+    }
+
+    @SuppressWarnings("unchecked")
+    private List<Object> construct(Constructor constructor, String data) {
+        StreamReader reader = new StreamReader(data);
+        Parser parser = new ParserImpl(reader);
+        Resolver resolver = new Resolver();
+        Composer composer = new Composer(parser, resolver);
+        constructor.setComposer(composer);
+        List<Object> result = (List<Object>) constructor.getSingleData(Object.class);
+        return result;
+    }
+
+    class CustomConstructor extends Constructor {
+        @Override
+        protected List<Object> createDefaultList(int initSize) {
+            return new ArrayList<Object>(initSize);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorTest.java
new file mode 100644
index 0000000..47f9805
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ConstructorTest.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ConstructorTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testMapOrder() {
+        String data = "one: zzz\ntwo: ccc\nthree: bbb\nfour: aaa";
+        Object map = construct(data);
+        assertNotNull(map);
+        assertTrue(map.getClass().toString(), map instanceof LinkedHashMap);
+        Map<String, String> m = (Map<String, String>) map;
+        assertEquals(4, m.keySet().size());
+        Iterator<String> iter = m.keySet().iterator();
+        assertEquals("one", iter.next());
+        assertEquals("two", iter.next());
+        assertEquals("three", iter.next());
+        assertEquals("four", iter.next());
+    }
+
+    /**
+     * create JavaBean
+     */
+    public void testGetBeanAssumeClass() {
+        String data = "--- !!org.yaml.snakeyaml.constructor.Person\nfirstName: Andrey\nage: 99";
+        Object obj = construct(data);
+        assertNotNull(obj);
+        assertTrue("Unexpected: " + obj.getClass().toString(), obj instanceof Person);
+        Person person = (Person) obj;
+        assertEquals("Andrey", person.getFirstName());
+        assertNull(person.getLastName());
+        assertEquals(99, person.getAge().intValue());
+    }
+
+    /**
+     * create instance using constructor arguments
+     */
+    public void testGetConstructorBean() {
+        String data = "--- !!org.yaml.snakeyaml.constructor.Person [ Andrey, Somov, 99 ]";
+        Object obj = construct(data);
+        assertNotNull(obj);
+        assertTrue(obj.getClass().toString(), obj instanceof Person);
+        Person person = (Person) obj;
+        assertEquals("Andrey", person.getFirstName());
+        assertEquals("Somov", person.getLastName());
+        assertEquals(99, person.getAge().intValue());
+    }
+
+    /**
+     * create instance using scalar argument
+     */
+    public void testGetConstructorFromScalar() {
+        String data = "--- !!org.yaml.snakeyaml.constructor.Person 'Somov'";
+        Object obj = construct(data);
+        assertNotNull(obj);
+        assertTrue(obj.getClass().toString(), obj instanceof Person);
+        Person person = (Person) obj;
+        assertNull("Andrey", person.getFirstName());
+        assertEquals("Somov", person.getLastName());
+        assertNull(person.getAge());
+    }
+
+    public void testJavaBeanLoad() {
+        java.util.Calendar cal = java.util.Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        cal.clear();
+        cal.set(1982, 5 - 1, 3); // Java's months are zero-based...
+
+        TestBean expected = new TestBean("Ola Bini", 24, cal.getTime());
+        assertEquals(
+                expected,
+                construct("--- !!org.yaml.snakeyaml.constructor.TestBean\nname: Ola Bini\nage: 24\nborn: 1982-05-03\n"));
+    }
+
+    public void testWrongName() {
+        try {
+            construct("--- !!org.yaml.snakeyaml.constructor.TestBean\nwrongName: No one\nage: 24\nborn: 1982-05-03\n");
+            fail("IntrospectionException expected.");
+        } catch (Exception e) {
+            // TODO improve the error message - the pointer should be at the
+            // property name, not value
+            assertEquals(
+                    "Cannot create property=wrongName for JavaBean=#<org.jvyaml.TestBean name=\"null\" age=0 born=\"null\">\n"
+                            + " in 'string', line 1, column 5:\n"
+                            + "    --- !!org.yaml.snakeyaml.constructor ... \n"
+                            + "        ^\n"
+                            + "Unable to find property 'wrongName' on class: org.yaml.snakeyaml.constructor.TestBean\n"
+                            + " in 'string', line 2, column 12:\n"
+                            + "    wrongName: No one\n"
+                            + "               ^\n", e.getMessage());
+        }
+    }
+
+    private Object construct(String data) {
+        Yaml yaml = new Yaml();
+        return yaml.load(data);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructorTest.java
new file mode 100644
index 0000000..c236e23
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/CustomClassLoaderConstructorTest.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class CustomClassLoaderConstructorTest extends TestCase {
+
+    public void testGetClassForNameNull() {
+        try {
+            new CustomClassLoaderConstructor(null);
+            fail();
+        } catch (Exception e) {
+            assertEquals("Loader must be provided.", e.getMessage());
+        }
+    }
+
+    public void testGetClassForName() {
+        CustomClassLoaderConstructor constr = new CustomClassLoaderConstructor(
+                CustomClassLoaderConstructorTest.class.getClassLoader());
+        Yaml yaml = new Yaml(constr);
+        String s = (String) yaml.load("abc");
+        assertEquals("abc", s);
+    }
+
+    public void testGetClassForNameWithRoot() throws ClassNotFoundException {
+        Class<?> clazz = Class.forName(
+                "org.yaml.snakeyaml.constructor.CustomClassLoaderConstructorTest$LoaderBean", true,
+                CustomClassLoaderConstructorTest.class.getClassLoader());
+        CustomClassLoaderConstructor constr = new CustomClassLoaderConstructor(clazz,
+                CustomClassLoaderConstructorTest.class.getClassLoader());
+        Yaml yaml = new Yaml(constr);
+        LoaderBean bean = (LoaderBean) yaml.load("{name: Andrey, number: 555}");
+        assertEquals("Andrey", bean.getName());
+        assertEquals(555, bean.getNumber());
+    }
+
+    public void testGetClassForNameBean() {
+        CustomClassLoaderConstructor constr = new CustomClassLoaderConstructor(
+                CustomClassLoaderConstructorTest.class.getClassLoader());
+        Yaml yaml = new Yaml(constr);
+        LoaderBean bean = (LoaderBean) yaml
+                .load("!!org.yaml.snakeyaml.constructor.CustomClassLoaderConstructorTest$LoaderBean {name: Andrey, number: 555}");
+        assertEquals("Andrey", bean.getName());
+        assertEquals(555, bean.getNumber());
+    }
+
+    public static class LoaderBean {
+        private String name;
+        private int number;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/FilterClassesConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/FilterClassesConstructorTest.java
new file mode 100644
index 0000000..734a58e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/FilterClassesConstructorTest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class FilterClassesConstructorTest extends TestCase {
+
+    public void testGetClassForName() {
+        Yaml yaml = new Yaml(new FilterConstructor(true));
+        String input = "!!org.yaml.snakeyaml.constructor.FilterClassesConstructorTest$FilteredBean {name: Andrey, number: 543}";
+        try {
+            yaml.load(input);
+            fail("Filter is expected.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage().contains("Filter is applied."));
+        }
+        yaml = new Yaml(new FilterConstructor(false));
+        FilteredBean s = (FilteredBean) yaml.load(input);
+        assertEquals("Andrey", s.getName());
+    }
+
+    class FilterConstructor extends Constructor {
+        private boolean filter;
+
+        public FilterConstructor(boolean f) {
+            filter = f;
+        }
+
+        @Override
+        protected Class<?> getClassForName(String name) throws ClassNotFoundException {
+            if (filter && name.startsWith("org.yaml")) {
+                throw new RuntimeException("Filter is applied.");
+            }
+            return super.getClassForName(name);
+        }
+    }
+
+    public static class FilteredBean {
+        private String name;
+        private int number;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/ImplicitTagsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/ImplicitTagsTest.java
new file mode 100644
index 0000000..0c1482d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/ImplicitTagsTest.java
@@ -0,0 +1,159 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ImplicitTagsTest extends TestCase {
+
+    public void testDefaultRepresenter() {
+        CarWithWheel car1 = new CarWithWheel();
+        car1.setPlate("12-XP-F4");
+        Wheel wheel = new Wheel();
+        wheel.setId(2);
+        car1.setWheel(wheel);
+        Map<String, Integer> map = new HashMap<String, Integer>();
+        map.put("id", 3);
+        car1.setMap(map);
+        car1.setPart(new Wheel(4));
+        car1.setYear("2008");
+        String carYaml1 = new Yaml().dump(car1);
+        assertEquals(Util.getLocalResource("constructor/carwheel-without-tags.yaml"), carYaml1);
+        CarWithWheel car2 = (CarWithWheel) new Yaml().load(carYaml1);
+        String carYaml2 = new Yaml().dump(car2);
+        assertEquals(carYaml1, carYaml2);
+    }
+
+    public void testNoRootTag() {
+        CarWithWheel car1 = new CarWithWheel();
+        car1.setPlate("12-XP-F4");
+        Wheel wheel = new Wheel();
+        wheel.setId(2);
+        car1.setWheel(wheel);
+        Map<String, Integer> map = new HashMap<String, Integer>();
+        map.put("id", 3);
+        car1.setMap(map);
+        car1.setYear("2008");
+        String carYaml1 = new Yaml().dumpAs(car1, Tag.MAP, FlowStyle.AUTO);
+        assertEquals(Util.getLocalResource("constructor/car-without-root-tag.yaml"), carYaml1);
+        //
+        Constructor contructor = new Constructor(CarWithWheel.class);
+        CarWithWheel car2 = (CarWithWheel) new Yaml(contructor).load(carYaml1);
+        String carYaml2 = new Yaml().dumpAs(car2, Tag.MAP, FlowStyle.AUTO);
+        assertEquals(carYaml1, carYaml2);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testRootMap() {
+        Map<Object, Object> car1 = new LinkedHashMap<Object, Object>();
+        Wheel wheel = new Wheel();
+        wheel.setId(2);
+        Map<String, Integer> map = new HashMap<String, Integer>();
+        map.put("id", 3);
+
+        car1.put("wheel", wheel);
+        car1.put("map", map);
+        car1.put("plate", "12-XP-F4");
+
+        String carYaml1 = new Yaml().dump(car1);
+        assertEquals(Util.getLocalResource("constructor/carwheel-root-map.yaml"), carYaml1);
+        Map<Object, Object> car2 = (Map<Object, Object>) new Yaml().load(carYaml1);
+        assertEquals(car1, car2);
+        assertEquals(carYaml1, new Yaml().dump(car2));
+    }
+
+    public void testLoadClassTag() {
+        Constructor constructor = new Constructor();
+        constructor.addTypeDescription(new TypeDescription(Car.class, "!car"));
+        Yaml yaml = new Yaml(constructor);
+        Car car = (Car) yaml.load(Util.getLocalResource("constructor/car-without-tags.yaml"));
+        assertEquals("12-XP-F4", car.getPlate());
+        List<Wheel> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+        Wheel w1 = wheels.get(0);
+        assertEquals(1, w1.getId());
+        //
+        String carYaml1 = new Yaml().dump(car);
+        assertTrue(carYaml1.startsWith("!!org.yaml.snakeyaml.constructor.Car"));
+        //
+        Representer representer = new Representer();
+        representer.addClassTag(Car.class, new Tag("!car"));
+        yaml = new Yaml(representer);
+        String carYaml2 = yaml.dump(car);
+        assertEquals(Util.getLocalResource("constructor/car-without-tags.yaml"), carYaml2);
+    }
+
+    public static class CarWithWheel {
+        private String plate;
+        private String year;
+        private Wheel wheel;
+        private Object part;
+        private Map<String, Integer> map;
+
+        public String getPlate() {
+            return plate;
+        }
+
+        public void setPlate(String plate) {
+            this.plate = plate;
+        }
+
+        public Wheel getWheel() {
+            return wheel;
+        }
+
+        public void setWheel(Wheel wheel) {
+            this.wheel = wheel;
+        }
+
+        public Map<String, Integer> getMap() {
+            return map;
+        }
+
+        public void setMap(Map<String, Integer> map) {
+            this.map = map;
+        }
+
+        public Object getPart() {
+            return part;
+        }
+
+        public void setPart(Object part) {
+            this.part = part;
+        }
+
+        public String getYear() {
+            return year;
+        }
+
+        public void setYear(String year) {
+            this.year = year;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/IncompleteBeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/IncompleteBeanConstructorTest.java
new file mode 100644
index 0000000..d3c8ba0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/IncompleteBeanConstructorTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class IncompleteBeanConstructorTest extends TestCase {
+
+    public void testRepresentor() {
+        IncompleteJavaBean bean = new IncompleteJavaBean();
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(bean);
+        String className = this.getClass().getPackage().getName();
+        assertEquals("!!" + className + ".IncompleteJavaBean {name: No name}\n", output);
+    }
+
+    public void testConstructor() {
+        String className = "!!" + this.getClass().getPackage().getName()
+                + ".IncompleteJavaBean {number: 2}";
+        Yaml yaml = new Yaml();
+        IncompleteJavaBean bean = (IncompleteJavaBean) yaml.load(className);
+        assertNotNull(bean);
+        assertEquals("No name", bean.getName());
+        assertEquals(2, bean.obtainNumber());
+    }
+
+    public void testConstructor2() {
+        String className = "!!" + this.getClass().getPackage().getName()
+                + ".IncompleteJavaBean {number: 2, name: Bill}";
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(className);
+            fail("'name' property does not have setter.");
+        } catch (YAMLException e) {
+            assertEquals(
+                    "Unable to find property 'name' on class: org.yaml.snakeyaml.constructor.IncompleteJavaBean",
+                    e.getCause().getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/IncompleteJavaBean.java b/src/test/java/org/yaml/snakeyaml/constructor/IncompleteJavaBean.java
new file mode 100644
index 0000000..d849680
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/IncompleteJavaBean.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+public class IncompleteJavaBean {
+    private int number;
+    private String name = "No name";
+    private float amount;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setNumber(int number) {
+        this.number = number;
+        amount += number;
+    }
+
+    public int obtainNumber() {
+        return number;
+    }
+
+    @Override
+    public String toString() {
+        return "<IncompleteJavaBean name=" + name + ">";
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/MockDateBeanConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/MockDateBeanConstructorTest.java
new file mode 100644
index 0000000..9b8d794
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/MockDateBeanConstructorTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class MockDateBeanConstructorTest extends TestCase {
+
+    public void testConstructor() {
+        String className = "!!org.yaml.snakeyaml.constructor.MockDateBeanConstructorTest$DateBean {number: 24, date: 2009-07-24}";
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(className);
+            fail("MockDate cannot be constructed.");
+        } catch (Exception e) {
+            assertEquals(
+                    "Cannot construct: 'class org.yaml.snakeyaml.constructor.MockDateBeanConstructorTest$MockDate'",
+                    e.getCause().getMessage());
+        }
+    }
+
+    public static class DateBean {
+        private int number;
+        private MockDate date;
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+
+        public MockDate getDate() {
+            return date;
+        }
+
+        public void setDate(MockDate date) {
+            this.date = date;
+        }
+
+        @Override
+        public String toString() {
+            return "<DateBean n=" + number + ">";
+        }
+    }
+
+    public static class MockDate extends Date {
+        private static final long serialVersionUID = 621384692653658062L;
+
+        public MockDate(long date) {
+            throw new RuntimeException("Test error.");
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/MyCar.java b/src/test/java/org/yaml/snakeyaml/constructor/MyCar.java
new file mode 100644
index 0000000..4753f3e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/MyCar.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Date;
+import java.util.Map;
+
+public class MyCar {
+    private String plate;
+    private Map<MyWheel, Date> wheels;
+    private Map<String, Integer> windows;
+
+    public String getPlate() {
+        return plate;
+    }
+
+    public void setPlate(String plate) {
+        this.plate = plate;
+    }
+
+    public Map<MyWheel, Date> getWheels() {
+        return wheels;
+    }
+
+    public void setWheels(Map<MyWheel, Date> wheels) {
+        this.wheels = wheels;
+    }
+
+    public Map<String, Integer> getWindows() {
+        return windows;
+    }
+
+    public void setWindows(Map<String, Integer> windows) {
+        this.windows = windows;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/MyWheel.java b/src/test/java/org/yaml/snakeyaml/constructor/MyWheel.java
new file mode 100644
index 0000000..67fbeab
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/MyWheel.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+public class MyWheel implements Comparable<MyWheel> {
+    private int id;
+    private String brand;
+
+    public MyWheel() {
+        brand = "Pirelli";
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return "Wheel id=" + id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MyWheel) {
+            MyWheel wheel = (MyWheel) obj;
+            return id == wheel.getId();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return new Integer(id).hashCode();
+    }
+
+    public String getBrand() {
+        return brand;
+    }
+
+    public void setBrand(String brand) {
+        this.brand = brand;
+    }
+
+    public int compareTo(MyWheel arg0) {
+        return new Integer(id).compareTo(new Integer(arg0.id));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/Parent1.java b/src/test/java/org/yaml/snakeyaml/constructor/Parent1.java
new file mode 100644
index 0000000..3703dbc
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/Parent1.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+public class Parent1 {
+    private String id;
+    private Child1 child;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Child1 getChild() {
+        return child;
+    }
+
+    public void setChild(Child1 child) {
+        this.child = child;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/Person.java b/src/test/java/org/yaml/snakeyaml/constructor/Person.java
new file mode 100644
index 0000000..a681ac8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/Person.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+/**
+ * Test JavaBean
+ */
+public class Person {
+    private String firstName;
+    private String lastName;
+    private Integer age;
+
+    public Person(String firstName, String lastName, Integer age) {
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.age = age;
+    }
+
+    public Person() {
+    }
+
+    public Person(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/PrefixConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/PrefixConstructorTest.java
new file mode 100644
index 0000000..6a6a5e0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/PrefixConstructorTest.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Example to process a family of tags with the same prefix with one constructor
+ * (PrefixConstruct)
+ */
+public class PrefixConstructorTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void test1() {
+        Yaml yaml = new Yaml(new CustomConstructor());
+        String input = "- !org.yaml.Foo 123\n- !org.yaml.Bar 456\n- !org.yaml.Exact 789\n- !Immutable [aaa, bbb]";
+        List<Extra> list = (List<Extra>) yaml.load(input);
+        assertEquals(4, list.size());
+        Extra foo = list.get(0);
+        assertEquals("Foo", foo.getName());
+        assertEquals("123", foo.getValue());
+        //
+        Extra bar = list.get(1);
+        assertEquals("Bar", bar.getName());
+        assertEquals("456", bar.getValue());
+        //
+        Extra item = list.get(2);
+        assertEquals("Item", item.getName());
+        assertEquals("789", item.getValue());
+        //
+        Extra immut = list.get(3);
+        assertEquals("aaa", immut.getName());
+        assertEquals("bbb", immut.getValue());
+    }
+
+    private class CustomConstructor extends SafeConstructor {
+        public CustomConstructor() {
+            // define tags which begin with !org.yaml.
+            String prefix = "!org.yaml.";
+            this.yamlMultiConstructors.put(prefix, new PrefixConstruct(prefix,
+                    CustomConstructor.this));
+            this.yamlConstructors.put(null, new ConstructUnknown(CustomConstructor.this));
+            this.yamlConstructors.put(new Tag("!org.yaml.Exact"), new ExactConstruct(
+                    CustomConstructor.this));
+        }
+    }
+
+    /**
+     * Process tags which start with '!org.yaml.'
+     */
+    private class PrefixConstruct extends AbstractConstruct {
+        private String prefix;
+        private BaseConstructor con;
+
+        public PrefixConstruct(String prefix, BaseConstructor con) {
+            this.prefix = prefix;
+            this.con = con;
+        }
+
+        public Object construct(Node node) {
+            String suffix = node.getTag().getValue().substring(prefix.length());
+            return new Extra(suffix, con.constructScalar((ScalarNode) node).toString());
+        }
+    }
+
+    /**
+     * This has more priority then PrefixConstruct
+     */
+    private class ExactConstruct extends AbstractConstruct {
+        private BaseConstructor con;
+
+        public ExactConstruct(BaseConstructor con) {
+            this.con = con;
+        }
+
+        public Object construct(Node node) {
+            return new Extra("Item", con.constructScalar((ScalarNode) node).toString());
+        }
+    }
+
+    /**
+     * Process unrecognised tags
+     */
+    private class ConstructUnknown extends AbstractConstruct {
+        private BaseConstructor con;
+
+        public ConstructUnknown(BaseConstructor con) {
+            this.con = con;
+        }
+
+        @SuppressWarnings("unchecked")
+        public Object construct(Node node) {
+            List<String> list = (List<String>) con.constructSequence((SequenceNode) node);
+            return new Extra(list.get(0), list.get(1));
+        }
+    }
+
+    private class Extra {
+        private String name;
+        private String value;
+
+        public Extra(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/SafeConstructorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/SafeConstructorTest.java
new file mode 100644
index 0000000..32fb36f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/SafeConstructorTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class SafeConstructorTest extends TestCase {
+
+    public void testConstructFloat() {
+        Yaml yaml = new Yaml();
+        assertEquals(3.1416, yaml.load("+3.1416"));
+        assertEquals(Double.POSITIVE_INFINITY, yaml.load("+.inf"));
+        assertEquals(Double.POSITIVE_INFINITY, yaml.load(".inf"));
+        assertEquals(Double.NEGATIVE_INFINITY, yaml.load("-.inf"));
+    }
+
+    public void testSafeConstruct() {
+        Yaml yaml = new Yaml(new SafeConstructor());
+        assertEquals(3.1416, yaml.load("+3.1416"));
+    }
+
+    public void testSafeConstructJavaBean() {
+        Yaml yaml = new Yaml(new SafeConstructor());
+        String data = "--- !!org.yaml.snakeyaml.constructor.Person\nfirstName: Andrey\nage: 99";
+        try {
+            yaml.load(data);
+            fail("JavaBeans cannot be created by SafeConstructor.");
+        } catch (ConstructorException e) {
+            assertTrue(e
+                    .getMessage()
+                    .contains(
+                            "could not determine a constructor for the tag tag:yaml.org,2002:org.yaml.snakeyaml.constructor.Person"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/TestBean.java b/src/test/java/org/yaml/snakeyaml/constructor/TestBean.java
new file mode 100644
index 0000000..31d9194
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/TestBean.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Date;
+
+/**
+ * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
+ */
+public class TestBean {
+    private String name;
+    private int age;
+    private Date born;
+
+    public TestBean() {
+    }
+
+    public TestBean(final String name, final int age, final Date born) {
+        this.name = name;
+        this.age = age;
+        this.born = born;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public Date getBorn() {
+        return born;
+    }
+
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public void setAge(final int age) {
+        this.age = age;
+    }
+
+    public void setBorn(final Date born) {
+        this.born = born;
+    }
+
+    public boolean equals(final Object other) {
+        boolean ret = this == other;
+        if (!ret && other instanceof TestBean) {
+            TestBean o = (TestBean) other;
+            ret = this.name == null ? o.name == null : this.name.equals(o.name)
+                    && this.age == o.age && this.born == null ? o.born == null : this.born
+                    .equals(o.born);
+        }
+        return ret;
+    }
+
+    public int hashCode() {
+        int val = 3;
+        val += 3 * (name == null ? 0 : name.hashCode());
+        val += 3 * age;
+        val += 3 * (born == null ? 0 : born.hashCode());
+        return val;
+    }
+
+    public String toString() {
+        return "#<org.jvyaml.TestBean name=\"" + name + "\" age=" + age + " born=\"" + born + "\">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/TestBean1.java b/src/test/java/org/yaml/snakeyaml/constructor/TestBean1.java
new file mode 100644
index 0000000..be9cf97
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/TestBean1.java
@@ -0,0 +1,204 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.math.BigInteger;
+import java.util.Date;
+
+public class TestBean1 {
+    private String text;
+    private String id;
+    private Byte byteClass;
+    private byte bytePrimitive;
+    private Short shortClass;
+    private short shortPrimitive;
+    private Integer integer;
+    private int intPrimitive;
+    private Long longClass;
+    private long longPrimitive;
+    private Boolean booleanClass;
+    private boolean booleanPrimitive;
+    private Character charClass;
+    private char charPrimitive;
+    private BigInteger bigInteger;
+    private Float floatClass;
+    private float floatPrimitive;
+    private Double doubleClass;
+    private double doublePrimitive;
+    private Date date;
+    public String publicField;
+    static public Integer staticInteger;
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public Integer getInteger() {
+        return integer;
+    }
+
+    public void setInteger(Integer integer) {
+        this.integer = integer;
+    }
+
+    public int getIntPrimitive() {
+        return intPrimitive;
+    }
+
+    public void setIntPrimitive(int intPrimitive) {
+        this.intPrimitive = intPrimitive;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Byte getByteClass() {
+        return byteClass;
+    }
+
+    public void setByteClass(Byte byteClass) {
+        this.byteClass = byteClass;
+    }
+
+    public byte getBytePrimitive() {
+        return bytePrimitive;
+    }
+
+    public void setBytePrimitive(byte bytePrimitive) {
+        this.bytePrimitive = bytePrimitive;
+    }
+
+    public Short getShortClass() {
+        return shortClass;
+    }
+
+    public void setShortClass(Short shortClass) {
+        this.shortClass = shortClass;
+    }
+
+    public short getShortPrimitive() {
+        return shortPrimitive;
+    }
+
+    public void setShortPrimitive(short shortPrimitive) {
+        this.shortPrimitive = shortPrimitive;
+    }
+
+    public Long getLongClass() {
+        return longClass;
+    }
+
+    public void setLongClass(Long longClass) {
+        this.longClass = longClass;
+    }
+
+    public long getLongPrimitive() {
+        return longPrimitive;
+    }
+
+    public void setLongPrimitive(long longPrimitive) {
+        this.longPrimitive = longPrimitive;
+    }
+
+    public Boolean getBooleanClass() {
+        return booleanClass;
+    }
+
+    public void setBooleanClass(Boolean booleanClass) {
+        this.booleanClass = booleanClass;
+    }
+
+    public boolean isBooleanPrimitive() {
+        return booleanPrimitive;
+    }
+
+    public void setBooleanPrimitive(boolean booleanPrimitive) {
+        this.booleanPrimitive = booleanPrimitive;
+    }
+
+    public Character getCharClass() {
+        return charClass;
+    }
+
+    public void setCharClass(Character charClass) {
+        this.charClass = charClass;
+    }
+
+    public char getCharPrimitive() {
+        return charPrimitive;
+    }
+
+    public void setCharPrimitive(char charPrimitive) {
+        this.charPrimitive = charPrimitive;
+    }
+
+    public BigInteger getBigInteger() {
+        return bigInteger;
+    }
+
+    public void setBigInteger(BigInteger bigInteger) {
+        this.bigInteger = bigInteger;
+    }
+
+    public Float getFloatClass() {
+        return floatClass;
+    }
+
+    public void setFloatClass(Float floatClass) {
+        this.floatClass = floatClass;
+    }
+
+    public float getFloatPrimitive() {
+        return floatPrimitive;
+    }
+
+    public void setFloatPrimitive(float floatPrimitive) {
+        this.floatPrimitive = floatPrimitive;
+    }
+
+    public Double getDoubleClass() {
+        return doubleClass;
+    }
+
+    public void setDoubleClass(Double doubleClass) {
+        this.doubleClass = doubleClass;
+    }
+
+    public double getDoublePrimitive() {
+        return doublePrimitive;
+    }
+
+    public void setDoublePrimitive(double doublePrimitive) {
+        this.doublePrimitive = doublePrimitive;
+    }
+
+    public Date getDate() {
+        return date;
+    }
+
+    public void setDate(Date date) {
+        this.date = date;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java b/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java
new file mode 100644
index 0000000..8afe3ce
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java
@@ -0,0 +1,103 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class TypeSafeCollectionsTest extends TestCase {
+
+    public void testTypeSafeList() {
+        Constructor constructor = new Constructor(Car.class);
+        TypeDescription carDescription = new TypeDescription(Car.class);
+        carDescription.putListPropertyType("wheels", Wheel.class);
+        constructor.addTypeDescription(carDescription);
+        Yaml yaml = new Yaml(constructor);
+        Car car = (Car) yaml.load(Util.getLocalResource("constructor/car-no-root-class.yaml"));
+        assertEquals("12-XP-F4", car.getPlate());
+        List<Wheel> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+        for (Wheel wheel : wheels) {
+            assertTrue(wheel.getId() > 0);
+        }
+    }
+
+    public void testTypeSafeMap() {
+        Constructor constructor = new Constructor(MyCar.class);
+        TypeDescription carDescription = new TypeDescription(MyCar.class);
+        carDescription.putMapPropertyType("wheels", MyWheel.class, Object.class);
+        constructor.addTypeDescription(carDescription);
+        Yaml yaml = new Yaml(constructor);
+        MyCar car = (MyCar) yaml.load(Util
+                .getLocalResource("constructor/car-no-root-class-map.yaml"));
+        assertEquals("00-FF-Q2", car.getPlate());
+        Map<MyWheel, Date> wheels = car.getWheels();
+        assertNotNull(wheels);
+        assertEquals(5, wheels.size());
+        for (MyWheel wheel : wheels.keySet()) {
+            assertTrue(wheel.getId() > 0);
+            Date date = wheels.get(wheel);
+            long time = date.getTime();
+            assertTrue("It must be midnight.", time % 10000 == 0);
+        }
+    }
+
+    public void testWithGlobalTag() {
+        Map<MyWheel, Date> wheels = new TreeMap<MyWheel, Date>();
+        long time = 1248212168084L;
+        for (int i = 1; i < 6; i++) {
+            MyWheel mw = new MyWheel();
+            mw.setId(i);
+            mw.setBrand(mw.getBrand() + String.valueOf(i));
+            wheels.put(mw, new Date(time + i));
+        }
+        MyCar c = new MyCar();
+        c.setPlate("00-FF-Q2");
+        c.setWheels(wheels);
+        Representer representer = new Representer();
+        representer.addClassTag(MyWheel.class, Tag.MAP);
+        Yaml yaml = new Yaml(representer);
+        String output = yaml.dump(c);
+        assertEquals(Util.getLocalResource("javabeans/mycar-with-global-tag1.yaml"), output);
+        // load
+        Yaml beanLoader = new Yaml();
+        MyCar car = beanLoader.loadAs(output, MyCar.class);
+        assertNotNull(car);
+        assertEquals("00-FF-Q2", car.getPlate());
+        assertEquals(5, car.getWheels().size());
+        for (Date d : car.getWheels().values()) {
+            // give a day for any timezone
+            assertTrue(d.before(new Date(time + 1000 * 60 * 60 * 24)));
+            assertTrue(d.after(new Date(time)));
+        }
+        Object wheel = car.getWheels().keySet().iterator().next();
+        assertTrue(wheel instanceof MyWheel);
+        MyWheel w = (MyWheel) wheel;
+        assertEquals(1, w.getId());
+        assertEquals("Pirelli1", w.getBrand());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/VectorTest.java b/src/test/java/org/yaml/snakeyaml/constructor/VectorTest.java
new file mode 100644
index 0000000..0edb6b9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/VectorTest.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+import java.util.Vector;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class VectorTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testVector() throws ClassNotFoundException {
+        // Data to serialise
+        Vector<String> srcVector = new Vector<String>();
+        srcVector.add("this");
+        srcVector.add("is");
+        srcVector.add("a");
+        srcVector.add("test");
+        // System.out.println("Source Vector: " + srcVector);
+        Yaml yaml = new Yaml();
+        String instance = yaml.dump(srcVector);
+        //System.out.println("YAML String: " + instance);
+        yaml = new Yaml(new Constructor("java.util.Vector"));
+        // If I try to get a Vector I receive a class cast exception.
+        Vector<String> vector = (Vector<String>) yaml.load(instance);
+        // System.out.println("Vector: " + vector);
+        assertEquals(4, vector.size());
+        assertEquals("this", vector.firstElement());
+        assertEquals("test", vector.lastElement());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/constructor/Wheel.java b/src/test/java/org/yaml/snakeyaml/constructor/Wheel.java
new file mode 100644
index 0000000..91c740e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/constructor/Wheel.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.constructor;
+
+public class Wheel {
+    private int id;
+
+    public Wheel(int id) {
+        this.id = id;
+    }
+
+    public Wheel() {
+        this(0);
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return "Wheel id=" + id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Wheel) {
+            Wheel wheel = (Wheel) obj;
+            return id == wheel.getId();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return new Integer(id).hashCode();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java
new file mode 100644
index 0000000..77c5d2e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+
+public class EmitterMultiLineTest extends TestCase {
+
+    public void testWriteMultiLineLiteral() {
+        String plain = "mama\nmila\nramu";
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(plain);
+        // System.out.println(output);
+        assertEquals("|-\n  mama\n  mila\n  ramu\n", output);
+        String parsed = (String) yaml.load(output);
+        // System.out.println(parsed);
+        assertEquals(plain, parsed);
+    }
+
+    public void testWriteMultiLineList() {
+        String one = "first\nsecond\nthird";
+        String two = "one\ntwo\nthree\n";
+        byte[] binary = { 8, 14, 15, 10, 126, 32, 65, 65, 65 };
+        List<Object> list = new ArrayList<Object>(2);
+        list.add(one);
+        list.add(two);
+        list.add(binary);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(list);
+        // System.out.println(output);
+        String etalon = "- |-\n  first\n  second\n  third\n- |\n  one\n  two\n  three\n- !!binary |-\n  CA4PCn4gQUFB\n";
+        assertEquals(etalon, output);
+        @SuppressWarnings("unchecked")
+        List<Object> parsed = (List<Object>) yaml.load(etalon);
+        assertEquals(3, parsed.size());
+        assertEquals(one, parsed.get(0));
+        assertEquals(two, parsed.get(1));
+        assertEquals(new String(binary), new String((byte[]) parsed.get(2)));
+    }
+
+    public void testWriteMultiLineLiteralWithClipChomping() {
+        String source = "a: 1\nb: |\n  mama\n  mila\n  ramu\n";
+        // System.out.println("Source:\n" + source);
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parsed = (Map<String, Object>) yaml.load(source);
+        String value = (String) parsed.get("b");
+        // System.out.println(value);
+        assertEquals("mama\nmila\nramu\n", value);
+        String dumped = yaml.dump(parsed);
+        // System.out.println(dumped);
+        assertEquals("a: 1\nb: |\n  mama\n  mila\n  ramu\n", dumped);
+    }
+
+    public void testWriteMultiLineQuotedInFlowContext() {
+        String source = "{a: 1, b: 'mama\n\n    mila\n\n    ramu'}\n";
+        // System.out.println("Source:\n" + source);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.FLOW);
+        Yaml yaml = new Yaml(options);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parsed = (Map<String, Object>) yaml.load(source);
+        String value = (String) parsed.get("b");
+        // System.out.println(value);
+        assertEquals("mama\nmila\nramu", value);
+        String dumped = yaml.dump(parsed);
+        // System.out.println(dumped);
+        assertEquals("{a: 1, b: \"mama\\nmila\\nramu\"}\n", dumped);
+    }
+
+    public void testWriteMultiLineLiteralWithStripChomping() {
+        String source = "a: 1\nb: |-\n  mama\n  mila\n  ramu\n";
+        // System.out.println("Source:\n" + source);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> parsed = (Map<String, Object>) yaml.load(source);
+        String value = (String) parsed.get("b");
+        // System.out.println(value);
+        assertEquals("mama\nmila\nramu", value);
+        String dumped = yaml.dump(parsed);
+        // System.out.println(dumped);
+        assertEquals(source, dumped);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
new file mode 100644
index 0000000..b50310e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
@@ -0,0 +1,263 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+
+public class EmitterTest extends TestCase {
+
+    public void testWriteFolded() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.FOLDED);
+        String folded = "0123456789 0123456789\n0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla\n");
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "\"aaa\": >-\n  0123456789 0123456789\n\n  0123456789 0123456789\n\"bbb\": >2\n\n  bla-bla\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testWriteLiteral() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.LITERAL);
+        String folded = "0123456789 0123456789 0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla\n");
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "\"aaa\": |-\n  0123456789 0123456789 0123456789 0123456789\n\"bbb\": |2\n\n  bla-bla\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testWritePlain() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.PLAIN);
+        String folded = "0123456789 0123456789\n0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla");
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "aaa: |-\n  0123456789 0123456789\n  0123456789 0123456789\nbbb: |2-\n\n  bla-bla\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testWritePlainPretty() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.PLAIN);
+        options.setPrettyFlow(true);
+
+        String folded = "0123456789 0123456789\n0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla");
+
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "aaa: |-\n  0123456789 0123456789\n  0123456789 0123456789\nbbb: |2-\n\n  bla-bla\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testWriteSingleQuoted() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.SINGLE_QUOTED);
+        String folded = "0123456789 0123456789\n0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla");
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "'aaa': '0123456789 0123456789\n\n  0123456789 0123456789'\n'bbb': '\n\n  bla-bla'\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testWriteDoubleQuoted() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        String folded = "0123456789 0123456789\n0123456789 0123456789";
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("aaa", folded);
+        map.put("bbb", "\nbla-bla");
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "\"aaa\": \"0123456789 0123456789\\n0123456789 0123456789\"\n\"bbb\": \"\\nbla-bla\"\n";
+        assertEquals(etalon, output);
+    }
+
+    // Issue #158
+    public void testWriteSupplementaryUnicode() throws IOException {
+        DumperOptions options = new DumperOptions();
+        String burger = new String(Character.toChars(0x1f354));
+        String halfBurger = "\uD83C";
+        StringWriter output = new StringWriter();
+        Emitter emitter = new Emitter(output, options);
+
+        emitter.emit(new StreamStartEvent(null, null));
+        emitter.emit(new DocumentStartEvent(null, null, false, null, null));
+        emitter.emit(new ScalarEvent(null, null, new ImplicitTuple(true, false), burger
+                + halfBurger, null, null, '"'));
+        String expected = "! \"\\U0001f354\\ud83c\"";
+        assertEquals(expected, output.toString());
+    }
+
+    public void testSplitLineExpectFirstFlowSequenceItem() {
+
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setWidth(8);
+        Yaml yaml;
+        String output;
+        Map<String, Object> map = new TreeMap<String, Object>();
+        map.put("12345", Arrays.asList("1111111111"));
+
+        // Split lines enabled (default)
+        yaml = new Yaml(options);
+        output = yaml.dump(map);
+        assertEquals("{\"12345\": [\n    \"1111111111\"]}\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump(map);
+        assertEquals("{\"12345\": [\"1111111111\"]}\n", output);
+    }
+
+    public void testWriteIndicatorIndent() {
+        DumperOptions options = new DumperOptions();
+        options.setIndent(5);
+        options.setIndicatorIndent(2);
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        List<?> topLevel = Arrays.asList(Collections.singletonMap("k1", "v1"), Collections.singletonMap("k2", "v2"));
+        Map<String, ?> map = Collections.singletonMap("aaa", topLevel);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        String etalon = "aaa:\n  -  k1: v1\n  -  k2: v2\n";
+        assertEquals(etalon, output);
+    }
+
+    public void testSplitLineExpectFlowSequenceItem() {
+
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setWidth(8);
+        Yaml yaml;
+        String output;
+
+        // Split lines enabled (default)
+        yaml = new Yaml(options);
+        output = yaml.dump(Arrays.asList("1111111111", "2222222222"));
+        assertEquals("[\"1111111111\",\n  \"2222222222\"]\n", output);
+        output = yaml.dump(Arrays.asList("1", "2"));
+        assertEquals("[\"1\", \"2\"]\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump(Arrays.asList("1111111111", "2222222222"));
+        assertEquals("[\"1111111111\", \"2222222222\"]\n", output);
+        output = yaml.dump(Arrays.asList("1", "2"));
+        assertEquals("[\"1\", \"2\"]\n", output);
+    }
+
+    public void testSplitLineExpectFirstFlowMappingKey() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setWidth(16);
+        Yaml yaml;
+        String output;
+        Map<String, String> nonSplitMap = new TreeMap<String, String>();
+        nonSplitMap.put("3", "4");
+        Map<String, Map<String, String>> nonSplitContainerMap = new TreeMap<String, Map<String, String>>();
+        nonSplitContainerMap.put("1 2", nonSplitMap);
+        Map<String, String> splitMap = new TreeMap<String, String>();
+        splitMap.put("3333333333", "4444444444");
+        Map<String, Map<String, String>> splitContainerMap = new TreeMap<String, Map<String, String>>();
+        splitContainerMap.put("1111111111 2222222222", splitMap);
+
+        // Split lines enabled (default)
+        yaml = new Yaml(options);
+        output = yaml.dump(splitContainerMap);
+        assertEquals("{\"1111111111 2222222222\": {\n    \"3333333333\": \"4444444444\"}}\n", output);
+        output = yaml.dump(nonSplitContainerMap);
+        assertEquals("{\"1 2\": {\"3\": \"4\"}}\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump(splitContainerMap);
+        assertEquals("{\"1111111111 2222222222\": {\"3333333333\": \"4444444444\"}}\n", output);
+        output = yaml.dump(nonSplitContainerMap);
+        assertEquals("{\"1 2\": {\"3\": \"4\"}}\n", output);
+    }
+
+    public void testSplitLineExpectFlowMappingKey() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setWidth(16);
+        Yaml yaml;
+        String output;
+        Map<String, String> nonSplitMap = new TreeMap<String, String>();
+        nonSplitMap.put("1", "2");
+        nonSplitMap.put("3", "4");
+        Map<String, String> splitMap = new TreeMap<String, String>();
+        splitMap.put("1111111111", "2222222222");
+        splitMap.put("3333333333", "4444444444");
+
+        // Split lines enabled (default)
+        yaml = new Yaml(options);
+        output = yaml.dump(splitMap);
+        assertEquals("{\"1111111111\": \"2222222222\",\n  \"3333333333\": \"4444444444\"}\n", output);
+        output = yaml.dump(nonSplitMap);
+        assertEquals("{\"1\": \"2\", \"3\": \"4\"}\n", output);
+
+        // Split lines disabled
+        options.setSplitLines(false);
+        assertFalse(options.getSplitLines());
+        yaml = new Yaml(options);
+        output = yaml.dump(splitMap);
+        assertEquals("{\"1111111111\": \"2222222222\", \"3333333333\": \"4444444444\"}\n", output);
+        output = yaml.dump(nonSplitMap);
+        assertEquals("{\"1\": \"2\", \"3\": \"4\"}\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java b/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java
new file mode 100644
index 0000000..feb773d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EventConstructor.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.events.AliasEvent;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+
+public class EventConstructor extends Constructor {
+
+    public EventConstructor() {
+        this.yamlConstructors.put(null, new ConstructEvent());
+    }
+
+    private class ConstructEvent extends AbstractConstruct {
+
+        @SuppressWarnings("unchecked")
+        public Object construct(Node node) {
+            Map<Object, Object> mapping;
+            if (node instanceof ScalarNode) {
+                mapping = new HashMap<Object, Object>();
+            } else {
+                mapping = constructMapping((MappingNode) node);
+            }
+            String className = node.getTag().getValue().substring(1) + "Event";
+            Event value;
+            if (className.equals("AliasEvent")) {
+                value = new AliasEvent((String) mapping.get("anchor"), null, null);
+            } else if (className.equals("ScalarEvent")) {
+                String tag = (String) mapping.get("tag");
+                String v = (String) mapping.get("value");
+                if (v == null) {
+                    v = "";
+                }
+                List<Boolean> implicitList = (List<Boolean>) mapping.get("implicit");
+                ImplicitTuple implicit;
+                if (implicitList == null) {
+                    implicit = new ImplicitTuple(false, true);
+                } else {
+                    implicit = new ImplicitTuple((Boolean) implicitList.get(0),
+                            (Boolean) implicitList.get(1));
+                }
+                value = new ScalarEvent((String) mapping.get("anchor"), tag, implicit, v, null,
+                        null, null);
+            } else if (className.equals("SequenceStartEvent")) {
+                String tag = (String) mapping.get("tag");
+                Boolean implicit = (Boolean) mapping.get("implicit");
+                if (implicit == null) {
+                    implicit = true;
+                }
+                value = new SequenceStartEvent((String) mapping.get("anchor"), tag, implicit, null,
+                        null, false);
+            } else if (className.equals("MappingStartEvent")) {
+                String tag = (String) mapping.get("tag");
+                Boolean implicit = (Boolean) mapping.get("implicit");
+                if (implicit == null) {
+                    implicit = true;
+                }
+                value = new MappingStartEvent((String) mapping.get("anchor"), tag, implicit, null,
+                        null, false);
+            } else if (className.equals("DocumentEndEvent")) {
+                value = new DocumentEndEvent(null, null, false);
+            } else if (className.equals("DocumentStartEvent")) {
+                Map<String, String> tags = (Map<String, String>) mapping.get("tags");
+                List<Integer> versionList = (List<Integer>) mapping.get("version");
+                Version version = null;
+                // TODO ???
+                if (versionList != null) {
+                    Integer major = versionList.get(0).intValue();
+                    if (major != 1) {
+                        throw new YAMLException("Unsupported version.");
+                    }
+                    Integer minor = versionList.get(1).intValue();
+                    if (minor == 0) {
+                        version = Version.V1_0;
+                    } else {
+                        version = Version.V1_1;
+                    }
+                }
+                value = new DocumentStartEvent(null, null, false, version, tags);
+            } else if (className.equals("MappingEndEvent")) {
+                value = new MappingEndEvent(null, null);
+            } else if (className.equals("SequenceEndEvent")) {
+                value = new SequenceEndEvent(null, null);
+            } else if (className.equals("StreamEndEvent")) {
+                value = new StreamEndEvent(null, null);
+            } else if (className.equals("StreamStartEvent")) {
+                value = new StreamStartEvent(null, null);
+            } else {
+                throw new UnsupportedOperationException();
+            }
+            return value;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/template/MyBean.java b/src/test/java/org/yaml/snakeyaml/emitter/template/MyBean.java
new file mode 100644
index 0000000..188c386
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/emitter/template/MyBean.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter.template;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.yaml.snakeyaml.immutable.Point;
+
+public class MyBean {
+    private Point point;
+    private List<String> list;
+    private List<Integer> empty = new ArrayList<Integer>();
+    private String id;
+
+    public Point getPoint() {
+        return point;
+    }
+
+    public void setPoint(Point point) {
+        this.point = point;
+    }
+
+    public List<String> getList() {
+        return list;
+    }
+
+    public void setList(List<String> list) {
+        this.list = list;
+    }
+
+    public List<Integer> getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(List<Integer> empty) {
+        this.empty = empty;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MyBean) {
+            MyBean bean = (MyBean) obj;
+            if (!id.equals(bean.id)) {
+                return false;
+            }
+            if (!point.equals(bean.point)) {
+                return false;
+            }
+            if (!list.equals(bean.list)) {
+                return false;
+            }
+            if (!empty.equals(bean.empty)) {
+                return false;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return id;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/emitter/template/VelocityTest.java b/src/test/java/org/yaml/snakeyaml/emitter/template/VelocityTest.java
new file mode 100644
index 0000000..df8b310
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/emitter/template/VelocityTest.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.emitter.template;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.immutable.Point;
+
+public class VelocityTest extends TestCase {
+    public void testNoTemplate() {
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dumpAsMap(createBean());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("template/etalon1.yaml"), output);
+    }
+
+    public void testTemplate1() throws Exception {
+        VelocityContext context = new VelocityContext();
+        MyBean bean = createBean();
+        context.put("bean", bean);
+        Yaml yaml = new Yaml();
+        context.put("list", yaml.dump(bean.getList()));
+        VelocityEngine ve = new VelocityEngine();
+        ve.setProperty("file.resource.loader.class", ClasspathResourceLoader.class.getName());
+        ve.init();
+        Template t = ve.getTemplate("template/mybean1.vm");
+        StringWriter writer = new StringWriter();
+        t.merge(context, writer);
+        String output = writer.toString().trim().replaceAll("\\r\\n", "\n");
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("template/etalon2-template.yaml").trim();
+        assertEquals(etalon.length(), output.length());
+        assertEquals(etalon, output);
+        // parse the YAML document
+        Yaml loader = new Yaml();
+        MyBean parsedBean = loader.loadAs(output, MyBean.class);
+        assertEquals(bean, parsedBean);
+    }
+
+    private MyBean createBean() {
+        MyBean bean = new MyBean();
+        bean.setId("id123");
+        List<String> list = new ArrayList<String>();
+        list.add("aaa");
+        list.add("bbb");
+        list.add("ccc");
+        bean.setList(list);
+        Point p = new Point(1.0, 2.0);
+        bean.setPoint(p);
+        return bean;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/error/MarkTest.java b/src/test/java/org/yaml/snakeyaml/error/MarkTest.java
new file mode 100644
index 0000000..b67d8fa
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/error/MarkTest.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.error;
+
+import junit.framework.TestCase;
+
+public class MarkTest extends TestCase {
+
+    public void testGet_snippet() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        assertEquals("    *The first line.\n    ^", mark.get_snippet());
+        mark = new Mark("test1", 9, 0, 0, "The first*line.\nThe last line.", 9);
+        assertEquals("    The first*line.\n             ^", mark.get_snippet());
+    }
+
+    public void testToString() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        String[] lines = mark.toString().split("\n");
+        assertEquals(" in test1, line 1, column 1:", lines[0]);
+        assertEquals("*The first line.", lines[1].trim());
+        assertEquals("^", lines[2].trim());
+    }
+
+    public void testPosition() {
+        Mark mark = new Mark("test1", 17, 29, 213, "*The first line.\nThe last line.", 0);
+        assertEquals(17, mark.getIndex());
+        assertEquals(29, mark.getLine());
+        assertEquals(213, mark.getColumn());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/error/MarkedYAMLExceptionTest.java b/src/test/java/org/yaml/snakeyaml/error/MarkedYAMLExceptionTest.java
new file mode 100644
index 0000000..0725db2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/error/MarkedYAMLExceptionTest.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.error;
+
+import junit.framework.TestCase;
+
+public class MarkedYAMLExceptionTest extends TestCase {
+
+    public void testToString1() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        MarkedYAMLException exception = new MarkedYAMLException(null, null, "Error happened", mark);
+        assertTrue(exception.toString().contains("Error happened"));
+        assertTrue(exception.toString().contains("The first line"));
+        assertTrue(exception.toString(), exception.toString().contains("test1"));
+    }
+
+    public void testToString2() {
+        Mark mark = new Mark("search", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        MarkedYAMLException exception = new MarkedYAMLException("See http://www.google.com", mark,
+                "Error2 happened", mark);
+        assertTrue(exception.toString().contains("Error2 happened"));
+        assertTrue(exception.toString().contains("The first line"));
+        assertTrue(exception.toString().contains("search"));
+    }
+
+    public void testToString3() {
+        MarkedYAMLException exception = new MarkedYAMLException("See http://www.google.com", null,
+                null, null, "Note1");
+        assertTrue(exception.toString().contains("Note1"));
+    }
+
+    public void testToString4() {
+        Mark mark = new Mark("search", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        MarkedYAMLException exception = new MarkedYAMLException("See http://www.google.com", mark,
+                null, null, null, null);
+        assertTrue(exception.toString().contains("first line"));
+    }
+
+    public void testGetters() {
+        Mark mark = new Mark("search", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        MarkedYAMLException exception = new MarkedYAMLException("See http://www.google.com", mark,
+                "Error2 happened", mark);
+        assertEquals("See http://www.google.com", exception.getContext());
+        assertEquals(mark, exception.getContextMark());
+        assertEquals("Error2 happened", exception.getProblem());
+        assertEquals(mark, exception.getProblemMark());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/events/ScalarEventTest.java b/src/test/java/org/yaml/snakeyaml/events/ScalarEventTest.java
new file mode 100644
index 0000000..b167f90
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/events/ScalarEventTest.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.events;
+
+import junit.framework.TestCase;
+
+public class ScalarEventTest extends TestCase {
+
+    public void testToString() {
+        ScalarEvent event = new ScalarEvent("a2", "str", new ImplicitTuple(true, true), "text",
+                null, null, '"');
+        assertEquals(
+                "<org.yaml.snakeyaml.events.ScalarEvent(anchor=a2, tag=str, implicit=[true, true], value=text)>",
+                event.toString());
+    }
+
+    public void testNotEqual() {
+        ScalarEvent event = new ScalarEvent("a2", "str", new ImplicitTuple(true, true), "text",
+                null, null, '"');
+        assertFalse(event.equals(event.toString()));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Box.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Box.java
new file mode 100644
index 0000000..5bcc036
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Box.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class Box {
+    private String id;
+    private String name;
+    private Item top;
+    private Item bottom;
+
+    public Box(String id, String name) {
+        super();
+        this.id = id;
+        this.name = name;
+    }
+
+    public Item getTop() {
+        return top;
+    }
+
+    public void setTop(Item top) {
+        this.top = top;
+    }
+
+    public Item getBottom() {
+        return bottom;
+    }
+
+    public void setBottom(Item bottom) {
+        this.bottom = bottom;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorErrorsTest.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorErrorsTest.java
new file mode 100644
index 0000000..161286c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorErrorsTest.java
@@ -0,0 +1,146 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.BaseConstructor;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class CompactConstructorErrorsTest extends TestCase {
+
+    public void test1() {
+        BaseConstructor compact = new CompactConstructor();
+        Yaml yaml = new Yaml(compact);
+        String doc = Util.getLocalResource("compactnotation/error1.yaml");
+        try {
+            yaml.load(doc);
+            fail("Package is not specified.");
+        } catch (Exception e) {
+            assertEquals("java.lang.ClassNotFoundException: Table", e.getMessage());
+        }
+    }
+
+    private Object load(String fileName) {
+        CompactConstructor compact = new PackageCompactConstructor(
+                "org.yaml.snakeyaml.extensions.compactnotation");
+        Yaml yaml = new Yaml(compact);
+        String doc = Util.getLocalResource("compactnotation/" + fileName);
+        Object obj = yaml.load(doc);
+        assertNotNull(obj);
+        return obj;
+    }
+
+    private void failLoad(String fileName, String failure) {
+        load(fileName);
+        fail(failure);
+    }
+
+    private void check(String fileName, String failure, String message) {
+        check(fileName, failure, message, true);
+    }
+
+    private void check(String fileName, String failure, String message, boolean exactMatch) {
+        try {
+            failLoad(fileName, failure);
+        } catch (YAMLException e) {
+            String eMessage = e.getMessage();
+            if (exactMatch) {
+                assertEquals(message, eMessage);
+            } else {
+                assertNotNull("Exception message is NULL", eMessage);
+                assertTrue(String.format(
+                        "\nException message\n%s\ndoes not contain expected value\n%s",
+                        e.getMessage(), message), eMessage.contains(message));
+            }
+        } catch (Exception e) {
+            fail("Exception must be YAMLException");
+        }
+    }
+
+    public void test2() {
+        check("error2.yaml",
+                "No single argument constructor provided.",
+                "java.lang.NoSuchMethodException: org.yaml.snakeyaml.extensions.compactnotation.Table.<init>(java.lang.String)");
+    }
+
+    public void test3() {
+        check("error3.yaml",
+                "In-line parameters can only be Strings.",
+                "org.yaml.snakeyaml.error.YAMLException: Cannot set property='size' with value='17' (class java.lang.String) in Row id=id111");
+    }
+
+    /**
+     * Created Map instead of Row
+     */
+    @SuppressWarnings("unchecked")
+    public void test4() {
+        Table table = (Table) load("error4.yaml");
+        List<Row> rows = table.getRows();
+        assertEquals(1, rows.size());
+        assertFalse("Row should not be created.", rows.get(0) instanceof Row);
+        Map<String, String> map = (Map<String, String>) rows.get(0);
+        assertEquals(1, map.size());
+        assertEquals("15}", map.get("Row(id111, description = text) {size"));
+    }
+
+    /**
+     * Wrong indent
+     */
+    @SuppressWarnings("unchecked")
+    public void test5() {
+        Table table = (Table) load("error5.yaml");
+        List<Row> rows = table.getRows();
+        assertEquals(1, rows.size());
+        assertFalse("Row should not be created.", rows.get(0) instanceof Row);
+        Map<String, String> map = (Map<String, String>) rows.get(0);
+        assertEquals(4, map.size());
+        // System.out.println(map);
+        assertNull(map.get(new Row("id222")));
+        assertTrue(map.containsKey(new Row("id222")));
+        assertEquals(17, map.get("size"));
+    }
+
+    public void test6() {
+        check("error6.yaml",
+                "Invalid property.",
+                "org.yaml.snakeyaml.error.YAMLException: Unable to find property 'foo' on class: org.yaml.snakeyaml.extensions.compactnotation.Table");
+    }
+
+    public void test7() {
+        check("error7.yaml",
+                "Invalid property.",
+                "Unable to find property 'foo' on class: org.yaml.snakeyaml.extensions.compactnotation.Table",
+                false);
+    }
+
+    public void test8() {
+        check("error8.yaml",
+                "No list property",
+                "org.yaml.snakeyaml.error.YAMLException: No list property found in class org.yaml.snakeyaml.extensions.compactnotation.Row");
+    }
+
+    public void test9() {
+        check("error9.yaml",
+                "Many list properties found",
+                "org.yaml.snakeyaml.error.YAMLException: Many list properties found in class org.yaml.snakeyaml.extensions.compactnotation.ManyListsTable; Please override getSequencePropertyName() to specify which property to use.");
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorExampleTest.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorExampleTest.java
new file mode 100644
index 0000000..d8e355c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorExampleTest.java
@@ -0,0 +1,180 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class CompactConstructorExampleTest extends TestCase {
+
+    private Object load(String fileName) {
+        CompactConstructor compact = new CompactConstructor();
+        Yaml yaml = new Yaml(compact);
+        String doc = Util.getLocalResource("compactnotation/" + fileName);
+        Object obj = yaml.load(doc);
+        assertNotNull(obj);
+        return obj;
+    }
+
+    public void test1() {
+        Object obj = load("example1.yaml");
+        assertEquals(new Container(), obj);
+    }
+
+    public void test2() {
+        Object obj = load("example2.yaml");
+        assertEquals(new Container("title"), obj);
+    }
+
+    public void test3() {
+        Container obj = (Container) load("example3.yaml");
+        assertEquals(new Container("title3"), obj);
+        assertEquals("title3", obj.getTitle());
+        assertEquals("parent", obj.getName());
+        assertEquals("123", obj.getId());
+    }
+
+    public void test4() {
+        Object obj = load("example4.yaml");
+        // System.out.println(obj);
+        Container container = (Container) obj;
+        assertNotNull(obj);
+        assertEquals(new Container("title4"), obj);
+        assertEquals("title4", container.getTitle());
+        assertEquals("child4", container.getName());
+        assertEquals("444", container.getId());
+    }
+
+    public void test5() {
+        Object obj = load("example5.yaml");
+        // System.out.println(obj);
+        Container container = (Container) obj;
+        assertNotNull(obj);
+        assertEquals(new Container("title4"), obj);
+        assertEquals("title4", container.getTitle());
+        assertEquals("child5", container.getName());
+        assertEquals("ID555", container.getId());
+    }
+
+    public void test6() {
+        Object obj = load("example6.yaml");
+        // System.out.println(obj);
+        Container container = (Container) obj;
+        assertNotNull(obj);
+        assertEquals(new Container("title4"), obj);
+        assertEquals("title4", container.getTitle());
+        assertEquals("child6", container.getName());
+        assertEquals("ID6", container.getId());
+    }
+
+    public void test7() {
+        Object obj = load("example7.yaml");
+        // System.out.println(obj);
+        Container container = (Container) obj;
+        assertNotNull(obj);
+        assertEquals(new Container("The title"), obj);
+        assertEquals("The title", container.getTitle());
+        assertEquals("child7", container.getName());
+        assertEquals("id7", container.getId());
+    }
+
+    @SuppressWarnings("unchecked")
+    // TODO it is unclear how the result should look like for CON
+    public void test9() {
+        Map<String, Object> map = (Map<String, Object>) load("example9.yaml");
+        assertEquals(1, map.size());
+        Map<Container, Map<String, String>> containers = (Map<Container, Map<String, String>>) map
+                .get("something");
+        // System.out.println(obj);
+        assertEquals(2, containers.size());
+        for (Container c : containers.keySet()) {
+            assertTrue(c.getId().matches("id\\d"));
+            assertEquals(1, containers.get(c).size());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void test10() {
+        Map<String, Object> map = (Map<String, Object>) load("example10.yaml");
+        assertEquals(1, map.size());
+        List<Container> containers = (List<Container>) map.get("something");
+        // System.out.println(obj);
+        assertEquals(3, containers.size());
+        for (Container c : containers) {
+            assertTrue(c.toString(), c.getId().matches("id\\d+"));
+            assertTrue(c.toString(), c.getName().matches("child\\d+"));
+            // System.out.println(c);
+        }
+    }
+
+    public void test11withoutPackageNames() {
+        Constructor compact = new PackageCompactConstructor(
+                "org.yaml.snakeyaml.extensions.compactnotation");
+        Yaml yaml = new Yaml(compact);
+        String doc = Util.getLocalResource("compactnotation/example11.yaml");
+        Box box = (Box) yaml.load(doc);
+        assertNotNull(box);
+        assertEquals("id11", box.getId());
+        assertEquals("Main box", box.getName());
+        Item top = box.getTop();
+        assertEquals("id003", top.getId());
+        assertEquals("25.0", top.getPrice());
+        assertEquals("parrot", top.getName());
+        Item bottom = box.getBottom();
+        assertEquals("id004", bottom.getId());
+        assertEquals("3.5", bottom.getPrice());
+        assertEquals("sweet", bottom.getName());
+    }
+
+    public void test12withList() {
+        Constructor compact = new TableCompactConstructor(
+                "org.yaml.snakeyaml.extensions.compactnotation");
+        Yaml yaml = new Yaml(compact);
+        String doc = Util.getLocalResource("compactnotation/example12.yaml");
+        Table table = (Table) yaml.load(doc);
+        assertNotNull(table);
+        assertEquals("id12", table.getId());
+        assertEquals("A table", table.getName());
+        List<Row> rows = table.getRows();
+        assertEquals(3, rows.size());
+        Iterator<Row> iter = rows.iterator();
+        Row first = iter.next();
+        assertEquals("id111", first.getId());
+        assertEquals("I think; therefore I am.", first.getDescription());
+        assertEquals(0.125, first.getRatio(), 0.000000001);
+        assertEquals(15, first.getSize());
+        Row second = iter.next();
+        assertEquals("id222", second.getId());
+        assertEquals("We do not need new lines here, just replace them all with spaces\n",
+                second.getDescription());
+        assertEquals(0.333, second.getRatio(), 0.000000001);
+        assertEquals(17, second.getSize());
+        Row third = iter.next();
+        assertEquals("id333", third.getId());
+        assertEquals(
+                "Please preserve all\nthe lines because they may be\nimportant, but do not include the last one !!!",
+                third.getDescription());
+        assertEquals(0.88, third.getRatio(), 0.000000001);
+        assertEquals(52, third.getSize());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorTest.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorTest.java
new file mode 100644
index 0000000..837d463
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/CompactConstructorTest.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import junit.framework.TestCase;
+
+public class CompactConstructorTest extends TestCase {
+
+    public void testNoCompactData() {
+        CompactConstructor flow = new CompactConstructor();
+        assertNull(flow.getCompactData("scalar"));
+        assertNull(flow.getCompactData("123"));
+        assertNull(flow.getCompactData("(name=frame,title=My Frame)"));
+        assertNull(flow.getCompactData("JFrame name=frame,title=My Frame)"));
+        assertNull(flow.getCompactData("JFrame name=frame,title=My Frame"));
+        assertNull(flow.getCompactData("JFrame(name=frame,title=My Frame"));
+        assertNull(flow.getCompactData("JFrame(name=frame,title=My Frame)b"));
+        assertNull(flow.getCompactData("JFrame(name=frame,title=My Frame) "));
+        assertNull(flow.getCompactData("JFrame(name=)"));
+        assertNull(flow.getCompactData("JFrame(=name)"));
+    }
+
+    public void testGetCompactData1() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("JFrame(name=frame)");
+        assertNotNull(data);
+        assertEquals("JFrame", data.getPrefix());
+        assertEquals(1, data.getProperties().size());
+        assertEquals("frame", data.getProperties().get("name"));
+    }
+
+    public void testGetCompactData2() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("Frame(name=frame,title=My Frame)");
+        assertNotNull(data);
+        assertEquals("Frame", data.getPrefix());
+        assertEquals(2, data.getProperties().size());
+        assertEquals("frame", data.getProperties().get("name"));
+        assertEquals("My Frame", data.getProperties().get("title"));
+
+        assertNotNull(flow.getCompactData("JFrame ( name = frame , title = My Frame )"));
+    }
+
+    public void testGetCompactData3() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow
+                .getCompactData("JFrame ( name = frame , title = My Frame, number= 123 )");
+        assertNotNull(data);
+        assertEquals("JFrame", data.getPrefix());
+        assertEquals(3, data.getProperties().size());
+        assertEquals("frame", data.getProperties().get("name"));
+        assertEquals("My Frame", data.getProperties().get("title"));
+        assertEquals("123", data.getProperties().get("number"));
+    }
+
+    public void testGetCompactData4() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("JFrame(title)");
+        assertNotNull(data);
+        assertEquals("JFrame", data.getPrefix());
+        assertEquals(0, data.getProperties().size());
+        assertEquals(1, data.getArguments().size());
+        assertEquals("title", data.getArguments().get(0));
+    }
+
+    public void testGetCompactData5() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("JFrame(id123, title, name=foo, alignment=center)");
+        assertNotNull(data);
+        assertEquals("JFrame", data.getPrefix());
+        assertEquals(2, data.getProperties().size());
+        assertEquals(2, data.getArguments().size());
+        assertEquals("id123", data.getArguments().get(0));
+        assertEquals("title", data.getArguments().get(1));
+    }
+
+    public void testGetCompactData6() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("JFrame()");
+        assertNotNull(data);
+        assertEquals("JFrame", data.getPrefix());
+        assertEquals(0, data.getProperties().size());
+        assertEquals(0, data.getArguments().size());
+    }
+
+    public void testGetCompactData7() {
+        CompactConstructor flow = new CompactConstructor();
+        CompactData data = flow.getCompactData("package.Container(name=parent, id=123)");
+        assertNotNull(data);
+        assertEquals("package.Container", data.getPrefix());
+        assertEquals(2, data.getProperties().size());
+        assertEquals(0, data.getArguments().size());
+    }
+
+    public void testCompactDataToString() {
+        CompactData data = new CompactData("foo");
+        assertEquals("CompactData: foo {}", data.toString());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Container.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Container.java
new file mode 100644
index 0000000..56b3c19
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Container.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class Container {
+    private String title;
+    private String name;
+    private String id;
+
+    public Container() {
+        this("no title");
+    }
+
+    public Container(String title) {
+        this.title = title;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Container) {
+            Container c = (Container) obj;
+            if (name != null && !name.equals(c.name)) {
+                return false;
+            }
+            if (id != null && !id.equals(c.id)) {
+                return false;
+            }
+            return title.equals(c.title);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return title.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Container=" + title;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Item.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Item.java
new file mode 100644
index 0000000..d6314c0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Item.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class Item {
+    private String id;
+    private String price;
+    private String name;
+
+    public Item(String id) {
+        this.id = id;
+    }
+
+    public String getPrice() {
+        return price;
+    }
+
+    public void setPrice(String price) {
+        this.price = price;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getId() {
+        return id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/ManyListsTable.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/ManyListsTable.java
new file mode 100644
index 0000000..8b52b2e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/ManyListsTable.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.util.List;
+
+public class ManyListsTable {
+    private String id;
+    private List<Row> rows;
+    private List<String> names;
+
+    public ManyListsTable(String id) {
+        this.id = id;
+    }
+
+    public List<Row> getRows() {
+        return rows;
+    }
+
+    public void setRows(List<Row> rows) {
+        this.rows = rows;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public List<String> getNames() {
+        return names;
+    }
+
+    public void setNames(List<String> names) {
+        this.names = names;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructorTest.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructorTest.java
new file mode 100644
index 0000000..551b721
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/PackageCompactConstructorTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import junit.framework.TestCase;
+
+public class PackageCompactConstructorTest extends TestCase {
+
+    public void testGetClassForName() throws ClassNotFoundException {
+        assertEquals(Table.class, check("Table"));
+        assertEquals(Table.class, check("org.yaml.snakeyaml.extensions.compactnotation.Table"));
+        assertEquals(String.class, check("java.lang.String"));
+    }
+
+    public void testException1() throws ClassNotFoundException {
+        try {
+            check("foo.Bar");
+            fail();
+        } catch (ClassNotFoundException e) {
+            assertEquals("foo.Bar", e.getMessage());
+        }
+    }
+
+    public void testException2() throws ClassNotFoundException {
+        try {
+            check("FooBar");
+            fail();
+        } catch (ClassNotFoundException e) {
+            assertEquals("FooBar", e.getMessage());
+        }
+    }
+
+    private Class<?> check(String name) throws ClassNotFoundException {
+        return new PackageCompactConstructor("org.yaml.snakeyaml.extensions.compactnotation")
+                .getClassForName(name);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Row.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Row.java
new file mode 100644
index 0000000..8e21ad0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Row.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class Row {
+    private String id;
+    private int size;
+    private double ratio;
+    private float floatRatio;
+    private String description;
+
+    public Row(String id) {
+        super();
+        this.id = id;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+    public double getRatio() {
+        return ratio;
+    }
+
+    public void setRatio(double ratio) {
+        this.ratio = ratio;
+    }
+
+    public float getFloatRatio() {
+        return floatRatio;
+    }
+
+    public void setFloatRatio(float floatRatio) {
+        this.floatRatio = floatRatio;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return toString().equals(obj.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Row id=" + id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Table.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Table.java
new file mode 100644
index 0000000..dce1eb0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/Table.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+import java.util.List;
+
+public class Table {
+    private String id;
+    private String name;
+    private List<Row> rows;
+
+    public Table(String id, String name) {
+        super();
+        this.id = id;
+        this.name = name;
+    }
+
+    public List<Row> getRows() {
+        return rows;
+    }
+
+    public void setRows(List<Row> rows) {
+        this.rows = rows;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/TableCompactConstructor.java b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/TableCompactConstructor.java
new file mode 100644
index 0000000..a6e074b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/extensions/compactnotation/TableCompactConstructor.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.extensions.compactnotation;
+
+public class TableCompactConstructor extends PackageCompactConstructor {
+
+    public TableCompactConstructor(String packageName) {
+        super(packageName);
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/AbstractAnimal.java b/src/test/java/org/yaml/snakeyaml/generics/AbstractAnimal.java
new file mode 100644
index 0000000..68452ef
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/AbstractAnimal.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+public abstract class AbstractAnimal<T> {
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public abstract T getHome();
+
+    public abstract void setHome(T home);
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/Bird.java b/src/test/java/org/yaml/snakeyaml/generics/Bird.java
new file mode 100644
index 0000000..0b7bbe8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/Bird.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+public class Bird extends AbstractAnimal<Nest> {
+    private Nest home;
+
+    public Nest getHome() {
+        return home;
+    }
+
+    public void setHome(Nest home) {
+        this.home = home;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/BirdTest.java b/src/test/java/org/yaml/snakeyaml/generics/BirdTest.java
new file mode 100644
index 0000000..499539f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/BirdTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import java.beans.IntrospectionException;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class BirdTest extends TestCase {
+
+    public void testHome() throws IntrospectionException {
+        Bird bird = new Bird();
+        bird.setName("Eagle");
+        Nest home = new Nest();
+        home = new Nest();
+        home.setHeight(3);
+        bird.setHome(home);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bird);
+        Bird parsed;
+        String javaVendor = System.getProperty("java.vm.name");
+        Yaml loader = new Yaml();
+        if (GenericsBugDetector.isProperIntrospection()) {
+            // no global tags
+            System.out.println("java.vm.name: " + javaVendor);
+            assertEquals("no global tags must be emitted.", "home:\n  height: 3\nname: Eagle\n",
+                    output);
+            parsed = loader.loadAs(output, Bird.class);
+
+        } else {
+            // with global tags
+            System.out
+                    .println("JDK requires global tags for JavaBean properties with Java Generics. java.vm.name: "
+                            + javaVendor);
+            assertEquals("global tags are inevitable here.",
+                    "home: !!org.yaml.snakeyaml.generics.Nest\n  height: 3\nname: Eagle\n", output);
+            parsed = loader.loadAs(output, Bird.class);
+        }
+        assertEquals(bird.getName(), parsed.getName());
+        assertEquals(bird.getHome().getHeight(), parsed.getHome().getHeight());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/GenericArrayTypeTest.java b/src/test/java/org/yaml/snakeyaml/generics/GenericArrayTypeTest.java
new file mode 100644
index 0000000..e81c137
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/GenericArrayTypeTest.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import java.beans.IntrospectionException;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericArrayTypeTest extends TestCase {
+
+    public void testClasses() throws IntrospectionException {
+        GenericArray ga = new GenericArray();
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(ga);
+        // System.out.println(doc);
+        String etalon = "!!org.yaml.snakeyaml.generics.GenericArrayTypeTest$GenericArray\n"
+                + "home: [1, 2, 3]\n" + "name: Array3\n";
+        assertEquals(etalon, doc);
+        if (GenericsBugDetector.isProperIntrospection()) {
+            GenericArray parsed = (GenericArray) yaml.load(doc);
+            assertEquals("Array3", parsed.getName());
+            assertEquals(3, parsed.getHome().length);
+        } else {
+            try {
+                yaml.load(doc);
+            } catch (Exception e) {
+                // TODO Check GenericArrayType
+                String message = "Cannot create property=home for JavaBean=org.yaml.snakeyaml.generics.GenericArrayTypeTest$GenericArray";
+                assertTrue(e.getMessage(), e.getMessage().contains(message));
+            }
+        }
+    }
+
+    public static class GenericArray extends AbstractAnimal<Integer[]> {
+        private Integer[] home;
+
+        public GenericArray() {
+            home = new Integer[3];
+            for (int i = 0; i < home.length; i++) {
+                home[i] = i + 1;
+            }
+            setName("Array" + String.valueOf(3));
+        }
+
+        @Override
+        public Integer[] getHome() {
+            return home;
+        }
+
+        @Override
+        public void setHome(Integer[] home) {
+            this.home = home;
+        }
+    }
+
+    public void testJavaBean() throws IntrospectionException {
+        GenericArray ga = new GenericArray();
+        ArrayBean bean = new ArrayBean();
+        bean.setId("ID556677");
+        bean.setGa(ga);
+        Yaml dumper = new Yaml();
+        String doc = dumper.dumpAsMap(bean);
+        // System.out.println(doc);
+        assertEquals(Util.getLocalResource("javabeans/genericArray-1.yaml"), doc);
+        //
+        Yaml beanLoader = new Yaml();
+        if (GenericsBugDetector.isProperIntrospection()) {
+            ArrayBean loaded = beanLoader.loadAs(doc, ArrayBean.class);
+            assertEquals("ID556677", loaded.getId());
+            assertEquals("Array3", loaded.getGa().getName());
+            assertEquals(3, loaded.getGa().getHome().length);
+        } else {
+            try {
+                beanLoader.load(doc);
+            } catch (Exception e) {
+                // TODO Check GenericArrayType
+                String message = "Cannot create property=home for JavaBean=org.yaml.snakeyaml.generics.GenericArrayTypeTest$GenericArray";
+                assertTrue(e.getMessage(), e.getMessage().contains(message));
+            }
+        }
+    }
+
+    public static class ArrayBean {
+        private String id;
+        private GenericArray ga;
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public GenericArray getGa() {
+            return ga;
+        }
+
+        public void setGa(GenericArray ga) {
+            this.ga = ga;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/GenericsBugDetector.java b/src/test/java/org/yaml/snakeyaml/generics/GenericsBugDetector.java
new file mode 100644
index 0000000..ad7f392
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/GenericsBugDetector.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+
+/**
+ * @see <a href="http://bugs.sun.com/view_bug.do?bug_id=6528714"></a>
+ */
+public class GenericsBugDetector {
+    /**
+     * Check whether the proper class Nest for Bird's property 'home' is
+     * recognized.
+     */
+    public static boolean isProperIntrospection() throws IntrospectionException {
+        for (PropertyDescriptor property : Introspector.getBeanInfo(Bird.class)
+                .getPropertyDescriptors()) {
+            if (property.getName().equals("home")) {
+                return property.getPropertyType() == Nest.class;
+            }
+        }
+        throw new RuntimeException("Bird must contain 'home' property.");
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/Nest.java b/src/test/java/org/yaml/snakeyaml/generics/Nest.java
new file mode 100644
index 0000000..fbf8dbf
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/Nest.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+public class Nest {
+    private int height;
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/ObjectValues.java b/src/test/java/org/yaml/snakeyaml/generics/ObjectValues.java
new file mode 100644
index 0000000..a321910
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/ObjectValues.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import java.util.Map;
+
+public class ObjectValues {
+
+    private Object object;
+    private Map<String, Map<Integer, Object>> values;
+    private String[] possible;
+
+    public Object getObject() {
+        return object;
+    }
+
+    public void setObject(Object object) {
+        this.object = object;
+    }
+
+    public void setValues(Map<String, Map<Integer, Object>> values) {
+        this.values = values;
+    }
+
+    public Map<String, Map<Integer, Object>> getValues() {
+        return values;
+    }
+
+    public void setPossible(String[] possible) {
+        this.possible = possible;
+    }
+
+    public String[] getPossible() {
+        return possible;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java
new file mode 100644
index 0000000..233a03d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesTest.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ObjectValuesTest extends TestCase {
+
+    public void testObjectValues() {
+        ObjectValues ov = new ObjectValues();
+        Integer obj = new Integer(131313);
+        ov.setObject(obj);
+        final Map<String, Map<Integer, Object>> prop2values = new HashMap<String, Map<Integer, Object>>();
+
+        final String[] props = { "prop1", "prop2", "prop3" };
+        for (String name : props) {
+            Map<Integer, Object> values = new HashMap<Integer, Object>();
+            prop2values.put(name, values);
+            for (int i = 0; i < 3; i++) {
+                values.put(i, name + i);
+            }
+        }
+
+        ov.setValues(prop2values);
+        ov.setPossible(props);
+
+        Yaml dumper = new Yaml();
+        String dumpedStr = dumper.dumpAsMap(ov);
+        Yaml loader = new Yaml();
+        ObjectValues ov2 = loader.loadAs(dumpedStr, ObjectValues.class);
+
+        assertEquals(ov.getObject(), ov2.getObject());
+        assertEquals(ov.getValues(), ov2.getValues());
+        assertArrayEquals(ov.getPossible(), ov2.getPossible());
+        ov.getPossible()[0] = ov2.getPossible()[0];
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testObjectValuesWithParam() {
+        ObjectValuesWithParam<String, Integer> ov = new ObjectValuesWithParam<String, Integer>();
+        Integer obj = new Integer(131313);
+        ov.setObject(obj);
+        final Map<String, Map<Integer, Object>> prop2values = new HashMap<String, Map<Integer, Object>>();
+
+        final String[] props = { "prop1", "prop2", "prop3" };
+        for (String name : props) {
+            Map<Integer, Object> values = new HashMap<Integer, Object>();
+            prop2values.put(name, values);
+            for (int i = 0; i < 3; i++) {
+                values.put(i, name + i);
+            }
+        }
+
+        ov.setValues(prop2values);
+        ov.setPossible(props);
+
+        Yaml dumper = new Yaml();
+        String dumpedStr = dumper.dumpAsMap(ov);
+        Yaml loader = new Yaml();
+        ObjectValuesWithParam<String, Integer> ov2 = loader.loadAs(dumpedStr,
+                new ObjectValuesWithParam<String, Integer>().getClass());
+
+        assertEquals(ov.getObject(), ov2.getObject());
+        assertEquals(ov.getValues(), ov2.getValues());
+        assertArrayEquals(ov.getPossible(), ov2.getPossible());
+        // TODO: This actually FAILS. Use of GenericArrays is ..... no words.
+        // assertEquals(ov.getPossible()[0], ov2.getPossible()[0]);
+        try {
+            ov2.getPossible()[0].toString();
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().startsWith("[Ljava.lang.Object"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesWithParam.java b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesWithParam.java
new file mode 100644
index 0000000..51c4485
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/generics/ObjectValuesWithParam.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.generics;
+
+import java.util.Map;
+
+public class ObjectValuesWithParam<T, S> {
+
+    private Object object;
+    private Map<T, Map<S, Object>> values;
+    private T[] possible;
+
+    public Object getObject() {
+        return object;
+    }
+
+    public void setObject(Object object) {
+        this.object = object;
+    }
+
+    public void setValues(Map<T, Map<S, Object>> values) {
+        this.values = values;
+    }
+
+    public Map<T, Map<S, Object>> getValues() {
+        return values;
+    }
+
+    public void setPossible(T[] possible) {
+        this.possible = possible;
+    }
+
+    public T[] getPossible() {
+        return possible;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Animal.java b/src/test/java/org/yaml/snakeyaml/immutable/Animal.java
new file mode 100644
index 0000000..f7dcb73
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Animal.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public interface Animal {
+    public String getName();
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code.java b/src/test/java/org/yaml/snakeyaml/immutable/Code.java
new file mode 100644
index 0000000..3ef1de4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Code {
+    private final Integer code;
+
+    public Code(Integer name) {
+        this.code = name;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code) {
+            Code code = (Code) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code code=" + code + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code2.java b/src/test/java/org/yaml/snakeyaml/immutable/Code2.java
new file mode 100644
index 0000000..02fc2e9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code2.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * Two constructors with 1 argument. These immutable objects are not supported.
+ */
+public class Code2 {
+    private final Integer code;
+
+    public Code2(Integer name) {
+        this.code = name;
+    }
+
+    public Code2(String name) {
+        this.code = new Integer(name);
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code2) {
+            Code2 code = (Code2) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code2 code=" + code + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code3.java b/src/test/java/org/yaml/snakeyaml/immutable/Code3.java
new file mode 100644
index 0000000..2638532
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code3.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * No constructors with 1 argument. These immutable objects are not supported.
+ */
+public class Code3 {
+    private final String name;
+    private final Integer code;
+
+    public Code3(String name, Integer code) {
+        this.code = code;
+        this.name = name;
+    }
+
+    public String getData() {
+        return name + code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code3) {
+            Code3 code = (Code3) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code3 data=" + getData() + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Code4.java b/src/test/java/org/yaml/snakeyaml/immutable/Code4.java
new file mode 100644
index 0000000..71c6828
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Code4.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * Two constructors with 1 argument. None of them has String as the argument
+ * class.
+ */
+public class Code4 {
+    private final Integer code;
+
+    public Code4(Integer name) {
+        this.code = name;
+    }
+
+    public Code4(Double name) {
+        this.code = new Integer(name.intValue());
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Code4) {
+            Code4 code = (Code4) obj;
+            return code.equals(code.code);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return code.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Code4 code=" + code + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Color.java b/src/test/java/org/yaml/snakeyaml/immutable/Color.java
new file mode 100644
index 0000000..2ac105a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Color.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Color {
+    private final String name;
+
+    public Color(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Color) {
+            Color color = (Color) obj;
+            return name.equals(color.name);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "<Color id=" + name + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Dog.java b/src/test/java/org/yaml/snakeyaml/immutable/Dog.java
new file mode 100644
index 0000000..710aab3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Dog.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Dog implements Animal {
+    private String name;
+
+    public Dog(String name) {
+        super();
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void bark() {
+        System.out.println("I am a " + name);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/DogImmutableTest.java b/src/test/java/org/yaml/snakeyaml/immutable/DogImmutableTest.java
new file mode 100644
index 0000000..90b1ff6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/DogImmutableTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class DogImmutableTest extends TestCase {
+
+    public void testDog() {
+        Yaml yaml = new Yaml();
+        Dog loaded = (Dog) yaml.load("!!org.yaml.snakeyaml.immutable.Dog Bulldog");
+        assertEquals("Bulldog", loaded.getName());
+    }
+
+    public void testHouse() {
+        Yaml yaml = new Yaml();
+        HouseBean loaded = (HouseBean) yaml
+                .load("!!org.yaml.snakeyaml.immutable.HouseBean\nanimal: !!org.yaml.snakeyaml.immutable.Dog Bulldog");
+        assertEquals("Bulldog", loaded.getAnimal().getName());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/HouseBean.java b/src/test/java/org/yaml/snakeyaml/immutable/HouseBean.java
new file mode 100644
index 0000000..edee6b1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/HouseBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class HouseBean {
+    private String name;
+    private Animal animal;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Animal getAnimal() {
+        return animal;
+    }
+
+    public void setAnimal(Animal animal) {
+        this.animal = animal;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/ImmutablesRepresenter.java b/src/test/java/org/yaml/snakeyaml/immutable/ImmutablesRepresenter.java
new file mode 100644
index 0000000..0595731
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/ImmutablesRepresenter.java
@@ -0,0 +1,82 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.util.Arrays;
+
+import javax.swing.border.MatteBorder;
+
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ImmutablesRepresenter extends Representer {
+
+    public ImmutablesRepresenter() {
+        super();
+        this.representers.put(java.awt.Color.class, new RepresentColor());
+        this.representers.put(Insets.class, new RepresentInsets());
+        this.representers.put(MatteBorder.class, new RepresentMatteBorder());
+        this.representers.put(Rectangle.class, new RepresentRectangle());
+    }
+
+    class RepresentInsets implements Represent {
+
+        public Node representData(Object data) {
+            Insets insets = (Insets) data;
+            return representSequence(
+                    getTag(data.getClass(), new Tag(data.getClass())),
+                    Arrays.asList(new Object[] { insets.top, insets.left, insets.bottom,
+                            insets.right }), true);
+        }
+
+    }
+
+    class RepresentRectangle implements Represent {
+
+        public Node representData(Object data) {
+            Rectangle rect = (Rectangle) data;
+            return representSequence(getTag(data.getClass(), new Tag(data.getClass())),
+                    Arrays.asList(new Object[] { rect.x, rect.y, rect.width, rect.height }), true);
+        }
+
+    }
+
+    class RepresentMatteBorder implements Represent {
+
+        public Node representData(Object data) {
+            MatteBorder mb = (MatteBorder) data;
+            return representSequence(getTag(data.getClass(), new Tag(data.getClass())),
+                    Arrays.asList(new Object[] { mb.getBorderInsets(), mb.getMatteColor() }), true);
+        }
+
+    }
+
+    class RepresentColor implements Represent {
+
+        public Node representData(Object data) {
+            java.awt.Color color = (java.awt.Color) data;
+            return representSequence(
+                    getTag(data.getClass(), new Tag(data.getClass())),
+                    Arrays.asList(new Integer[] { color.getRed(), color.getGreen(),
+                            color.getBlue(), color.getAlpha() }), true);
+        }
+
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/MoreImmutablesTest.java b/src/test/java/org/yaml/snakeyaml/immutable/MoreImmutablesTest.java
new file mode 100644
index 0000000..b96b37a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/MoreImmutablesTest.java
@@ -0,0 +1,78 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+import java.awt.Color;
+import java.awt.Insets;
+import java.awt.Rectangle;
+
+import javax.swing.BorderFactory;
+import javax.swing.border.MatteBorder;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class MoreImmutablesTest extends TestCase {
+
+    public void testInsets() {
+        Yaml yaml = new Yaml(new ImmutablesRepresenter());
+        Insets insets = new Insets(10, 20, 30, 40);
+        String dump = yaml.dump(insets);
+        assertEquals("!!java.awt.Insets [10, 20, 30, 40]\n", dump);
+        Object loaded = yaml.load(dump);
+        assertEquals(insets, loaded);
+    }
+
+    public void testAwtColor() {
+        Yaml yaml = new Yaml(new ImmutablesRepresenter());
+        Color color = new Color(10, 20, 30, 40);
+        String dump = yaml.dump(color);
+        assertEquals("!!java.awt.Color [10, 20, 30, 40]\n", dump);
+        Object loaded = yaml.load(dump);
+        assertEquals(color, loaded);
+    }
+
+    public void testRectangle() {
+        Yaml yaml = new Yaml(new ImmutablesRepresenter());
+        Rectangle rect = new Rectangle(10, 20, 30, 40);
+        String dump = yaml.dump(rect);
+        assertEquals("!!java.awt.Rectangle [10, 20, 30, 40]\n", dump);
+        Object loaded = yaml.load(dump);
+        assertEquals(rect, loaded);
+    }
+
+    // matteborder - only with color - no icon
+    public void testMatteBorder() {
+        DumperOptions options = new DumperOptions();
+        options.setWidth(400);
+        Yaml yaml = new Yaml(new ImmutablesRepresenter(), options);
+        Insets insets = new Insets(10, 20, 30, 40);
+        Color color = new Color(100, 150, 200);
+        MatteBorder border = BorderFactory.createMatteBorder(insets.top, insets.left,
+                insets.bottom, insets.right, color);
+        String dump = yaml.dump(border);
+        assertEquals(
+                "!!javax.swing.border.MatteBorder [!!java.awt.Insets [10, 20, 30, 40], !!java.awt.Color [100, 150, 200, 255]]\n",
+                dump);
+        Object loaded = yaml.load(dump);
+        assertTrue(loaded instanceof MatteBorder);
+        MatteBorder loadedBorder = (MatteBorder) loaded;
+        assertEquals(insets, loadedBorder.getBorderInsets());
+        assertEquals(color, loadedBorder.getMatteColor());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point.java b/src/test/java/org/yaml/snakeyaml/immutable/Point.java
new file mode 100644
index 0000000..98312da
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Point {
+    private final double x;
+    private final double y;
+
+    public double getX() {
+        return x;
+    }
+
+    public double getY() {
+        return y;
+    }
+
+    public Point(Double x, Double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    @Override
+    public String toString() {
+        return "<Point x=" + String.valueOf(x) + " y=" + String.valueOf(y) + ">";
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Point) {
+            return toString().equals(obj.toString());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point2.java b/src/test/java/org/yaml/snakeyaml/immutable/Point2.java
new file mode 100644
index 0000000..89d1c85
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point2.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+/**
+ * Two public constructor with 2 argument are present
+ */
+public class Point2 {
+    private final Integer x;
+    private final Integer y;
+
+    public Integer getX() {
+        return x;
+    }
+
+    public Integer getY() {
+        return y;
+    }
+
+    public Point2(Double x, Double y) {
+        this.x = x.intValue();
+        this.y = y.intValue();
+    }
+
+    public Point2(Integer x, Integer y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    @Override
+    public String toString() {
+        return "<Point2 x=" + String.valueOf(x) + " y=" + String.valueOf(y) + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java b/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java
new file mode 100644
index 0000000..333de04
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Point3d.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Point3d {
+    private final double z;
+    private final Point point;
+
+    public Point3d(Point point, Double z) {
+        this.point = point;
+        this.z = z;
+    }
+
+    public double getZ() {
+        return z;
+    }
+
+    public Point getPoint() {
+        return point;
+    }
+
+    @Override
+    public String toString() {
+        return "<Point3d point=" + point.toString() + " z=" + String.valueOf(z) + ">";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/Shape.java b/src/test/java/org/yaml/snakeyaml/immutable/Shape.java
new file mode 100644
index 0000000..b1f0b41
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/Shape.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class Shape {
+    private Color color;
+    private Point point;
+    private Point3d point3d;
+    private Integer id;
+
+    public Point3d getPoint3d() {
+        return point3d;
+    }
+
+    public void setPoint3d(Point3d point3d) {
+        this.point3d = point3d;
+    }
+
+    public void setColor(Color color) {
+        this.color = color;
+    }
+
+    public void setPoint(Point point) {
+        this.point = point;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Color getColor() {
+        return color;
+    }
+
+    public Point getPoint() {
+        return point;
+    }
+
+    public Integer getId() {
+        return id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java b/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java
new file mode 100644
index 0000000..4d8f454
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/ShapeImmutableTest.java
@@ -0,0 +1,151 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class ShapeImmutableTest extends TestCase {
+
+    public void testColor() {
+        Yaml yaml = new Yaml();
+        Color loaded = (Color) yaml.load("!!org.yaml.snakeyaml.immutable.Color BLACK");
+        assertEquals("BLACK", loaded.getName());
+    }
+
+    public void testCode() {
+        Yaml yaml = new Yaml();
+        Code loaded = (Code) yaml.load("!!org.yaml.snakeyaml.immutable.Code 123");
+        assertEquals(new Integer(123), loaded.getCode());
+    }
+
+    public void testSuperColor() {
+        Yaml yaml = new Yaml();
+        SuperColor superColor = (SuperColor) yaml
+                .load("!!org.yaml.snakeyaml.immutable.SuperColor [!!org.yaml.snakeyaml.immutable.Color BLACK]");
+        assertEquals("BLACK", superColor.getColor().getName());
+    }
+
+    public void testSuperColorFail() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.SuperColor BLACK");
+            fail("SuperColor requires Color and not a String.");
+        } catch (Exception e) {
+            assertTrue(e
+                    .getMessage()
+                    .startsWith(
+                            "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.SuperColor; exception=Unsupported class: class org.yaml.snakeyaml.immutable.Color"));
+        }
+    }
+
+    public void testCode2() {
+        Yaml yaml = new Yaml();
+        Code2 code2 = (Code2) yaml.load("!!org.yaml.snakeyaml.immutable.Code2 555");
+        assertEquals(new Integer(555), code2.getCode());
+    }
+
+    public void testCode3() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Code3 777");
+            fail("There must be 1 constructor with 1 argument for scalar.");
+        } catch (Exception e) {
+            assertTrue(e
+                    .getMessage()
+                    .startsWith(
+                            "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Code3; exception=No single argument constructor found for class org.yaml.snakeyaml.immutable.Code3"));
+        }
+    }
+
+    public void testCode4() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Code4 777");
+            fail("Constructor with String is required.");
+        } catch (Exception e) {
+            assertEquals(
+                    "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Code4; exception=Can't construct a java object for scalar tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Code4; No String constructor found. Exception=org.yaml.snakeyaml.immutable.Code4.<init>(java.lang.String)\n"
+                            + " in 'string', line 1, column 1:\n"
+                            + "    !!org.yaml.snakeyaml.immutable.C ... \n" + "    ^\n",
+                    e.getMessage());
+        }
+    }
+
+    public void testPoint() {
+        Yaml yaml = new Yaml();
+        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14]");
+        assertEquals(1.17, loaded.getX());
+        assertEquals(3.14, loaded.getY());
+    }
+
+    public void testPointBlock() {
+        Yaml yaml = new Yaml();
+        Point loaded = (Point) yaml.load("!!org.yaml.snakeyaml.immutable.Point\n- 1.17\n- 3.14");
+        assertEquals(1.17, loaded.getX());
+        assertEquals(3.14, loaded.getY());
+    }
+
+    public void testPointOnlyOneArgument() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("!!org.yaml.snakeyaml.immutable.Point\n- 1.17");
+            fail("Two arguments required.");
+        } catch (Exception e) {
+            assertEquals(
+                    "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.Point; exception=No suitable constructor with 1 arguments found for class org.yaml.snakeyaml.immutable.Point\n"
+                            + " in 'string', line 1, column 1:\n"
+                            + "    !!org.yaml.snakeyaml.immutable.Point\n" + "    ^\n",
+                    e.getMessage());
+        }
+    }
+
+    public void testPoint2() {
+        Yaml yaml = new Yaml();
+        Point2 loaded = (Point2) yaml.load("!!org.yaml.snakeyaml.immutable.Point2\n- 1\n- 3");
+        assertEquals(new Integer(1), loaded.getX());
+        assertEquals(new Integer(3), loaded.getY());
+    }
+
+    public void testPoint3d() {
+        Yaml yaml = new Yaml();
+        Point3d loaded = (Point3d) yaml
+                .load("!!org.yaml.snakeyaml.immutable.Point3d [!!org.yaml.snakeyaml.immutable.Point [1.17, 3.14], 345.1]");
+        assertEquals(345.1, loaded.getZ());
+    }
+
+    public void testShape() {
+        Yaml yaml = new Yaml();
+        String source = Util.getLocalResource("immutable/shape1.yaml");
+        Shape loaded = (Shape) yaml.load(source);
+        assertEquals(new Integer(123), loaded.getId());
+    }
+
+    public void testShapeNoTags() {
+        String source = Util.getLocalResource("immutable/shapeNoTags.yaml");
+        Yaml beanLoader = new Yaml();
+        Shape loaded = beanLoader.loadAs(source, Shape.class);
+        assertEquals(new Integer(123), loaded.getId());
+        assertEquals("BLACK", loaded.getColor().getName());
+        assertEquals(1.17, loaded.getPoint().getX());
+        assertEquals(3.14, loaded.getPoint().getY());
+        assertEquals(345.1, loaded.getPoint3d().getZ());
+        assertEquals(1.96, loaded.getPoint3d().getPoint().getX());
+        assertEquals(1.78, loaded.getPoint3d().getPoint().getY());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java b/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java
new file mode 100644
index 0000000..0a24962
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/SuperColor.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable;
+
+public class SuperColor {
+    private final Color color;
+
+    public SuperColor(Color name) {
+        this.color = name;
+    }
+
+    public Color getName() {
+        return color;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof SuperColor) {
+            SuperColor color = (SuperColor) obj;
+            return color.equals(color.color);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return color.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "SuperColor color=" + color;
+    }
+
+    public Color getColor() {
+        return color;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/primitives/BunchOfPrimitives.java b/src/test/java/org/yaml/snakeyaml/immutable/primitives/BunchOfPrimitives.java
new file mode 100644
index 0000000..19008b4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/primitives/BunchOfPrimitives.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable.primitives;
+
+public class BunchOfPrimitives {
+    private int primitiveInt;
+    private double primitiveDouble;
+    public boolean primitiveBoolean;
+
+    public BunchOfPrimitives(int primitiveInt, double primitiveDouble, boolean primitiveBoolean) {
+        this.primitiveInt = primitiveInt;
+        this.primitiveDouble = primitiveDouble;
+        this.primitiveBoolean = primitiveBoolean;
+    }
+
+    /**
+     * The number of parameters is the same but the type is different
+     */
+    public BunchOfPrimitives(int i1, int i2, int i3) {
+        this.primitiveInt = i1;
+    }
+
+    public BunchOfPrimitives(long i1, double i2, boolean i3) {
+        this((int) i1, i2, i3);
+    }
+
+    public int getPrimitiveInt() {
+        return primitiveInt;
+    }
+
+    public double getPrimitiveDouble() {
+        return primitiveDouble;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof BunchOfPrimitives) {
+            BunchOfPrimitives bunch = (BunchOfPrimitives) obj;
+            return primitiveInt == bunch.primitiveInt;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return primitiveInt;
+    }
+
+    @Override
+    public String toString() {
+        return "BunchOfPrimitives " + primitiveInt;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesRepresenter.java b/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesRepresenter.java
new file mode 100644
index 0000000..36745dc
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesRepresenter.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable.primitives;
+
+import java.util.Arrays;
+
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ImmutablePrimitivesRepresenter extends Representer {
+    public ImmutablePrimitivesRepresenter() {
+        super();
+        this.representers.put(BunchOfPrimitives.class, new RepresentPrimitives());
+    }
+
+    class RepresentPrimitives implements Represent {
+        public Node representData(Object data) {
+            BunchOfPrimitives bunch = (BunchOfPrimitives) data;
+            return representSequence(
+                    getTag(data.getClass(), new Tag(data.getClass())),
+                    Arrays.asList(new Object[] { bunch.getPrimitiveInt(),
+                            bunch.getPrimitiveDouble(), bunch.primitiveBoolean }), true);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesTest.java b/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesTest.java
new file mode 100644
index 0000000..801715b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/immutable/primitives/ImmutablePrimitivesTest.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.immutable.primitives;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class ImmutablePrimitivesTest extends TestCase {
+
+    public void testPrimitives() {
+        Yaml yaml = new Yaml(new ImmutablePrimitivesRepresenter());
+        BunchOfPrimitives bunch = new BunchOfPrimitives(10, 40.0, true);
+        String dump = yaml.dump(bunch);
+        assertEquals("!!" + bunch.getClass().getCanonicalName() + " [10, 40.0, true]\n", dump);
+        Object loaded = yaml.load(dump);
+        assertEquals(loaded.toString(), bunch, loaded);
+    }
+
+    public void testPrimitivesLong() {
+        Yaml yaml = new Yaml();
+        String dump = "!!org.yaml.snakeyaml.immutable.primitives.BunchOfPrimitives [10000000000, 40.0, true]";
+        BunchOfPrimitives bunch = (BunchOfPrimitives) yaml.load(dump);
+        assertEquals("Must be truncated.", new Long(10000000000L).intValue(),
+                bunch.getPrimitiveInt());
+    }
+
+    public void testPrimitivesException() {
+        Yaml yaml = new Yaml();
+        String dump = "!!org.yaml.snakeyaml.immutable.primitives.BunchOfPrimitives [10, 40, true]";
+        try {
+            yaml.load(dump);
+            fail();
+        } catch (YAMLException e) {
+            assertTrue(e
+                    .getMessage()
+                    .startsWith(
+                            "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.immutable.primitives.BunchOfPrimitives; exception=No suitable constructor with 3 arguments found for class org.yaml.snakeyaml.immutable.primitives.BunchOfPrimitives"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/introspector/MethodPropertyTest.java b/src/test/java/org/yaml/snakeyaml/introspector/MethodPropertyTest.java
new file mode 100644
index 0000000..46c6e0d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/introspector/MethodPropertyTest.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.introspector;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.constructor.TestBean1;
+
+public class MethodPropertyTest extends TestCase {
+
+    public void testToString() throws IntrospectionException {
+        for (PropertyDescriptor property : Introspector.getBeanInfo(TestBean1.class)
+                .getPropertyDescriptors()) {
+            if (property.getName().equals("text")) {
+                MethodProperty prop = new MethodProperty(property);
+                assertEquals("text of class java.lang.String", prop.toString());
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue10/BasicDumpTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue10/BasicDumpTest.java
new file mode 100644
index 0000000..a16c4e2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue10/BasicDumpTest.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue10;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class BasicDumpTest extends TestCase {
+
+    public void testTag() {
+        DataSource base = new DataSource();
+        JDBCDataSource baseJDBC = new JDBCDataSource();
+        baseJDBC.setParent(base);
+
+        ArrayList<DataSource> dataSources = new ArrayList<DataSource>();
+        // trying expected order first
+        dataSources.add(base);
+        dataSources.add(baseJDBC);
+
+        DataSources ds = new DataSources();
+        ds.setDataSources(dataSources);
+
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(ds);
+
+        String etalon = Util.getLocalResource("javabeans/issue10-1.yaml");
+        assertEquals(etalon.trim(), output.trim());
+        Object obj = yaml.load(output);
+        DataSources dsOut = (DataSources) obj;
+        Iterator<DataSource> iter = dsOut.getDataSources().iterator();
+        assertFalse("Must be DataSource.", iter.next() instanceof JDBCDataSource);
+        assertTrue(iter.next() instanceof JDBCDataSource);
+    }
+
+    public void testTag2() {
+        DataSource base = new DataSource();
+        JDBCDataSource baseJDBC = new JDBCDataSource();
+        baseJDBC.setParent(base);
+
+        ArrayList<DataSource> dataSources = new ArrayList<DataSource>();
+        dataSources.add(base);
+        dataSources.add(baseJDBC);
+
+        DataSources ds = new DataSources();
+        ds.setDataSources(dataSources);
+
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(ds);
+
+        String etalon = Util.getLocalResource("javabeans/issue10-2.yaml");
+        assertEquals(etalon.trim(), output.trim());
+    }
+
+    /**
+     * different order does not require the global tag
+     */
+    public void testTag3() {
+        DataSource base = new DataSource();
+        JDBCDataSource baseJDBC = new JDBCDataSource();
+        baseJDBC.setParent(base);
+
+        ArrayList<DataSource> dataSources = new ArrayList<DataSource>();
+        dataSources.add(baseJDBC);
+        dataSources.add(base);
+
+        DataSources ds = new DataSources();
+        ds.setDataSources(dataSources);
+
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(ds);
+
+        String etalon = Util.getLocalResource("javabeans/issue10-3.yaml");
+        assertEquals(etalon.trim(), output.trim());
+        // load
+        Yaml beanLoader = new Yaml();
+        DataSources bean = beanLoader.loadAs(output, DataSources.class);
+        Iterator<DataSource> iter = bean.getDataSources().iterator();
+        assertTrue(iter.next() instanceof JDBCDataSource);
+        assertFalse("Must be DataSource.", iter.next() instanceof JDBCDataSource);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSource.java b/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSource.java
new file mode 100644
index 0000000..3063b9e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSource.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue10;
+
+public class DataSource {
+    String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSources.java b/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSources.java
new file mode 100644
index 0000000..995e2ca
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue10/DataSources.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue10;
+
+import java.util.List;
+
+public class DataSources {
+    List<DataSource> dataSources;
+
+    public List<DataSource> getDataSources() {
+        return dataSources;
+    }
+
+    public void setDataSources(List<DataSource> dataSources) {
+        this.dataSources = dataSources;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue10/JDBCDataSource.java b/src/test/java/org/yaml/snakeyaml/issues/issue10/JDBCDataSource.java
new file mode 100644
index 0000000..e80193e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue10/JDBCDataSource.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue10;
+
+public class JDBCDataSource extends DataSource {
+    String username;
+    String password;
+    String url;
+
+    DataSource parent;
+
+    public DataSource getParent() {
+        return parent;
+    }
+
+    public void setParent(DataSource parent) {
+        this.parent = parent;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue100/Data.java b/src/test/java/org/yaml/snakeyaml/issues/issue100/Data.java
new file mode 100644
index 0000000..1596802
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue100/Data.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue100;
+
+public class Data {
+    private String id;
+    private int age;
+
+    public Data() {
+        this.id = null;
+        this.age = 999;
+    }
+
+    public Data(String id, int age) {
+        this.id = id;
+        this.age = age;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    @Override
+    public String toString() {
+        return "Data [age=" + age + ", id=" + id + "]";
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Data) {
+            return toString().equals(obj.toString());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue100/DataBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue100/DataBean.java
new file mode 100644
index 0000000..cd31171
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue100/DataBean.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue100;
+
+public class DataBean {
+    private String id;
+    private Data data;
+    private DataMore more;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public Data getData() {
+        return data;
+    }
+
+    public void setData(Data data) {
+        this.data = data;
+    }
+
+    public DataMore getMore() {
+        return more;
+    }
+
+    public void setMore(DataMore more) {
+        this.more = more;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue100/DataMore.java b/src/test/java/org/yaml/snakeyaml/issues/issue100/DataMore.java
new file mode 100644
index 0000000..4d4dff3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue100/DataMore.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue100;
+
+public class DataMore extends Data {
+    private boolean complete;
+
+    public DataMore() {
+        setId("-1");
+        setAge(-1);
+    }
+
+    public DataMore(String id, int age, boolean complete) {
+        super(id, age);
+        this.complete = complete;
+    }
+
+    public boolean isComplete() {
+        return complete;
+    }
+
+    public void setComplete(boolean complete) {
+        this.complete = complete;
+    }
+
+    @Override
+    public String toString() {
+        return "DataMore [complete=" + complete + ", toString()=" + super.toString() + "]";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeJavaBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeJavaBeanTest.java
new file mode 100644
index 0000000..2154557
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeJavaBeanTest.java
@@ -0,0 +1,198 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue100;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class MergeJavaBeanTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testNoMerge() {
+        String input = "- &id001 !!org.yaml.snakeyaml.issues.issue100.Data {age: 11, id: id123}\n- *id001";
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        List<Data> list = (List<Data>) yaml.load(input);
+        for (Data data : list) {
+            // System.out.println(data);
+            assertEquals("id123", data.getId());
+            assertEquals(11, data.getAge());
+        }
+    }
+
+    public void testMergeWithTags() {
+        String input = Util.getLocalResource("issues/issue100-1.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+
+        List<?> list = (List<?>) yaml.load(input);
+        // First object: Data ( 11, "id123" )
+        assertEquals(list.get(0).getClass(), Data.class);
+        assertEquals(11, ((Data) list.get(0)).getAge());
+        assertEquals("id123", ((Data) list.get(0)).getId());
+
+        // Second object: Data ( 13, "id456" )
+        assertEquals(list.get(1).getClass(), Data.class);
+        assertEquals(13, ((Data) list.get(1)).getAge());
+        assertEquals("id456", ((Data) list.get(1)).getId());
+
+        // Third object: Data ( 11, "id789" )
+        assertEquals(list.get(2).getClass(), Data.class);
+        assertEquals(11, ((Data) list.get(2)).getAge());
+        assertEquals("id789", ((Data) list.get(2)).getId());
+
+        // 4th object: DataMore ( 30, "id123" )
+        assertEquals(list.get(3).getClass(), DataMore.class);
+        assertEquals(30, ((DataMore) list.get(3)).getAge());
+        assertEquals("id123", ((DataMore) list.get(3)).getId());
+        assertTrue(((DataMore) list.get(3)).isComplete());
+
+        // 5th object: Map ( age: 100 )
+        assertTrue(list.get(4) instanceof Map);
+        assertEquals(1, ((Map<?, ?>) list.get(4)).size());
+        assertTrue(((Map<?, ?>) list.get(4)).containsKey("age"));
+        assertEquals(100, ((Map<?, ?>) list.get(4)).get("age"));
+
+        // 6th object: DataMore ( 100, "id789" )
+        assertEquals(list.get(5).getClass(), DataMore.class);
+        assertEquals(100, ((DataMore) list.get(5)).getAge());
+        assertEquals("id789", ((DataMore) list.get(5)).getId());
+        assertFalse(((DataMore) list.get(5)).isComplete());
+        // All instances except the last Map must be different Data instances
+        Set<Data> dataSet = new HashSet<Data>();
+        for (Object data : list) {
+            if (data instanceof Data) {
+                dataSet.add((Data) data);
+            }
+        }
+        assertEquals("Must be all but one Data instances.", list.size() - 1, dataSet.size());
+    }
+
+    /**
+     * Since to explicit tag is provided JavaBean properties are used to create
+     * a map
+     */
+    @SuppressWarnings("unchecked")
+    public void testMergeBeanToMap() {
+        String input = "- &id001 !!org.yaml.snakeyaml.issues.issue100.Data {age: 11, id: id123}\n- << : *id001";
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        List<Object> list = (List<Object>) yaml.load(input);
+        // First object: Data ( 11, "id123" )
+        Data first = (Data) list.get(0);
+        assertEquals(11, first.getAge());
+        assertEquals("id123", first.getId());
+        // Second object is map
+        Map<?, ?> second = (Map<?, ?>) list.get(1);
+        assertEquals(11, second.get("age"));
+        assertEquals("id123", second.get("id"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testMergeAndDeviate() {
+        String input = "- &id001 !!org.yaml.snakeyaml.issues.issue100.Data {age: 11, id: id123}\n- <<: *id001\n  id: id456";
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        List<Object> list = (List<Object>) yaml.load(input);
+        // First object: Data ( 11, "id123" )
+        Data first = (Data) list.get(0);
+        assertEquals(11, first.getAge());
+        assertEquals("id123", first.getId());
+        // Second object is map with a diffrent id
+        Map<?, ?> second = (Map<?, ?>) list.get(1);
+        assertEquals(11, second.get("age"));
+        assertEquals("id456", second.get("id"));
+    }
+
+    /**
+     * <pre>
+     * Test that the merge-override works correctly in the case of custom classes / data types.
+     * Two base objects are specified:
+     *    id001 refers to Data ( 11, "id123" )
+     *    id002 refers to Data ( 37, null )  with a literal null value
+     * The third object is specified as having the properties of id001, with the properties of
+     * id002 overriding where they conflict.
+     *    Data ( 37, "id123" )
+     * </pre>
+     */
+    @SuppressWarnings("unchecked")
+    public void testMergeAndDeviateOverride() {
+        String input = "- &id001 !!org.yaml.snakeyaml.issues.issue100.Data {age: 11, id: id123}\n- &id002 !!org.yaml.snakeyaml.issues.issue100.Data {age: 37}\n- <<: [ *id002, *id001 ]";
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        List<Data> list = (List<Data>) yaml.load(input);
+
+        // First object: Data ( 11, "id123" )
+        assertEquals(11, list.get(0).getAge());
+        assertEquals("id123", list.get(0).getId());
+
+        // Second object: Data ( 37, null )
+        assertEquals(37, list.get(1).getAge());
+        assertEquals(null, list.get(1).getId());
+
+        // Third object: map
+        Map<?, ?> third = (Map<?, ?>) list.get(2);
+        assertEquals(37, third.get("age"));
+        assertEquals("id123", third.get("id"));
+    }
+
+    /**
+     * When the merged JavaBean is itself a JavaBean property then explicit tag
+     * is not required
+     */
+    public void testMergeBeanProperty() {
+        String input = Util.getLocalResource("issues/issue100-3.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml(new Constructor(DataBean.class));
+        DataBean bean = (DataBean) yaml.load(input);
+        assertEquals("id001", bean.getId());
+        assertEquals("id002", bean.getData().getId());
+        assertEquals(17, bean.getData().getAge());
+        assertEquals(17, bean.getMore().getAge());
+        assertEquals("more003", bean.getMore().getId());
+        assertTrue(bean.getMore().isComplete());
+    }
+
+    /**
+     * Merge map to JavaBean
+     */
+    @SuppressWarnings("unchecked")
+    public void testMergeMapToJavaBean() {
+        String input = "- &id001 { age: 11, id: id123 }\n- !!org.yaml.snakeyaml.issues.issue100.Data\n  <<: *id001\n  id: id456";
+        // System.out.println(input);
+        Yaml yaml = new Yaml(new Constructor());
+        List<Object> objects = (List<Object>) yaml.load(input);
+        assertEquals(2, objects.size());
+        // Check first type
+        Object first = objects.get(0);
+        Map<Object, Object> firstMap = (Map<Object, Object>) first;
+        // Check first contents
+        assertEquals(11, firstMap.get("age"));
+        assertEquals("id123", firstMap.get("id"));
+        // Check second contents
+        Data secondData = (Data) objects.get(1);
+        assertEquals(11, secondData.getAge());
+        assertEquals("id456", secondData.getId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeMapsTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeMapsTest.java
new file mode 100644
index 0000000..c89bc3b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue100/MergeMapsTest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue100;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class MergeMapsTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testExplicitMergeTag() {
+        String input = Util.getLocalResource("issues/issue100-2.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        Map<String, ?> list = (Map<String, ?>) yaml.load(input);
+        List<Map<?, ?>> result = (List<Map<?, ?>>) list.get("result");
+        Map<?, ?> first = result.iterator().next();
+        for (Map<?, ?> data : result) {
+            // System.out.println(data);
+            assertEquals("Size must coinside.", first.size(), data.size());
+            assertEquals(first, data);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue102/BigDataLoadTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue102/BigDataLoadTest.java
new file mode 100644
index 0000000..7e4beca
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue102/BigDataLoadTest.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue102;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class BigDataLoadTest extends TestCase {
+    private static final int SIZE = 5000;
+
+    public void testBigStringData() {
+        Yaml yaml = new Yaml();
+        List<?> loaded = (List<?>) yaml.load(getLongYamlDocument(SIZE));
+        assertEquals(SIZE, loaded.size());
+    }
+
+    public void testBigStreamData() {
+        Yaml yaml = new Yaml();
+        StringReader buffer = new StringReader(getLongYamlDocument(SIZE));
+        List<?> loaded = (List<?>) yaml.load(buffer);
+        assertEquals(SIZE, loaded.size());
+    }
+
+    private String getLongYamlDocument(int size) {
+        List<DataBean> beans = new ArrayList<DataBean>();
+        Yaml yaml = new Yaml();
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < size; i++) {
+            List<String> list = new ArrayList<String>();
+            for (int j = 0; j < 10; j++) {
+                list.add(String.valueOf(i + j));
+            }
+            Map<String, Integer> map = new HashMap<String, Integer>();
+            for (int j = 0; j < 10; j++) {
+                map.put(String.valueOf(j), i + j);
+            }
+            DataBean bean = new DataBean();
+            bean.setId("id" + i);
+            bean.setList(list);
+            bean.setMap(map);
+            beans.add(bean);
+            String ooo = yaml.dumpAsMap(bean);
+            String[] lines = ooo.split("\n");
+            builder.append("-\n");
+            for (int j = 0; j < lines.length; j++) {
+                builder.append("  ");
+                builder.append(lines[j]);
+                builder.append("\n");
+            }
+        }
+        String data = builder.toString();
+        System.out.println("Long data size: " + data.length() / 1024 + " kBytes.");
+        return data;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue102/DataBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue102/DataBean.java
new file mode 100644
index 0000000..9b1eb38
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue102/DataBean.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue102;
+
+import java.util.List;
+import java.util.Map;
+
+public class DataBean {
+    private String id;
+    private List<String> list;
+    private Map<String, Integer> map;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public List<String> getList() {
+        return list;
+    }
+
+    public void setList(List<String> list) {
+        this.list = list;
+    }
+
+    public Map<String, Integer> getMap() {
+        return map;
+    }
+
+    public void setMap(Map<String, Integer> map) {
+        this.map = map;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue103/FakeMap.java b/src/test/java/org/yaml/snakeyaml/issues/issue103/FakeMap.java
new file mode 100644
index 0000000..bdced75
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue103/FakeMap.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue103;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Implements map interface, but behaves like collection. It just collects
+ * whatever you put(...) in here. Needed to track duplications in merge
+ * procedure.
+ * 
+ * issue100 & issue103
+ */
+public class FakeMap<K, V> implements Map<K, V> {
+
+    class FakeEntry implements java.util.Map.Entry<K, V> {
+
+        private final K key;
+        private V val;
+
+        public FakeEntry(K key, V val) {
+            this.key = key;
+            this.val = val;
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return val;
+        }
+
+        public V setValue(V newV) {
+            V old = val;
+            val = newV;
+            return old;
+        }
+
+    }
+
+    ArrayList<java.util.Map.Entry<K, V>> entries = new ArrayList<Map.Entry<K, V>>();
+
+    public void clear() {
+        entries.clear();
+    }
+
+    public boolean containsKey(Object arg0) {
+        for (java.util.Map.Entry<K, V> entry : entries) {
+            if (entry.getKey().equals(arg0))
+                return true;
+        }
+        return false;
+    }
+
+    public boolean containsValue(Object arg0) {
+        for (java.util.Map.Entry<K, V> entry : entries) {
+            if (entry.getValue().equals(arg0))
+                return true;
+        }
+        return false;
+    }
+
+    public Set<java.util.Map.Entry<K, V>> entrySet() {
+        throw new UnsupportedOperationException();
+    }
+
+    public V get(Object arg0) {
+        for (java.util.Map.Entry<K, V> entry : entries) {
+            if (entry.getKey().equals(arg0))
+                return entry.getValue();
+        }
+        return null;
+    }
+
+    public boolean isEmpty() {
+        return values().isEmpty();
+    }
+
+    public Set<K> keySet() {
+        throw new UnsupportedOperationException();
+    }
+
+    public V put(K key, V val) {
+        entries.add(new FakeEntry(key, val));
+        return null;
+    }
+
+    public void putAll(Map<? extends K, ? extends V> arg0) {
+        throw new UnsupportedOperationException();
+    }
+
+    public V remove(Object arg0) {
+        for (Iterator<java.util.Map.Entry<K, V>> iter = entries.iterator(); iter.hasNext();) {
+            java.util.Map.Entry<K, V> entry = iter.next();
+            if (entry.getKey().equals(arg0)) {
+                iter.remove();
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    public int size() {
+        return entries.size();
+    }
+
+    public Collection<V> values() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue103/MergingTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue103/MergingTest.java
new file mode 100644
index 0000000..b825e28
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue103/MergingTest.java
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue103;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class MergingTest extends TestCase {
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void testMergeWithDefaultMap() {
+        String input = Util.getLocalResource("issues/issue103.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+
+        check((Map) yaml.load(input));
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testMergeWithFakeMap() {
+        String input = Util.getLocalResource("issues/issue103.yaml");
+        // System.out.println(input);
+        Constructor c = new Constructor() {
+            protected java.util.Map<Object, Object> createDefaultMap() {
+                return new FakeMap<Object, Object>();
+            }
+        };
+
+        Yaml yaml = new Yaml(c);
+
+        check((FakeMap) yaml.load(input));
+    }
+
+    private void check(Map<String, List<Map<Object, Object>>> map) {
+
+        assertEquals(2, map.size());
+        assertTrue(map.containsKey("input"));
+        assertTrue(map.containsKey("result"));
+
+        // input: ...
+        List<Map<Object, Object>> inputList = map.get("input");
+        assertEquals(4, inputList.size());
+
+        Map<Object, Object> center = inputList.get(0);
+        assertEquals(2, center.size());
+        assertEquals(Integer.valueOf(1), center.get("x"));
+        assertEquals(Integer.valueOf(2), center.get("y"));
+
+        Map<Object, Object> left = inputList.get(1);
+        assertEquals(2, left.size());
+        assertEquals(Integer.valueOf(0), left.get("x"));
+        assertEquals(Integer.valueOf(2), left.get("y"));
+
+        Map<Object, Object> big = inputList.get(2);
+        assertEquals(1, big.size());
+        assertEquals(Integer.valueOf(10), big.get("r"));
+
+        Map<Object, Object> small = inputList.get(3);
+        assertEquals(1, small.size());
+        assertEquals(Integer.valueOf(1), small.get("r"));
+
+        // result : ...
+        List<Map<Object, Object>> resultList = map.get("result");
+        assertEquals(5, resultList.size());
+
+        Map<Object, Object> explicitKeys = resultList.get(0);
+        assertEquals(4, explicitKeys.size());
+        assertEquals(Integer.valueOf(1), explicitKeys.get("x"));
+        assertEquals(Integer.valueOf(2), explicitKeys.get("y"));
+        assertEquals(Integer.valueOf(10), explicitKeys.get("r"));
+        assertEquals("center/big", explicitKeys.get("label"));
+
+        Map<?, ?> merge_center = resultList.get(1);
+        assertEquals(4, merge_center.size());
+        assertEquals(Integer.valueOf(1), merge_center.get("x"));
+        assertEquals(Integer.valueOf(2), merge_center.get("y"));
+        assertEquals(Integer.valueOf(10), merge_center.get("r"));
+        assertEquals("center/big", merge_center.get("label"));
+
+        Map<?, ?> merge_left_override = resultList.get(2);
+        assertEquals(4, merge_left_override.size());
+        assertEquals(Integer.valueOf(0), merge_left_override.get("x"));
+        assertEquals(Integer.valueOf(5), merge_left_override.get("y"));
+        assertEquals(Integer.valueOf(10), merge_left_override.get("r"));
+        assertEquals("center/big", merge_left_override.get("label"));
+
+        Map<?, ?> merge_center_big = resultList.get(3);
+        assertEquals(4, merge_center_big.size());
+        assertEquals(Integer.valueOf(1), merge_center_big.get("x"));
+        assertEquals(Integer.valueOf(2), merge_center_big.get("y"));
+        assertEquals(Integer.valueOf(10), merge_center_big.get("r"));
+        assertEquals("center/big", merge_center_big.get("label"));
+
+        Map<?, ?> merge_big_left_small_override = resultList.get(4);
+        assertEquals(4, merge_big_left_small_override.size());
+        assertEquals(Integer.valueOf(1), merge_big_left_small_override.get("x"));
+        assertEquals(Integer.valueOf(2), merge_big_left_small_override.get("y"));
+        assertEquals(Integer.valueOf(10), merge_big_left_small_override.get("r"));
+        assertEquals("center/big", merge_big_left_small_override.get("label"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java
new file mode 100644
index 0000000..6550875
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue11/YamlMapTest.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue11;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class YamlMapTest extends TestCase {
+    public void testYaml() {
+        Yaml yaml = new Yaml(new ExtendedConstructor(), new ExtendedRepresenter());
+        String output = yaml.dump(new Custom(123));
+        // System.out.println(output);
+        Custom o = (Custom) yaml.load(output);
+        assertEquals("123", o.getStr());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testYamlMap() {
+        Map<String, Object> data = new TreeMap<String, Object>();
+        data.put("customTag", new Custom(123));
+
+        Yaml yaml = new Yaml(new ExtendedConstructor(), new ExtendedRepresenter());
+        String output = yaml.dump(data);
+        // System.out.println(output);
+        Object o = yaml.load(output);
+
+        assertTrue(o instanceof Map);
+        Map<String, Object> m = (Map<String, Object>) o;
+        assertTrue(m.get("customTag") instanceof Custom);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testYamlMapBean() {
+        Map<String, Object> data = new TreeMap<String, Object>();
+        data.put("knownClass", new Wrapper("test", new Custom(456)));
+
+        Yaml yaml = new Yaml(new ExtendedConstructor(), new ExtendedRepresenter());
+        String output = yaml.dump(data);
+        // System.out.println(output);
+        Object o = yaml.load(output);
+
+        assertTrue(o instanceof Map);
+        Map<String, Object> m = (Map<String, Object>) o;
+        assertEquals(Wrapper.class, m.get("knownClass").getClass());
+    }
+
+    public static class Wrapper {
+        private String a;
+        private Custom b;
+
+        public Wrapper(String s, Custom bb) {
+            a = s;
+            b = bb;
+        }
+
+        public Wrapper() {
+        }
+
+        public String getA() {
+            return a;
+        }
+
+        public void setA(String s) {
+            a = s;
+        }
+
+        public Custom getB() {
+            return b;
+        }
+
+        public void setB(Custom bb) {
+            b = bb;
+        }
+    }
+
+    public static class Custom {
+        final private String str;
+
+        public Custom(Integer i) {
+            str = i.toString();
+        }
+
+        public Custom(Custom c) {
+            str = c.str;
+        }
+
+        public String toString() {
+            return str;
+        }
+
+        public String getStr() {
+            return str;
+        }
+    }
+
+    public static class ExtendedRepresenter extends Representer {
+        public ExtendedRepresenter() {
+            this.representers.put(Custom.class, new RepresentCustom());
+        }
+
+        private class RepresentCustom implements Represent {
+            public Node representData(Object data) {
+                return representScalar(new Tag("!Custom"), ((Custom) data).toString());
+            }
+        }
+    }
+
+    public static class ExtendedConstructor extends Constructor {
+        public ExtendedConstructor() {
+            this.yamlConstructors.put(new Tag("!Custom"), new ConstructCustom());
+        }
+
+        private class ConstructCustom extends AbstractConstruct {
+            public Object construct(Node node) {
+                String str = (String) constructScalar((ScalarNode) node);
+                return new Custom(new Integer(str));
+            }
+
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue111/LongUriTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue111/LongUriTest.java
new file mode 100644
index 0000000..acf0091
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue111/LongUriTest.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue111;
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+
+public class LongUriTest extends TestCase {
+    /**
+     * Try loading a tag with a very long escaped URI section (over 256 bytes'
+     * worth).
+     */
+    public void testLongURIEscape() {
+        Yaml loader = new Yaml();
+        // Create a long escaped string by exponential growth...
+        String longEscURI = "%41"; // capital A...
+        for (int i = 0; i < 10; ++i) {
+            longEscURI = longEscURI + longEscURI;
+        }
+        assertEquals(1024 * 3, longEscURI.length());
+        String yaml = "!" + longEscURI + " www";
+
+        Node node = loader.compose(new StringReader(yaml));
+        ScalarNode scalar = (ScalarNode) node;
+        String etalon = "!";
+        for (int i = 0; i < 1024; i++) {
+            etalon += "A";
+        }
+        assertEquals(1025, etalon.length());
+        assertEquals(etalon, scalar.getTag().toString());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue112/MyClass.java b/src/test/java/org/yaml/snakeyaml/issues/issue112/MyClass.java
new file mode 100644
index 0000000..2f7c1b6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue112/MyClass.java
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue112;
+
+public class MyClass<T extends Object> {
+    T name;
+
+    public void setName(T name) {
+        this.name = name;
+    }
+
+    public T getName() {
+        return this.name;
+    }
+
+    @Override
+    public String toString() {
+        return name.toString();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue112/MyCompositeObject.java b/src/test/java/org/yaml/snakeyaml/issues/issue112/MyCompositeObject.java
new file mode 100644
index 0000000..471e678
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue112/MyCompositeObject.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue112;
+
+import java.util.Collection;
+
+public class MyCompositeObject {
+    Collection<MyClass<? extends Object>> things;
+
+    public Collection<MyClass<? extends Object>> getThings() {
+        return this.things;
+    }
+
+    public void setThings(Collection<MyClass<? extends Object>> things) {
+        this.things = things;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue112/ParameterisedTypeLoadingTestCase.java b/src/test/java/org/yaml/snakeyaml/issues/issue112/ParameterisedTypeLoadingTestCase.java
new file mode 100644
index 0000000..6b7f94a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue112/ParameterisedTypeLoadingTestCase.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue112;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class ParameterisedTypeLoadingTestCase {
+
+    @Test
+    public void testParameterisedTypeLoading() throws IOException {
+        Yaml yamlParser = new Yaml(new Constructor(MyCompositeObject.class));
+        MyCompositeObject obj = (MyCompositeObject) yamlParser.load(getInput());
+        check(obj);
+
+        // dump the object
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(obj);
+        assertEquals(Util.getLocalResource("issues/issue112-2.yaml"), output);
+    }
+
+    @Test
+    public void testJavaBeanLoader() throws IOException {
+        Yaml yamlParser = new Yaml();
+        MyCompositeObject obj = yamlParser.loadAs(getInput(), MyCompositeObject.class);
+        check(obj);
+    }
+
+    private void check(MyCompositeObject obj) {
+        Object[] values = { 1, "two", 3, "four", "!!!" };
+        assertNotNull(obj);
+        assertEquals(5, obj.getThings().size());
+        int i = 0;
+        for (MyClass<? extends Object> thing : obj.getThings()) {
+            assertEquals(MyClass.class, thing.getClass());
+            assertNotNull("The 'name' property must be set.", thing.getName());
+            assertEquals(values[i++], thing.getName());
+        }
+    }
+
+    private InputStream getInput() throws IOException {
+        return this.getClass().getClassLoader().getResource("issues/issue112-1.yaml").openStream();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue114/PreserveTypeTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue114/PreserveTypeTest.java
new file mode 100644
index 0000000..fd69f9a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue114/PreserveTypeTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue114;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class PreserveTypeTest extends TestCase {
+
+    public static class MyBean {
+
+        private int dummy;
+
+        public int getDummy() {
+            return dummy;
+        }
+
+        public void setDummy(int dummy) {
+            this.dummy = dummy;
+        }
+    }
+
+    public static class ReferencingBean {
+
+        private List<MyBean> myBeans = new LinkedList<PreserveTypeTest.MyBean>();
+
+        public List<MyBean> getMyBeans() {
+            return myBeans;
+        }
+
+        public void setMyBeans(List<MyBean> myBeans) {
+            this.myBeans = myBeans;
+        }
+    }
+
+    private Map<String, Object> createData(boolean collectionFirst) {
+        MyBean myBean = new MyBean();
+        ReferencingBean referencingBean = new ReferencingBean();
+        referencingBean.getMyBeans().add(myBean);
+
+        LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
+        if (collectionFirst) {
+            map.put("referencingBean", referencingBean);
+            map.put("myBean", myBean);
+        } else {
+            map.put("myBean", myBean);
+            map.put("referencingBean", referencingBean);
+        }
+        return map;
+    }
+
+    private void check(String doc) {
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, Object> loaded = (Map<String, Object>) yaml.load(doc);
+        Object myBean2 = loaded.get("myBean");
+        assertTrue(myBean2.getClass().toString(), myBean2 instanceof MyBean);
+    }
+
+    public void testPreserveType1() {
+        Yaml yaml = new Yaml();
+        String s = yaml.dump(createData(true));
+        check(s);
+    }
+
+    public void testPreserveType2() {
+        Yaml yaml = new Yaml();
+        String s = yaml.dump(createData(false));
+        check(s);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue115/IssueBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue115/IssueBean.java
new file mode 100644
index 0000000..572b94c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue115/IssueBean.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue115;
+
+public class IssueBean {
+    private ParameterizedBean<Integer, String> bean = new ParameterizedBean<Integer, String>();
+
+    public ParameterizedBean<Integer, String> getBean() {
+        return bean;
+    }
+
+    public void setBean(ParameterizedBean<Integer, String> bean) {
+        this.bean = bean;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedBean.java
new file mode 100644
index 0000000..ad3376a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue115;
+
+public class ParameterizedBean<K, V> {
+    private K k;
+    private V v;
+
+    public K getK() {
+        return k;
+    }
+
+    public void setK(K k) {
+        this.k = k;
+    }
+
+    public V getV() {
+        return v;
+    }
+
+    public void setV(V v) {
+        this.v = v;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedJavaBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedJavaBeanTest.java
new file mode 100644
index 0000000..ca3ccb4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedJavaBeanTest.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue115;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ParameterizedJavaBeanTest extends TestCase {
+
+    public void testAsStandalone() {
+        ParameterizedBean<Integer, String> bean = new ParameterizedBean<Integer, String>();
+        bean.setK(13);
+        bean.setV("ID47");
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(bean);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue115.ParameterizedBean {k: 13, v: ID47}\n",
+                result);
+        // load
+        @SuppressWarnings("unchecked")
+        ParameterizedBean<Integer, String> beanParsed = (ParameterizedBean<Integer, String>) yaml
+                .load(result);
+        assertEquals(new Integer(13), beanParsed.getK());
+        assertEquals("ID47", beanParsed.getV());
+    }
+
+    public void testAsJavaBeanProperty() {
+        Yaml yaml = new Yaml();
+        IssueBean issue = new IssueBean();
+        ParameterizedBean<Integer, String> bean = new ParameterizedBean<Integer, String>();
+        bean.setK(432);
+        bean.setV("Val432");
+        issue.setBean(bean);
+        String result = yaml.dump(issue);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue115.IssueBean\nbean: {k: 432, v: Val432}\n",
+                result);
+        // load
+        IssueBean issueParsed = (IssueBean) yaml.load(result);
+        assertEquals(new Integer(432), issueParsed.getBean().getK());
+        assertEquals("Val432", issueParsed.getBean().getV());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedTest.java
new file mode 100644
index 0000000..c6c69d3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue115/ParameterizedTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue115;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ParameterizedTest extends TestCase {
+
+    public void testAsStandalone() {
+        Parameterized<Integer> parm = new Parameterized<Integer>();
+        parm.t = 3;
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(parm);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue115.Parameterized {t: 3}\n", result);
+        @SuppressWarnings("unchecked")
+        // load
+        Parameterized<Integer> parmParsed = (Parameterized<Integer>) yaml.load(result);
+        assertEquals(new Integer(3), parmParsed.t);
+    }
+
+    public void testAsJavaBeanProperty() {
+        Yaml yaml = new Yaml();
+        Issue issue = new Issue();
+        Parameterized<Integer> parm = new Parameterized<Integer>();
+        parm.t = 555;
+        issue.parm = parm;
+        String result = yaml.dump(issue);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue115.Issue\nparm: {t: 555}\n", result);
+        // load
+        Issue issueParsed = (Issue) yaml.load(result);
+        assertEquals(new Integer(555), issueParsed.parm.t);
+    }
+}
+
+class Issue {
+    public Parameterized<Integer> parm = new Parameterized<Integer>();
+}
+
+class Parameterized<T> {
+    public T t;
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue116/HiddenSpecial.java b/src/test/java/org/yaml/snakeyaml/issues/issue116/HiddenSpecial.java
new file mode 100644
index 0000000..8fea9ff
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue116/HiddenSpecial.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue116;
+
+public class HiddenSpecial {
+    private int inaccessableField;
+
+    public HiddenSpecial(String something) {
+        this.inaccessableField = something.hashCode();
+    }
+
+    public int retrieveMyVerySpecialField() {
+        return inaccessableField;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue116/NoFieldsTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue116/NoFieldsTest.java
new file mode 100644
index 0000000..044cdb8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue116/NoFieldsTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue116;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class NoFieldsTest extends TestCase {
+
+    public void testEmptyClass() {
+        Empty empty = new Empty();
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(empty);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue116.Empty {}\n", result);
+        Object emptyParsed = yaml.load(result);
+        assertTrue(emptyParsed instanceof Empty);
+    }
+
+    public void testHiddenParameter() {
+        Hidden hidden = new Hidden();
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(hidden);
+            fail("an exception should have been thrown");
+        } catch (YAMLException e) {
+            assertEquals(e.getMessage(),
+                    "No JavaBean properties found in org.yaml.snakeyaml.issues.issue116.Hidden");
+        }
+        Object hiddenParsed = yaml.load("!!org.yaml.snakeyaml.issues.issue116.Hidden {}\n");
+        assertTrue(hiddenParsed instanceof Hidden);
+    }
+
+    public void testSpecialHiddenParameter() {
+        HiddenSpecial hidden = new HiddenSpecial("qwerty");
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(hidden);
+            fail("an exception should have been thrown");
+        } catch (YAMLException e) {
+            assertEquals(e.getMessage(),
+                    "No JavaBean properties found in org.yaml.snakeyaml.issues.issue116.HiddenSpecial");
+        }
+        HiddenSpecial hs = (HiddenSpecial) yaml
+                .load("!!org.yaml.snakeyaml.issues.issue116.HiddenSpecial foo\n");
+        assertEquals("foo".hashCode(), hs.retrieveMyVerySpecialField());
+    }
+}
+
+class Empty {
+}
+
+class Hidden {
+    @SuppressWarnings("unused")
+    private int inaccessableField;
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue124/Bean124.java b/src/test/java/org/yaml/snakeyaml/issues/issue124/Bean124.java
new file mode 100644
index 0000000..202e84e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue124/Bean124.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue124;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Bean124 {
+    private String a;
+    private List<Integer> numbers;
+
+    public Bean124() {
+        this.a = "aaa";
+        this.numbers = new ArrayList<Integer>(3);
+        numbers.add(1);
+        numbers.add(2);
+        numbers.add(3);
+    }
+
+    public Bean124(String a, List<Integer> numbers) {
+        super();
+        this.a = a;
+        this.numbers = numbers;
+    }
+
+    public String getA() {
+        return a;
+    }
+
+    public void setA(String a) {
+        this.a = a;
+    }
+
+    public List<Integer> getNumbers() {
+        return numbers;
+    }
+
+    public void setNumbers(List<Integer> numbers) {
+        this.numbers = numbers;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue124/DumpTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue124/DumpTest.java
new file mode 100644
index 0000000..7da5038
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue124/DumpTest.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue124;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class DumpTest extends TestCase {
+
+    public void testDumperOptionsSideEffect() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output0 = yaml.dump(bean);
+        // System.out.println(output0);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue124.Bean124\na: aaa\nnumbers: [1, 2, 3]\n",
+                output0);
+        String output1 = yaml.dumpAsMap(bean);
+        // System.out.println(output1);
+        assertEquals("a: aaa\nnumbers:\n- 1\n- 2\n- 3\n", output1);
+        String output2 = yaml.dump(bean);
+        // System.out.println(output2);
+        assertEquals("Yaml.dumpAs() should not have any side effects.", output0, output2);
+    }
+
+    public void testDumperDifferentTag() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, new Tag("!!foo.bar"), FlowStyle.BLOCK);
+        assertEquals("!!foo.bar\na: aaa\nnumbers:\n- 1\n- 2\n- 3\n", output1);
+    }
+
+    public void testDumperFlowStyle() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, new Tag("!!foo.bar"), FlowStyle.FLOW);
+        assertEquals("!!foo.bar {a: aaa, numbers: [1, 2, 3]}\n", output1);
+    }
+
+    public void testDumperAutoStyle() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, new Tag("!!foo.bar"), FlowStyle.AUTO);
+        assertEquals("!!foo.bar\na: aaa\nnumbers: [1, 2, 3]\n", output1);
+    }
+
+    public void testDumperNullStyle() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, new Tag("!!foo.bar"), null);
+        assertEquals("!!foo.bar\na: aaa\nnumbers: [1, 2, 3]\n", output1);
+    }
+
+    public void testDumperNullStyle2() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, new Tag("!!foo2.bar2"), null);
+        assertEquals("!!foo2.bar2\na: aaa\nnumbers:\n- 1\n- 2\n- 3\n", output1);
+    }
+
+    public void testDumperNullTag() {
+        Yaml yaml = new Yaml();
+        Bean124 bean = new Bean124();
+        String output1 = yaml.dumpAs(bean, null, FlowStyle.BLOCK);
+        assertEquals(
+                "!!org.yaml.snakeyaml.issues.issue124.Bean124\na: aaa\nnumbers:\n- 1\n- 2\n- 3\n",
+                output1);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue127/Bean.java b/src/test/java/org/yaml/snakeyaml/issues/issue127/Bean.java
new file mode 100644
index 0000000..d347249
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue127/Bean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue127;
+
+public class Bean {
+    private String a;
+    private String b;
+
+    public String getA() {
+        return a;
+    }
+
+    public void setA(String a) {
+        this.a = a;
+    }
+
+    public String getB() {
+        return b;
+    }
+
+    public void setB(String b) {
+        this.b = b;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue127/NullAliasTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue127/NullAliasTest.java
new file mode 100644
index 0000000..bd3da2f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue127/NullAliasTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue127;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class NullAliasTest extends TestCase {
+    private static final Tag MY_TAG = new Tag("tag:example.com,2011:bean");
+
+    public void testRespresenter() {
+        Bean bean = new Bean();
+
+        bean.setA("a"); // leave b null
+        Yaml yaml = new Yaml(new BeanRepresenter());
+        String output = yaml.dump(bean);
+        assertEquals("!<tag:example.com,2011:bean>\na: a\nb: null\n", output);
+    }
+
+    class BeanRepresenter extends Representer {
+        public BeanRepresenter() {
+            this.representers.put(Bean.class, new RepresentBean());
+        }
+
+        private class RepresentBean implements Represent {
+            public Node representData(Object data) {
+                Bean bean = (Bean) data;
+                Map<String, Object> fields = new LinkedHashMap<String, Object>(2);
+                fields.put("a", bean.getA());
+                fields.put("b", bean.getB());
+                return representMapping(MY_TAG, fields, false);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue132/ScalarEventTagTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue132/ScalarEventTagTest.java
new file mode 100644
index 0000000..52eb0bd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue132/ScalarEventTagTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue132;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.nodes.Node;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=132
+ */
+public class ScalarEventTagTest extends TestCase {
+    public void testLoad() {
+        Yaml yaml = new Yaml();
+        Iterable<Event> parsed = yaml.parse(new StringReader("5"));
+        List<Event> events = new ArrayList<Event>(5);
+        for (Event event : parsed) {
+            events.add(event);
+            // System.out.println(event);
+        }
+        String tag = ((ScalarEvent) events.get(2)).getTag();
+        assertNull("The tag should not be specified: " + tag, tag);
+    }
+
+    public void testDump() {
+        Yaml yaml = new Yaml();
+        Node intNode = yaml.represent(7);
+        assertEquals("tag:yaml.org,2002:int", intNode.getTag().toString());
+        // System.out.println(intNode);
+        List<Event> intEvents = yaml.serialize(intNode);
+        String tag = ((ScalarEvent) intEvents.get(2)).getTag();
+        assertEquals("Without the tag emitter would not know how to emit '7'",
+                "tag:yaml.org,2002:int", tag);
+        //
+        Node strNode = yaml.represent("7");
+        assertEquals("tag:yaml.org,2002:str", strNode.getTag().toString());
+        // System.out.println(strNode);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue133/StackOverflowTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue133/StackOverflowTest.java
new file mode 100644
index 0000000..080081d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue133/StackOverflowTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue133;
+
+import java.awt.Point;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=133
+ */
+public class StackOverflowTest extends TestCase {
+    public void testDumpRecursiveObject() {
+        try {
+            Yaml yaml = new Yaml();
+            // by default it must fail with StackOverflow
+            yaml.dump(new Point());
+            fail("getLocation() is recursive.");
+        } catch (Throwable e) {
+            String message = e.getMessage();
+            assertTrue("StackOverflow has no message: " + e.getMessage(), message == null
+                    || message.contains("Unable to find getter for property 'location'"));
+        }
+    }
+
+    /**
+     * Since Point.getLocation() creates a new instance of Point class,
+     * SnakeYAML will fail to dump an instance of Point if 'getLocation()' is
+     * also included.
+     * 
+     * Since Point is not really a JavaBean, we can safely skip the recursive
+     * property when we dump the instance of Point.
+     */
+    private class PointRepresenter extends Representer {
+
+        @Override
+        protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+                Object propertyValue, Tag customTag) {
+            if (javaBean instanceof Point && "location".equals(property.getName())) {
+                return null;
+            } else {
+                return super
+                        .representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+            }
+        }
+    }
+
+    public void testDump() {
+        Yaml yaml = new Yaml(new PointRepresenter());
+        String output = yaml.dump(new Point());
+        assertEquals("!!java.awt.Point {x: 0, y: 0}\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue136/TabInScalarTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue136/TabInScalarTest.java
new file mode 100644
index 0000000..09c3b08
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue136/TabInScalarTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue136;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class TabInScalarTest extends TestCase {
+
+    public void testTab() {
+        String data = (String) new Yaml().load("L\tD");
+        assertEquals("L\tD", data);
+    }
+
+    public void testNoTab() {
+        String data = (String) new Yaml().load("L D");
+        assertEquals("L D", data);
+    }
+
+    public void testTabDoubleQuotes() {
+        String data = (String) new Yaml().load("\"L\tD\"");
+        // System.out.println(data);
+        assertEquals("L\tD", data);
+    }
+
+    public void testTabSingleQuotes() {
+        String data = (String) new Yaml().load("'L\tD'");
+        // System.out.println(data);
+        assertEquals("L\tD", data);
+    }
+
+    public void testDumpTab() {
+        String data = (String) new Yaml().dump("L\tD");
+        // System.out.println(data);
+        assertEquals("\"L\\tD\"\n", data);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue137/SupplementaryCharactersTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue137/SupplementaryCharactersTest.java
new file mode 100644
index 0000000..389735d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue137/SupplementaryCharactersTest.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue137;
+
+import java.io.UnsupportedEncodingException;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * http://java.sun.com/developer/technicalArticles/Intl/Supplementary/
+ */
+public class SupplementaryCharactersTest extends TestCase {
+
+    public void testSupplementaryCharacter() {
+        Yaml yaml = new Yaml();
+        String parsed = (String) yaml.load("\"\\U0001f648\"");
+        assertEquals("\ud83d\ude48", parsed);
+        // System.out.println(data);
+    }
+
+    public void testBasicMultilingualPlane() {
+        Yaml yaml = new Yaml();
+        String parsed = (String) yaml.load("\"\\U00000041\"");
+        assertEquals("A", parsed);
+    }
+
+    /**
+     * Supplementary Characters are dumped as binary
+     */
+    public void testDumpSupplementaryCharacter() throws UnsupportedEncodingException {
+        String supplementary = "\ud83d\ude48";
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(supplementary);
+        assertEquals("!!binary |-\n  8J+ZiA==\n", output);
+        byte[] binary = (byte[]) yaml.load(output);
+        String binString = new String(binary, "UTF-8");
+        assertEquals(supplementary, binString);
+    }
+
+    public void testLoadSupplementaryCharacter() {
+        try {
+            new Yaml().load("\"\ud83d\ude48\"\n");
+            fail("Are Supplementary Characters printable ?");
+        } catch (Exception e) {
+            assertEquals("special characters are not allowed", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue138/ReaderExceptionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue138/ReaderExceptionTest.java
new file mode 100644
index 0000000..851ab17
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue138/ReaderExceptionTest.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue138;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.reader.ReaderException;
+
+public class ReaderExceptionTest extends TestCase {
+
+    public void testGetters() {
+        try {
+            new Yaml().load("012\u0019");
+            fail();
+        } catch (ReaderException e) {
+            assertEquals(3, e.getPosition());
+            assertEquals("'string'", e.getName());
+            assertEquals('\u0019', e.getCharacter());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue139/MergeValueTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue139/MergeValueTest.java
new file mode 100644
index 0000000..debf99f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue139/MergeValueTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue139;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class MergeValueTest extends TestCase {
+
+    public void testNotUniqueSimple() {
+        String simple = "{key: 1, key: 2}";
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, Integer> map = (Map<String, Integer>) yaml.load(simple);
+        assertEquals(1, map.size());
+        assertEquals(new Integer(2), map.get("key"));
+    }
+
+    public void testMerge() {
+        check("issues/issue139-1.yaml");// merge with unique keys
+        check("issues/issue139-2.yaml");// merge with same key
+    }
+
+    private void check(String name) {
+        String input = Util.getLocalResource(name);
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, Object> map = (Map<String, Object>) yaml.load(input);
+        assertEquals(2, map.size());
+        assertTrue(map.containsKey("common"));
+        assertTrue(map.containsKey("production"));
+        assertEquals(map.get("common"), map.get("production"));
+    }
+
+    /**
+     * http://yaml.org/type/merge.html: If the value associated with the key is
+     * a single mapping node, each of its key/value pairs is inserted into the
+     * current mapping, <b>unless the key already exists in it</b>.
+     */
+    @SuppressWarnings("unchecked")
+    public void testMergeUnlessAlreadyExists() {
+        String input = Util.getLocalResource("issues/issue139-3.yaml");
+        // System.out.println(input);
+        Yaml yaml = new Yaml();
+        Map<String, Object> map = (Map<String, Object>) yaml.load(input);
+        assertEquals(2, map.size());
+        Map<String, Integer> common = (Map<String, Integer>) map.get("common");
+        Map<String, Integer> production = (Map<String, Integer>) map.get("production");
+        assertEquals(new Integer(2), common.get("key"));
+        assertEquals(new Integer(3), production.get("key"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue139/UniqueKeyTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue139/UniqueKeyTest.java
new file mode 100644
index 0000000..4117b97
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue139/UniqueKeyTest.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue139;
+
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+
+public class UniqueKeyTest extends TestCase {
+
+    public void testNotUnique() {
+        String data = "{key: 1, key: 2}";
+        Yaml yaml = new Yaml(new UniqueKeyConstructor());
+        try {
+            yaml.load(data);
+            fail("The same key must be rejected");
+        } catch (Exception e) {
+            assertEquals("The key is not unique key", e.getMessage());
+        }
+    }
+
+    private class UniqueKeyConstructor extends Constructor {
+
+        @Override
+        protected void constructMapping2ndStep(MappingNode node, Map<Object, Object> mapping) {
+            List<NodeTuple> nodeValue = (List<NodeTuple>) node.getValue();
+            for (NodeTuple tuple : nodeValue) {
+                Node keyNode = tuple.getKeyNode();
+                Node valueNode = tuple.getValueNode();
+                Object key = constructObject(keyNode);
+                if (key != null) {
+                    key.hashCode();// check circular dependencies
+                }
+                Object value = constructObject(valueNode);
+                Object old = mapping.put(key, value);
+                if (old != null) {
+                    throw new YAMLException("The key is not unique " + key);
+                }
+            }
+        }
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue141/ConfigurableTimezoneTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue141/ConfigurableTimezoneTest.java
new file mode 100644
index 0000000..872af85
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue141/ConfigurableTimezoneTest.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue141;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class ConfigurableTimezoneTest extends TestCase {
+
+    public void testNoTimezone() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(new Date());
+        assertTrue(output, output.endsWith("Z\n"));
+    }
+
+    public void testTimezone() {
+        DumperOptions options = new DumperOptions();
+        options.setTimeZone(TimeZone.getTimeZone("GMT+1:00"));
+        Yaml yaml = new Yaml(options);
+        Date date = new Date();
+        String output = yaml.dump(date);
+        // System.out.println(output);
+        assertTrue(output, output.trim().endsWith("+1:00"));
+        Date parsed = (Date) yaml.load(output);
+        assertEquals(date, parsed);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue143/GenericMapTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue143/GenericMapTest.java
new file mode 100644
index 0000000..c98449e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue143/GenericMapTest.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue143;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericMapTest extends TestCase {
+
+    public void testMap() throws Exception {
+        BeanWithMap fact = new BeanWithMap();
+        GenericMap<Integer> shash = fact.getMap();
+        shash.put("toto", new Integer(10));
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        // String txt = yaml.dump(fact);
+        // assertTrue(txt.contains("org.yaml.snakeyaml.issues.issue143.GenericMapTest"));
+    }
+
+    public static class GenericMap<T> extends java.util.HashMap<String, T> {
+        private static final long serialVersionUID = -6833859369398863926L;
+    }
+
+    public class BeanWithMap {
+        public GenericMap<Integer> getMap() {
+            return new GenericMap<Integer>();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue144/BeanData.java b/src/test/java/org/yaml/snakeyaml/issues/issue144/BeanData.java
new file mode 100644
index 0000000..7988000
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue144/BeanData.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue144;
+
+public class BeanData {
+    private String id;
+    private float number;
+
+    public BeanData() {
+        this.id = "noid";
+    }
+
+    public BeanData(String id) {
+        this.id = id;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public float getNumber() {
+        return number;
+    }
+
+    public void setNumber(float number) {
+        this.number = number;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue144/FloatPropertyTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue144/FloatPropertyTest.java
new file mode 100644
index 0000000..b9492aa
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue144/FloatPropertyTest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue144;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.extensions.compactnotation.CompactConstructor;
+
+public class FloatPropertyTest extends TestCase {
+
+    public void testFloatAsJavaBeanProperty() throws Exception {
+        BeanData bean = new BeanData();
+        bean.setId("id1");
+        bean.setNumber(3.5f);
+        Yaml yaml = new Yaml();
+        String txt = yaml.dump(bean);
+        BeanData parsed = yaml.loadAs(txt, BeanData.class);
+        assertEquals(3.5f, parsed.getNumber());
+    }
+
+    public void testCompact() {
+        Yaml yaml = new Yaml(new CompactConstructor());
+        BeanData obj = (BeanData) yaml
+                .load("org.yaml.snakeyaml.issues.issue144.BeanData(id): { number: 123.4 }");
+        assertEquals(123.4f, obj.getNumber());
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue145/AbstractThing.java b/src/test/java/org/yaml/snakeyaml/issues/issue145/AbstractThing.java
new file mode 100644
index 0000000..c169e38
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue145/AbstractThing.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue145;
+
+public abstract class AbstractThing {
+    protected String id;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue145/CompleteThing.java b/src/test/java/org/yaml/snakeyaml/issues/issue145/CompleteThing.java
new file mode 100644
index 0000000..eca4a06
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue145/CompleteThing.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue145;
+
+public class CompleteThing extends AbstractThing {
+
+    @Override
+    public String toString() {
+        return "CompleteThing-" + id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue145/LineNumberInExceptionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue145/LineNumberInExceptionTest.java
new file mode 100644
index 0000000..39c8c74
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue145/LineNumberInExceptionTest.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue145;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class LineNumberInExceptionTest extends TestCase {
+
+    public void testLineReport() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("---\n!!org.yaml.snakeyaml.issues.issue145.AbstractThing { id: QQQ }");
+            fail("Instances for abstract classes cannot be created");
+        } catch (Exception e) {
+            assertTrue(e.toString().contains("line 2, column 1"));
+            assertEquals(
+                    "Can't construct a java object for tag:yaml.org,2002:org.yaml.snakeyaml.issues.issue145.AbstractThing; exception=java.lang.InstantiationException\n"
+                            + " in 'string', line 2, column 1:\n"
+                            + "    !!org.yaml.snakeyaml.issues.issu ... \n" + "    ^\n",
+                    e.getMessage());
+        }
+    }
+
+    public void testCompleteThing() {
+        Yaml yaml = new Yaml();
+        CompleteThing thing = (CompleteThing) yaml
+                .load("---\n!!org.yaml.snakeyaml.issues.issue145.CompleteThing { id: QQQ }");
+        assertEquals("QQQ", thing.getId());
+    }
+
+    public void testWrongParameter() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("---\n!!org.yaml.snakeyaml.issues.issue145.CompleteThing { id2: QQQ }");
+            fail("Invalid parameter");
+        } catch (Exception e) {
+            assertTrue("The error should ponit to QQQ.", e.toString().contains("line 2, column 59"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue147/PrintableTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue147/PrintableTest.java
new file mode 100644
index 0000000..a534b49
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue147/PrintableTest.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue147;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class PrintableTest extends TestCase {
+    // http://code.google.com/p/snakeyaml/issues/detail?id=147
+    public void testFFFD() {
+        Yaml yaml = new Yaml();
+        String fffd = (String) yaml.load(yaml.dump("\uFFFD"));
+        assertEquals("\uFFFD", fffd);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue148/PrintableUnicodeTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue148/PrintableUnicodeTest.java
new file mode 100644
index 0000000..0fa9322
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue148/PrintableUnicodeTest.java
@@ -0,0 +1,177 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue148;
+
+import java.util.Formatter;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.reader.ReaderException;
+
+public class PrintableUnicodeTest extends TestCase {
+    public void testFFFD() {
+        Yaml yaml = createYaml();
+        String fffd = yaml.dump("\uFFFD");
+        assertEquals("\"\\ufffd\"\n", fffd);
+    }
+
+    public void testSerialization() {
+        // test serialization of all Unicode codepoints
+        Yaml yaml = createYaml();
+        for (int c = Character.MIN_VALUE; c <= Character.MAX_VALUE; c++) {
+            String original = Character.toString((char) c);
+            String serialized = yaml.dump(original);
+
+            // "On output, a YAML processor must only produce these acceptable
+            // characters,
+            // and should also escape all non-printable Unicode characters."
+            for (int i = 0; i < serialized.length(); i++) {
+                int cp = (int) serialized.charAt(i);
+                if (!isAcceptable(cp))
+                    fail(String.format(
+                            "U+%04x: Serialization produced result with unacceptable U+%04x\n", c,
+                            cp));
+                if (!isPrintable(cp))
+                    fail(String.format(
+                            "U+%04x: Serialization produced result with nonprintable U+%04x\n", c,
+                            cp));
+            }
+        }
+    }
+
+    public void testDeserialization() {
+        // test deserialization of non-escaped codepoints
+        for (int c = Character.MIN_VALUE; c <= Character.MAX_VALUE; c++) {
+            // ignore breaks, which have special meaning
+            if (c == 0x0A || c == 0x0D || c == 0x85 || c == 0x2028 || c == 0x2029)
+                continue;
+            if (!isAcceptable(c) || c == 0x27)
+                continue;
+            String expected = Character.toString((char) c);
+            String serialized = "'" + expected + "'";
+
+            String result;
+            try {
+                result = new Yaml().load(serialized).toString();
+            } catch (ReaderException e) {
+                fail(String
+                        .format("U+%04x: Deserialization threw ReaderException for an acceptable character\n",
+                                c));
+                continue;
+            }
+            if (!result.equals(expected))
+                fail(String.format("U+%04x: Deserialization incorrect: %s\n", c, hexdump(result)));
+        }
+    }
+
+    public void testDeserialization2() {
+        // test deserialization of escaped codepoints
+        // "Any such characters must be presented using escape sequences."
+        for (int c = Character.MIN_VALUE; c <= Character.MAX_VALUE; c++) {
+            String expected = Character.toString((char) c);
+            String serialized = String.format("\"\\u%04x\"", c);
+
+            String result;
+            try {
+                result = new Yaml().load(serialized).toString();
+            } catch (ReaderException e) {
+                fail(String
+                        .format("U+%04x: Deserialization threw ReaderException for an acceptable escaped character\n",
+                                c));
+                continue;
+            }
+            if (!result.equals(expected))
+                fail(String.format("U+%04x: Deserialization of escaped character incorrect: %s\n",
+                        c, hexdump(result)));
+        }
+    }
+
+    private Yaml createYaml() {
+        DumperOptions options = new DumperOptions();
+        options.setAllowUnicode(false);
+        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        return new Yaml(options);
+    }
+
+    /**
+     * Test whether a character is printable, according to the YAML spec.
+     * ('c-printable')
+     */
+    public static boolean isPrintable(int c) {
+        return c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0x7E) // 8
+                                                                            // bit
+                || c == 0x85 || (c >= 0xA0 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) // 16
+                                                                                           // bit
+                || (c >= 0x10000 && c <= 0x10FFFF); // 32 bit
+    }
+
+    /**
+     * "On input, a YAML processor must accept all printable ASCII characters,
+     * the space, tab, line break, and all Unicode characters beyond #x9F. On
+     * output, a YAML processor must only produce these acceptable characters,
+     * and should also escape all non-printable Unicode characters. The allowed
+     * character range explicitly excludes the surrogate block #xD800-#xDFFF,
+     * DEL #x7F, the C0 control block #x0-#x1F (except for #x9, #xA, and #xD),
+     * the C1 control block #x80-#x9F, #xFFFE, and #xFFFF."
+     */
+    public static boolean isAcceptable(int c) {
+        return (c >= 0x20 && c <= 0x7e // accept all printable ASCII characters,
+                                       // the space,
+                || c == 0x09 // tab,
+                || c == 0x0A || c == 0x0D || c == 0x85 || c == 0x2028 || c == 0x2029 // line
+                                                                                     // break,
+        || isUnicodeCharacter(c) && c >= 0x9F // and all Unicode characters
+                                              // beyond #x9F
+        ) && !( // The allowed character range explicitly excludes
+                c >= 0xD800 && c <= 0xDFFF // the surrogate block #xD800-#xDFFF
+                        || c == 0x7f // DEL #x7F,
+                        || c <= 0x1F && !(c == 0x09 || c == 0x0A || c == 0x0D) // the
+                                                                               // C0
+                                                                               // control
+                                                                               // block
+                                                                               // #x0-#x1F
+                                                                               // (except
+                                                                               // for
+                                                                               // #x9,
+                                                                               // #xA,
+                                                                               // and
+                                                                               // #xD),
+                        || c >= 0x80 && c <= 0x9F // the C1 control block
+                                                  // #x80-#x9F,
+                        || c == 0xFFFE // #xFFFE,
+                || c == 0xFFFF // and #xFFFF.
+                );
+    }
+
+    /**
+     * Tests whether a codepoint is a designated Unicode noncharacter or not.
+     */
+    public static boolean isUnicodeCharacter(int c) {
+        int plane = c / 0x10000;
+        return !(c >= 0xFDD0 && c <= 0xFDEF) && (plane <= 16 && (c & 0xFFFE) != 0xFFFE);
+    }
+
+    public static String hexdump(String input) {
+        StringBuilder result = new StringBuilder();
+        Formatter formatter = new Formatter(result);
+        for (int i = 0; i < input.length(); i++)
+            formatter.format("%02x ", (int) input.charAt(i));
+        return result.toString();
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue149/ComponentBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue149/ComponentBean.java
new file mode 100644
index 0000000..97f7d1e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue149/ComponentBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue149;
+
+public class ComponentBean {
+    private int property1;
+    private String property2;
+
+    public int getProperty1() {
+        return property1;
+    }
+
+    public void setProperty1(int property1) {
+        this.property1 = property1;
+    }
+
+    public String getProperty2() {
+        return property2;
+    }
+
+    public void setProperty2(String property2) {
+        this.property2 = property2;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue149/GlobalDirectivesTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue149/GlobalDirectivesTest.java
new file mode 100644
index 0000000..6c2b5f4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue149/GlobalDirectivesTest.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue149;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class GlobalDirectivesTest extends TestCase {
+    public void testOneDocument() {
+        String input = Util.getLocalResource("issues/issue149-one-document.yaml");
+        // System.out.println(input);
+        Constructor constr = new Constructor();
+        TypeDescription description = new TypeDescription(ComponentBean.class, new Tag(
+                "tag:ualberta.ca,2012:29"));
+        constr.addTypeDescription(description);
+        Yaml yaml = new Yaml(constr);
+        Iterator<Object> parsed = yaml.loadAll(input).iterator();
+        ComponentBean bean = (ComponentBean) parsed.next();
+        assertEquals(0, bean.getProperty1());
+        assertEquals("aaa", bean.getProperty2());
+        assertFalse(parsed.hasNext());
+    }
+
+    public void testDirectives() {
+        String input = Util.getLocalResource("issues/issue149-losing-directives.yaml");
+        // System.out.println(input);
+        Constructor constr = new Constructor();
+        TypeDescription description = new TypeDescription(ComponentBean.class, new Tag(
+                "tag:ualberta.ca,2012:" + 29));
+        constr.addTypeDescription(description);
+        Yaml yaml = new Yaml(constr);
+        Iterator<Object> parsed = yaml.loadAll(input).iterator();
+        ComponentBean bean1 = (ComponentBean) parsed.next();
+        assertEquals(0, bean1.getProperty1());
+        assertEquals("aaa", bean1.getProperty2());
+        ComponentBean bean2 = (ComponentBean) parsed.next();
+        assertEquals(3, bean2.getProperty1());
+        assertEquals("bbb", bean2.getProperty2());
+        assertFalse(parsed.hasNext());
+    }
+
+    public void testDirectives2() {
+        String input = Util.getLocalResource("issues/issue149-losing-directives-2.yaml");
+        // System.out.println(input);
+        Constructor constr = new Constructor();
+        TypeDescription description = new TypeDescription(ComponentBean.class, new Tag(
+                "tag:ualberta.ca,2012:" + 29));
+        constr.addTypeDescription(description);
+        Yaml yaml = new Yaml(constr);
+        Iterator<Object> parsed = yaml.loadAll(input).iterator();
+        ComponentBean bean1 = (ComponentBean) parsed.next();
+        assertEquals(0, bean1.getProperty1());
+        assertEquals("aaa", bean1.getProperty2());
+        ComponentBean bean2 = (ComponentBean) parsed.next();
+        assertEquals(3, bean2.getProperty1());
+        assertEquals("bbb", bean2.getProperty2());
+        assertFalse(parsed.hasNext());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue150/Car.java b/src/test/java/org/yaml/snakeyaml/issues/issue150/Car.java
new file mode 100644
index 0000000..1973d9d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue150/Car.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue150;
+
+import java.util.Collection;
+
+/**
+ * 
+ * @author tturki
+ * 
+ */
+public class Car {
+    private String plate;
+    private Collection<Wheel> wheels;
+
+    public Car() {
+    }
+
+    public String getPlate() {
+        return plate;
+    }
+
+    public void setPlate(String plate) {
+        this.plate = plate;
+    }
+
+    public Collection<Wheel> getWheels() {
+        return wheels;
+    }
+
+    public void setWheels(Collection<Wheel> wheels) {
+        this.wheels = wheels;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue150/Wheel.java b/src/test/java/org/yaml/snakeyaml/issues/issue150/Wheel.java
new file mode 100644
index 0000000..bae1637
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue150/Wheel.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue150;
+
+public class Wheel {
+    private Integer id;
+
+    public Wheel() {
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue150/YamlLoadAsIssueTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue150/YamlLoadAsIssueTest.java
new file mode 100644
index 0000000..f4002d4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue150/YamlLoadAsIssueTest.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue150;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class YamlLoadAsIssueTest {
+
+    private StringBuilder doc;
+
+    @Before
+    public void setUp() {
+        doc = new StringBuilder();
+        line("!car");
+        line("plate: 12-XP-F4");
+        line("wheels:");
+        line("- w#1");
+        line("- w#2");
+        line("- w#3");
+        line("- w#4");
+    }
+
+    private StringBuilder line(String text) {
+        return doc.append(text).append('\n');
+    }
+
+    @Test
+    public void loadConstructsDocumentCorrectly() {
+        Yaml yaml = yaml();
+        Object result = yaml.load(reader());
+        assertNotNull(result);
+        assertEquals(Car.class, result.getClass());
+        assertEquals("12-XP-F4", ((Car) result).getPlate());
+        assertEquals(4, ((Car) result).getWheels().size());
+    }
+
+    private Yaml yaml() {
+        Yaml yaml = new Yaml(new MyConstructor());
+        yaml.addImplicitResolver(new Tag("!wheel"), Pattern.compile("w#\\d+"), "w");
+        return yaml;
+    }
+
+    @Test
+    public void ignoreImplicitTag() {
+        Yaml yaml = yaml();
+        Car car = yaml.loadAs(reader(), Car.class);
+        assertNotNull(car);
+        assertEquals("12-XP-F4", car.getPlate());
+        ArrayList<Wheel> wheels = new ArrayList<Wheel>(car.getWheels());
+        assertEquals(4, wheels.size());
+        for (int i = 0; i < wheels.size(); i++) {
+            assertEquals(wheels.get(i).getId(), Integer.valueOf(i + 1));
+        }
+    }
+
+    private Reader reader() {
+        return new StringReader(doc.toString());
+    }
+
+    private class MyConstructor extends Constructor {
+        public MyConstructor() {
+            yamlConstructors.put(new Tag("!car"), new ConstructCar());
+            yamlConstructors.put(new Tag("!wheel"), new ConstructWheel());
+        }
+
+        private String toScalarString(Node node) {
+            return (String) constructScalar((ScalarNode) node);
+        }
+
+        private class ConstructCar extends AbstractConstruct {
+
+            @SuppressWarnings("unchecked")
+            public Car construct(Node node) {
+                Car car = new Car();
+                MappingNode mapping = (MappingNode) node;
+                List<NodeTuple> list = mapping.getValue();
+                for (NodeTuple tuple : list) {
+                    String field = toScalarString(tuple.getKeyNode());
+                    if ("plate".equals(field)) {
+                        car.setPlate(toScalarString(tuple.getValueNode()));
+                    }
+                    if ("wheels".equals(field)) {
+                        SequenceNode snode = (SequenceNode) tuple.getValueNode();
+                        List<Wheel> wheels = (List<Wheel>) constructSequence(snode);
+                        car.setWheels(wheels);
+                    }
+                }
+                return car;
+            }
+        }
+
+        private class ConstructWheel extends AbstractConstruct {
+
+            public Wheel construct(Node node) {
+                Wheel w = null;
+                String strValue = toScalarString(node);
+                if (strValue != null && strValue.length() > 2) {
+                    strValue = strValue.trim().substring(2);
+                    w = new Wheel();
+                    w.setId(Integer.valueOf(strValue));
+                }
+                return w;
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue151/EscapedUnicodeTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue151/EscapedUnicodeTest.java
new file mode 100644
index 0000000..86a88fc
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue151/EscapedUnicodeTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue151;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class EscapedUnicodeTest extends TestCase {
+
+    public void testUnicode() {
+        Yaml yaml = new Yaml();
+        // http://www.tutorialspoint.com/html/ascii_table_lookup.htm
+        String str = (String) yaml.load("\"\\xC3\\xA4\"");
+        assertEquals("2 escape sequences must be converted to 2 characters.", "ä", str);
+    }
+
+    public void testUnicode2() {
+        Yaml yaml = new Yaml();
+        String str = (String) yaml.load("\"Acetylsalicyls\\xE4ure\"");
+        assertEquals("E4 -> ä", "Acetylsalicylsäure", str);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue154/MissingPropertyTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue154/MissingPropertyTest.java
new file mode 100644
index 0000000..6a4c514
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue154/MissingPropertyTest.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue154;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class MissingPropertyTest extends TestCase {
+
+    private Yaml yaml;
+
+    public void setUp() {
+        yaml = new Yaml();
+    }
+
+    /**
+     * A normal scalar property should work fine.
+     */
+    public void testPublicField() throws Exception {
+        String doc = "hello: 5";
+        TestBean bean = yaml.loadAs(doc, TestBean.class);
+        assertNotNull(bean);
+        assertEquals(5, bean.hello);
+    }
+
+    /**
+     * By default, unknown fields should throw a YAMLException.
+     */
+    public void testUnknownField() throws Exception {
+        try {
+            String doc = "goodbye: 10";
+            yaml.loadAs(doc, TestBean.class);
+        } catch (YAMLException e) {
+            assertTrue(e.getMessage().contains("Cannot create property=goodbye"));
+        }
+    }
+
+    /**
+     * A new method setSkipMissingProperties(boolean) was added to configure
+     * whether missing properties should throw a YAMLException (the default) or
+     * simply show a warning. The default is false.
+     */
+    public void testSkipMissingProperties() throws Exception {
+        Representer representer = new Representer();
+        representer.getPropertyUtils().setSkipMissingProperties(true);
+        yaml = new Yaml(new Constructor(), representer);
+        String doc = "goodbye: 10\nhello: 5\nfizz: [1]";
+        TestBean bean = yaml.loadAs(doc, TestBean.class);
+        assertNotNull(bean);
+        assertEquals(5, bean.hello);
+    }
+
+    /**
+     * The default for setSkipMissingProperties(boolean) is false; this just
+     * ensures it works if set manually.
+     */
+    public void testNoSkipMissingProperties() throws Exception {
+        try {
+            Representer representer = new Representer();
+            representer.getPropertyUtils().setSkipMissingProperties(false);
+            yaml = new Yaml(new Constructor(), representer);
+            String doc = "goodbye: 10";
+            yaml.loadAs(doc, TestBean.class);
+        } catch (YAMLException e) {
+            assertTrue(e.getMessage().contains("Cannot create property=goodbye"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue154/TestBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue154/TestBean.java
new file mode 100644
index 0000000..264b58b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue154/TestBean.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue154;
+
+public class TestBean {
+    public int hello;
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue155/BinaryTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue155/BinaryTest.java
new file mode 100644
index 0000000..f5c5ef2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue155/BinaryTest.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue155;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class BinaryTest extends TestCase {
+
+    public void testBinaryString() throws Exception {
+        String data = "\u2666";
+        byte[] bytes = data.getBytes("UTF-8");
+        String inconsistentString = new String(bytes, "ISO-8859-1");
+        Yaml yaml = new Yaml();
+        String payload = yaml.dump(inconsistentString);
+        // System.out.println("payload: '" + payload + "'");
+        String loaded = new String((byte[]) yaml.load(payload), "UTF-8");
+        assertEquals(inconsistentString, loaded);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue163/LinearScalaTralingTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue163/LinearScalaTralingTest.java
new file mode 100644
index 0000000..6210f92
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue163/LinearScalaTralingTest.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue163;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class LinearScalaTralingTest extends TestCase {
+
+    public void testClipChomping() throws Exception {
+        String data = "testnode: |\n   This is line 1\n   This is line 2\n";
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, String> payload = (Map<String, String>) yaml.load(data);
+        assertEquals("This is line 1\nThis is line 2\n", payload.get("testnode"));
+    }
+
+    public void testStripChomping() throws Exception {
+        // mind the trailing '|-' to indicate strip chomping
+        String data = "testnode: |-\n   This is line 1\n   This is line 2\n";
+        Yaml yaml = new Yaml();
+        @SuppressWarnings("unchecked")
+        Map<String, String> payload = (Map<String, String>) yaml.load(data);
+        assertEquals("No traling line break expected.", "This is line 1\nThis is line 2",
+                payload.get("testnode"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue171/ClassWithGenericMap.java b/src/test/java/org/yaml/snakeyaml/issues/issue171/ClassWithGenericMap.java
new file mode 100644
index 0000000..5cfe7cc
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue171/ClassWithGenericMap.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue171;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+class ClassWithGenericMap {
+    public final Map<String, Collection<Integer>> services = new HashMap<String, Collection<Integer>>();
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue171/CustomRepresenter.java b/src/test/java/org/yaml/snakeyaml/issues/issue171/CustomRepresenter.java
new file mode 100644
index 0000000..093f90a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue171/CustomRepresenter.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue171;
+
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+class CustomRepresenter extends Representer {
+    public CustomRepresenter() {
+        this.representers.put(ClassWithGenericMap.class, new RepresentClassX());
+    }
+
+    private class RepresentClassX implements Represent {
+        public Node representData(Object data) {
+            ClassWithGenericMap classX = (ClassWithGenericMap) data;
+            return representMapping(Tag.MAP, classX.services, false);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue171/GenericExtendsObjectTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue171/GenericExtendsObjectTest.java
new file mode 100644
index 0000000..4ff2a49
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue171/GenericExtendsObjectTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue171;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericExtendsObjectTest extends TestCase {
+
+    public void testNoCompilationError() throws Exception {
+        Yaml yaml = new Yaml(new CustomRepresenter());
+        ClassWithGenericMap map = new ClassWithGenericMap();
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(7);
+        map.services.put("wow", list);
+        String output = yaml.dump(map);
+        assertEquals("wow: [7]\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue173/RecursiveAnchorTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue173/RecursiveAnchorTest.java
new file mode 100644
index 0000000..4571d1c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue173/RecursiveAnchorTest.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue173;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class RecursiveAnchorTest extends TestCase {
+
+    public void testWithoutCustomStyle() throws Exception {
+        Map<String, Map<String, Object>> rootMap = new HashMap<String, Map<String, Object>>();
+        Map<String, Object> enclosedMap = new HashMap<String, Object>();
+        enclosedMap.put("world", "test");
+        rootMap.put("test", enclosedMap);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(rootMap);
+        assertEquals("test: {world: test}\n", output);
+    }
+
+    public void testWithBlockStyle() throws Exception {
+        Map<String, Map<String, Object>> rootMap = new HashMap<String, Map<String, Object>>();
+        Map<String, Object> enclosedMap = new HashMap<String, Object>();
+        enclosedMap.put("world", "test");
+        rootMap.put("test", enclosedMap);
+
+        DumperOptions yamlOptions = new DumperOptions();
+        yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+
+        Representer yamlRepresenter = new Representer();
+
+        Yaml yaml = new Yaml(yamlRepresenter, yamlOptions);
+        String output = yaml.dump(rootMap);
+        assertEquals("test:\n  world: test\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue176/SingleQuoteTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue176/SingleQuoteTest.java
new file mode 100644
index 0000000..be5a040
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue176/SingleQuoteTest.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue176;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class SingleQuoteTest extends TestCase {
+
+    public void testNoSingleQuoteForBlockStyle() throws Exception {
+        checkQuotes(true, "cows:\n    steak:cow: '11'");
+    }
+
+    public void testSingleQuoteForFlowStyle() throws Exception {
+        checkQuotes(false, "cows: {'steak:cow': '11'}");
+    }
+
+    private void checkQuotes(boolean isBlock, String expectation) {
+        DumperOptions options = new DumperOptions();
+        options.setIndent(4);
+        if (isBlock) {
+            options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        }
+        Representer representer = new Representer();
+
+        Yaml yaml = new Yaml(new SafeConstructor(), representer, options);
+
+        LinkedHashMap<String, Object> lvl1 = new LinkedHashMap<String, Object>();
+        lvl1.put("steak:cow", "11");
+        LinkedHashMap<String, Object> root = new LinkedHashMap<String, Object>();
+        root.put("cows", lvl1);
+        String output = yaml.dump(root);
+        assertEquals(expectation + "\n", output);
+
+        // parse the value back
+        @SuppressWarnings("unchecked")
+        Map<String, Object> cows = (Map<String, Object>) yaml.load(output);
+        @SuppressWarnings("unchecked")
+        Map<String, String> cow = (Map<String, String>) cows.get("cows");
+        assertEquals("11", cow.get("steak:cow"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBean.java
new file mode 100644
index 0000000..ea46af9
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBean.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue177;
+
+public class PointBean {
+    public int x;
+    public int y;
+
+    @Override
+    public String toString() {
+        return "PointBean";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBeanTest.java
new file mode 100644
index 0000000..274cba3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue177/PointBeanTest.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue177;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class PointBeanTest extends TestCase {
+
+    public void testNoSingleQuoteForBlockStyle() throws Exception {
+        String input = Util.getLocalResource("issues/issue177-1.yaml");
+        try {
+            Yaml yaml = new Yaml();
+            yaml.load(input);
+            fail();
+        } catch (Exception e) {
+            assertEquals("Cannot create property=points for JavaBean=All Points\n"
+                    + " in 'string', line 1, column 1:\n"
+                    + "    !!org.yaml.snakeyaml.issues.issu ... \n" + "    ^\n"
+                    + "Cannot create property=x for JavaBean=PointBean\n"
+                    + " in 'string', line 7, column 5:\n" + "        x: a\n" + "        ^\n"
+                    + "For input string: \"a\"\n" + " in 'string', line 7, column 8:\n"
+                    + "        x: a\n" + "           ^\n" + "\n"
+                    + " in 'string', line 3, column 3:\n" + "      pt1:\n" + "      ^\n",
+                    e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue177/Points.java b/src/test/java/org/yaml/snakeyaml/issues/issue177/Points.java
new file mode 100644
index 0000000..e0e57c5
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue177/Points.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue177;
+
+import java.util.Map;
+
+public class Points {
+    public Map<String, PointBean> points;
+
+    @Override
+    public String toString() {
+        return "All Points";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBean.java
new file mode 100644
index 0000000..691959f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBean.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue182;
+
+public class InfinityFloatBean {
+    public float infinityFloat;
+    public Float infinityFloatObject;
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBeanTest.java
new file mode 100644
index 0000000..f589f03
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue182/InfinityFloatBeanTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue182;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class InfinityFloatBeanTest extends TestCase {
+
+    public void testInfinityFloatBean() throws Exception {
+
+        InfinityFloatBean bean = new InfinityFloatBean();
+        bean.infinityFloat = Float.POSITIVE_INFINITY;
+        bean.infinityFloatObject = Float.POSITIVE_INFINITY;
+
+        Yaml yaml = new Yaml();
+        String yamled = yaml.dump(bean);
+        InfinityFloatBean loadedBean = yaml.loadAs(yamled, InfinityFloatBean.class);
+        assertEquals(Float.POSITIVE_INFINITY, loadedBean.infinityFloat);
+        assertEquals(Float.POSITIVE_INFINITY, loadedBean.infinityFloatObject);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBean.java
new file mode 100644
index 0000000..0d49846
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBean.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue183;
+
+public class NumberBean {
+    public Number number;
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBeanTest.java
new file mode 100644
index 0000000..8200a01
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue183/NumberBeanTest.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue183;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class NumberBeanTest extends TestCase {
+
+    public void testNumberBean() throws Exception {
+
+        NumberBean number = new NumberBean();
+        number.number = 1;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(new Long(1), loaded.number);
+    }
+
+    public void testNumberAsFloatInfinity() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Float.POSITIVE_INFINITY;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Float.POSITIVE_INFINITY, loaded.number.floatValue());
+    }
+
+    public void testNumberAsDoubleInfinity() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Double.POSITIVE_INFINITY;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Double.POSITIVE_INFINITY, loaded.number.doubleValue());
+    }
+
+    public void testNumberAsNegativeFloatInfinity() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Float.NEGATIVE_INFINITY;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Float.NEGATIVE_INFINITY, loaded.number.floatValue());
+    }
+
+    public void testNumberAsNegativeDoubleInfinity() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Double.NEGATIVE_INFINITY;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Double.NEGATIVE_INFINITY, loaded.number.doubleValue());
+    }
+
+    public void testNumberAsFloatNaN() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Float.NaN;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Float.NaN, loaded.number.floatValue());
+    }
+
+    public void testNumberAsDoubleNaN() throws Exception {
+        NumberBean number = new NumberBean();
+        number.number = Double.NaN;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(number);
+        NumberBean loaded = yaml.loadAs(dump, NumberBean.class);
+        assertEquals(Double.NaN, loaded.number.doubleValue());
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue188/ErrorMessageTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue188/ErrorMessageTest.java
new file mode 100644
index 0000000..fa17b68
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue188/ErrorMessageTest.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue188;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ErrorMessageTest extends TestCase {
+
+    public void testErrorMessage() throws Exception {
+
+        try {
+            Yaml yaml = new Yaml();
+            String doc = "templates:\n" + "  master:\n" + "    type: Compute : invalid123";
+            yaml.load(doc);
+            fail();
+        } catch (Exception e) {
+            assertFalse(e.getMessage(), e.getMessage().contains("null"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue192/EqualsSignTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue192/EqualsSignTest.java
new file mode 100644
index 0000000..f48f7d1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue192/EqualsSignTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue192;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.Yaml;
+
+public class EqualsSignTest extends TestCase {
+
+    public void testMappingNode() {
+        new Yaml().load("part1: =");
+    }
+
+    public void testScalarNode() {
+        new Yaml().load("=");
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue193/AbstractBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue193/AbstractBeanTest.java
new file mode 100644
index 0000000..8476a6a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue193/AbstractBeanTest.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue193;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.BaseConstructor;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class AbstractBeanTest extends TestCase {
+
+    public void testErrorMessage() throws Exception {
+
+        BeanA1 b = new BeanA1();
+        b.setId(2l);
+        b.setName("name1");
+
+        Constructor c = new Constructor();
+        Representer r = new Representer();
+
+        PropertyUtils pu = new PropertyUtils();
+        c.setPropertyUtils(pu);
+        r.setPropertyUtils(pu);
+
+        pu.getProperties(BeanA1.class, BeanAccess.FIELD);
+
+        Yaml yaml = new Yaml(c, r);
+        // yaml.setBeanAccess(BeanAccess.FIELD);
+        String dump = yaml.dump(b);
+        BeanA1 b2 = (BeanA1) yaml.load(dump);
+        assertEquals(b.getId(), b2.getId());
+        assertEquals(b.getName(), b2.getName());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA.java b/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA.java
new file mode 100644
index 0000000..e2ab61b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue193;
+
+public abstract class BeanA {
+
+    public abstract Object getId();
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA1.java b/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA1.java
new file mode 100644
index 0000000..f7b7719
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue193/BeanA1.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue193;
+
+public class BeanA1 extends BeanA {
+
+    private Long id;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue193/TestYaml.java b/src/test/java/org/yaml/snakeyaml/issues/issue193/TestYaml.java
new file mode 100644
index 0000000..662c74b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue193/TestYaml.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue193;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class TestYaml {
+
+    public static abstract class BeanA {
+
+        public abstract Object getId();
+
+    }
+
+    public static class BeanA1 extends BeanA {
+
+        private Long id;
+
+        // @Override
+        public Long getId() {
+            return id;
+        }
+
+        public void setId(Long id) {
+            this.id = id;
+        }
+
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+    }
+
+    public static void main(String[] args) {
+
+        System.out.println("test...");
+        BeanA1 b = new BeanA1();
+        b.setId(2l);
+        b.setName("name1");
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(b);
+
+        System.out.println("dump:" + dump);
+
+        dump = "!!org.yaml.snakeyaml.issues.issue193.TestYaml$BeanA1 {id: 2, name: name1}";
+
+        yaml.load(dump);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifier.java b/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifier.java
new file mode 100644
index 0000000..0557412
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifier.java
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue203;
+
+public interface ContentIdentifier extends Identifiable<Long> {};
+
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifierImpl.java b/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifierImpl.java
new file mode 100644
index 0000000..b4c10f8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue203/ContentIdentifierImpl.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue203;
+
+public class ContentIdentifierImpl  implements ContentIdentifier {
+
+    private Long id;
+
+    public ContentIdentifierImpl(Long id) {
+        this.id = id;
+    }
+
+    public Long getId() {
+        return id;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue203/DataBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue203/DataBean.java
new file mode 100644
index 0000000..dc96d04
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue203/DataBean.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue203;
+
+public class DataBean {
+    private String id;
+    private ContentIdentifier content;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+
+    public ContentIdentifier getContent() {
+        return content;
+    }
+
+    public void setContent(ContentIdentifier content) {
+        this.content = content;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue203/GenericTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue203/GenericTest.java
new file mode 100644
index 0000000..165fbe6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue203/GenericTest.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue203;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericTest extends TestCase {
+
+    public void testGenericInterface() {
+        Yaml yaml = new Yaml();
+        String uuu = "!!org.yaml.snakeyaml.issues.issue203.DataBean\n"
+                + "content: !!org.yaml.snakeyaml.issues.issue203.ContentIdentifierImpl 33\n"
+                + "id: 555";
+        DataBean obj = (DataBean) yaml.load(uuu);
+        assertEquals(33, obj.getContent().getId().intValue());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue203/Identifiable.java b/src/test/java/org/yaml/snakeyaml/issues/issue203/Identifiable.java
new file mode 100644
index 0000000..dbe1ffe
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue203/Identifiable.java
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue203;
+
+import java.io.Serializable;
+
+public interface Identifiable<ID extends Serializable> {
+    public ID getId();
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue205/AppleSmileTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue205/AppleSmileTest.java
new file mode 100644
index 0000000..f17556e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue205/AppleSmileTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue205;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.InputStream;
+
+public class AppleSmileTest extends TestCase {
+
+    public void testEmoji() {
+        //http://support.apple.com/en-us/ht4976
+        InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("issues/ios_emoji_surrogate.yaml");
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(input);
+            fail("Surrogate characters must not be accepted.");
+        } catch (Exception e) {
+            assertEquals("special characters are not allowed", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue207/OctalNumberTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue207/OctalNumberTest.java
new file mode 100644
index 0000000..82dd5d5
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue207/OctalNumberTest.java
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue207;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.InputStream;
+import java.util.Map;
+
+public class OctalNumberTest extends TestCase {
+
+    public void testOctalNumbersMoreThenSeven() {
+        Yaml yaml = new Yaml();
+        assertEquals(7, yaml.load("07"));
+        assertEquals(63, yaml.load("077"));
+        assertEquals(0, yaml.load("0"));
+        assertEquals("0A", yaml.load("0A"));
+        assertEquals("09", yaml.load("!!str 09"));
+
+        /* TODO fix 207 and 130
+        assertEquals("08", yaml.load("08"));
+        assertEquals("09", yaml.load("09"));
+        Map<String, String> parsed9 = (Map<String, String>) yaml.load("a: 09");
+        assertEquals("09", parsed9.get("a"));
+        */
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue24/LineNumberTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue24/LineNumberTest.java
new file mode 100644
index 0000000..3ea8ead
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue24/LineNumberTest.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue24;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=24
+ */
+public class LineNumberTest extends TestCase {
+    public void test1() {
+        String resource = Util.getLocalResource("issues/issue24-1.yaml");
+        // System.out.println(resource);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(resource);
+            fail();
+        } catch (Exception e) {
+            assertTrue(e.toString(), e.toString().contains("line 3"));
+            assertTrue(e.toString(), e.toString().contains("column 12"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue29/BigJavaBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue29/BigJavaBean.java
new file mode 100644
index 0000000..5141662
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue29/BigJavaBean.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue29;
+
+import java.util.List;
+import java.util.Map;
+
+public class BigJavaBean {
+    private String name;
+    private String address;
+    private String description;
+    private int id;
+    private List<Integer> numbers;
+    private Map<String, String> data;
+
+    public BigJavaBean() {
+    }
+
+    public BigJavaBean(int id, String name, String address, String description) {
+        super();
+        this.name = name;
+        this.address = address;
+        this.description = description;
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getAddress() {
+        return address;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public List<Integer> getNumbers() {
+        return numbers;
+    }
+
+    public void setNumbers(List<Integer> numbers) {
+        this.numbers = numbers;
+    }
+
+    public Map<String, String> getData() {
+        return data;
+    }
+
+    public void setData(Map<String, String> data) {
+        this.data = data;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((address == null) ? 0 : address.hashCode());
+        result = prime * result + ((data == null) ? 0 : data.hashCode());
+        result = prime * result + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + id;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((numbers == null) ? 0 : numbers.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BigJavaBean other = (BigJavaBean) obj;
+        if (address == null) {
+            if (other.address != null)
+                return false;
+        } else if (!address.equals(other.address))
+            return false;
+        if (data == null) {
+            if (other.data != null)
+                return false;
+        } else if (!data.equals(other.data))
+            return false;
+        if (description == null) {
+            if (other.description != null)
+                return false;
+        } else if (!description.equals(other.description))
+            return false;
+        if (id != other.id)
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (numbers == null) {
+            if (other.numbers != null)
+                return false;
+        } else if (!numbers.equals(other.numbers))
+            return false;
+        return true;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStyleTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStyleTest.java
new file mode 100644
index 0000000..384b83d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStyleTest.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue29;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=29
+ */
+public class FlexibleScalarStyleTest extends TestCase {
+    public void testLong() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.FOLDED);
+        Yaml yaml = new Yaml(options);
+        String result = yaml
+                .dump("qqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqq "
+                        + "qqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqq "
+                        + "qqqqqqqqqqqqqqqqqqqqqqqqq 111111111111111111111111\n "
+                        + "qqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqq\n");
+        // System.out.println(result);
+        assertTrue(result.startsWith(">\n"));
+        assertEquals(
+                ">\n  qqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqq\n  qqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqq 111111111111111111111111\n   qqqqqqqqqqqqqqqqqqqqqqqqqqqqq qqqqqqqqqqqqqqqqqqqqqqqqqqq\n",
+                result);
+    }
+
+    public void testNoFoldedScalar() {
+        DumperOptions options = new DumperOptions();
+        options.setWidth(30);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(getData());
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("representer/scalar-style1.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testCustomScalarStyle() {
+        DumperOptions options = new DumperOptions();
+        options.setWidth(30);
+        Yaml yaml = new Yaml(new MyRepresenter(), options);
+        String output = yaml.dump(getData());
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("representer/scalar-style2.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testCustomScalarStyleNoSplitLines() {
+        DumperOptions options = new DumperOptions();
+        options.setWidth(30);
+        options.setSplitLines(false);
+        Yaml yaml = new Yaml(new MyRepresenter(), options);
+        String output = yaml.dump(getData());
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("representer/scalar-style3.yaml");
+        assertEquals(etalon, output);
+    }
+
+    private Map<String, String> getData() {
+        Map<String, String> map = new LinkedHashMap<String, String>();
+        map.put("name", "Steve Jobs");
+        map.put("address", "Name\nStreet Number\nCountry");
+        map.put("description",
+                "1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000");
+        return map;
+    }
+
+    private class MyRepresenter extends Representer {
+
+        public MyRepresenter() {
+            super();
+            this.representers.put(String.class, new FlexibleRepresent());
+        }
+
+        private class FlexibleRepresent extends RepresentString {
+            public Node representData(Object data) {
+                ScalarNode node = (ScalarNode) super.representData(data);
+                if (node.getStyle() == null) {
+                    // if Plain scalar style
+                    if (node.getValue().length() < 25) {
+                        return node;
+                    } else {
+                        // Folded scalar style
+                        return new ScalarNode(node.getTag(), node.getValue(), node.getStartMark(),
+                                node.getEndMark(), '>');
+                    }
+                } else {
+                    return node;
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStylesInJavaBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStylesInJavaBeanTest.java
new file mode 100644
index 0000000..afddbc8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue29/FlexibleScalarStylesInJavaBeanTest.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue29;
+
+import java.beans.IntrospectionException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class FlexibleScalarStylesInJavaBeanTest extends TestCase {
+    public void testDifferentStyles() {
+        BigJavaBean bean1 = new BigJavaBean(1, "simple", "line 1\nline2\nzipcode", "short text1");
+        List<Integer> numbers1 = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
+        bean1.setNumbers(numbers1);
+        Map<String, String> data1 = new HashMap<String, String>();
+        data1.put("key1", "value1");
+        data1.put("key2", "value2");
+        bean1.setData(data1);
+        //
+        BigJavaBean bean2 = new BigJavaBean(1, "second", "line 111\nline 222\nzipcode 12345\n\n",
+                "info: semicolon is used");
+        List<Integer> numbers2 = new ArrayList<Integer>(Arrays.asList(4, 5, 6, 777, 888, 999, 1000));
+        bean2.setNumbers(numbers2);
+        Map<String, String> data2 = new HashMap<String, String>();
+        data2.put("key21", "value12");
+        data2.put("key22", "value with\ntwo lines");
+        bean2.setData(data2);
+        //
+        List<BigJavaBean> list = new ArrayList<BigJavaBean>();
+        list.add(bean1);
+        list.add(bean2);
+        Yaml yaml = new Yaml(new MyRepresenter());
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        String output = yaml.dump(list);
+        // System.out.println(output);
+        // parse back
+        @SuppressWarnings("unchecked")
+        List<BigJavaBean> parsed = (List<BigJavaBean>) yaml.load(output);
+        assertEquals(2, parsed.size());
+        assertEquals(bean1, parsed.get(0));
+        assertEquals(bean2, parsed.get(1));
+
+    }
+
+    private class MyRepresenter extends Representer {
+        /*
+         * Change the default order. Important data goes first.
+         */
+        @Override
+        protected Set<Property> getProperties(Class<? extends Object> type)
+                throws IntrospectionException {
+            if (type.isAssignableFrom(BigJavaBean.class)) {
+                Set<Property> standard = super.getProperties(type);
+                Set<Property> sorted = new TreeSet<Property>(new PropertyComparator());
+                sorted.addAll(standard);
+                return sorted;
+            } else {
+                return super.getProperties(type);
+            }
+        }
+
+        private class PropertyComparator implements Comparator<Property> {
+            public int compare(Property o1, Property o2) {
+                // important go first
+                List<String> order = new ArrayList<String>(Arrays.asList("id", "name",
+                        "description", "address"));
+                for (String name : order) {
+                    int c = compareByName(o1, o2, name);
+                    if (c != 0) {
+                        return c;
+                    }
+                }
+                // all the rest
+                return o1.compareTo(o2);
+            }
+
+            private int compareByName(Property o1, Property o2, String name) {
+                if (o1.getName().equals(name)) {
+                    return -1;
+                } else if (o2.getName().equals(name)) {
+                    return 1;
+                }
+                return 0;// compare further
+            }
+        }
+
+        @Override
+        protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+                Object propertyValue, Tag customTag) {
+            if (javaBean instanceof BigJavaBean) {
+                BigJavaBean bean = (BigJavaBean) javaBean;
+                NodeTuple standard = super.representJavaBeanProperty(javaBean, property,
+                        propertyValue, customTag);
+                if (property.getName().equals("numbers")) {
+                    // when the list is small, make it block collection style
+                    if (bean.getNumbers().size() < 5) {
+                        SequenceNode n = (SequenceNode) standard.getValueNode();
+                        return new NodeTuple(standard.getKeyNode(), new SequenceNode(n.getTag(),
+                                true, n.getValue(), n.getStartMark(), n.getEndMark(), false));
+                    }
+                }
+                if (property.getName().equals("description")) {
+                    // if description contains ':' use folded scalar style and
+                    // not single quoted scalar style
+                    if (bean.getDescription().indexOf(':') > 0) {
+                        ScalarNode n = (ScalarNode) standard.getValueNode();
+                        return new NodeTuple(standard.getKeyNode(), new ScalarNode(n.getTag(),
+                                n.getValue(), n.getStartMark(), n.getEndMark(), '>'));
+                    }
+                }
+                return standard;
+            } else {
+                return super
+                        .representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue306/BeanWithId.java b/src/test/java/org/yaml/snakeyaml/issues/issue306/BeanWithId.java
new file mode 100644
index 0000000..40f849e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue306/BeanWithId.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue306;
+
+import java.util.UUID;
+
+public class BeanWithId {
+    private int value;
+    private UUID id;
+
+    public int getValue() {
+        return value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+
+    public UUID getId() {
+        return id;
+    }
+
+    public void setId(UUID id) {
+        this.id = id;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue306/UuidSupportTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue306/UuidSupportTest.java
new file mode 100644
index 0000000..3622868
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue306/UuidSupportTest.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue306;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class UuidSupportTest {
+
+    public static final Pattern UUID_PATTERN = Pattern
+            .compile("^(?:\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12})$");
+    public static final Tag UUID_TAG = new Tag(Tag.PREFIX + "java.util.UUID");
+
+    @Test
+    public void pattern() {
+        assertTrue(UUID_PATTERN.matcher("7f511847-781a-45df-9c8d-1e32e028b9b3").matches());
+        assertTrue(UUID_PATTERN.matcher("AC4877BE-0C31-4458-A86E-0272EFE1AAA8").matches());
+    }
+
+    @Test
+    public void dumpAsString() {
+        UUID uuid = UUID.randomUUID();
+        String str = uuid.toString();
+        Yaml yaml = new Yaml();
+        yaml.addImplicitResolver(UUID_TAG, UUID_PATTERN, null);
+        String output = yaml.dump(str);
+        assertEquals("'" + str + "'\n", output);
+        assertEquals(str + "\n", yaml.dump(uuid));
+    }
+
+    @Test
+    public void loadAsUuid() {
+        Yaml yaml = new Yaml();
+        yaml.addImplicitResolver(UUID_TAG, UUID_PATTERN, null);
+        UUID uuid = (UUID) yaml.load("7f511847-781a-45df-9c8d-1e32e028b9b3");
+        assertEquals("7f511847-781a-45df-9c8d-1e32e028b9b3", uuid.toString());
+    }
+
+    @Test
+    public void loadFromBean() {
+        String input = Util.getLocalResource("issues/issue306-1.yaml");
+        Yaml yaml = new Yaml();
+        BeanWithId bean = yaml.loadAs(input, BeanWithId.class);
+        assertEquals("7f511847-781a-45df-9c8d-1e32e028b9b3", bean.getId().toString());
+    }
+
+    @Test
+    public void dumpUuid() {
+        UUID uuid = UUID.randomUUID();
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(uuid);
+        assertEquals("!!java.util.UUID '" + uuid.toString() + "'\n", output);
+    }
+
+    @Test
+    public void dumpBean() {
+        BeanWithId bean = new BeanWithId();
+        bean.setValue(3);
+        UUID uuid = UUID.fromString("ac4877be-0c31-4458-a86e-0272efe1aaa8");
+        bean.setId(uuid);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAs(bean, Tag.MAP, DumperOptions.FlowStyle.BLOCK);
+        String expected = Util.getLocalResource("issues/issue306-2.yaml");
+        assertEquals(expected, output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue310/Option.java b/src/test/java/org/yaml/snakeyaml/issues/issue310/Option.java
new file mode 100644
index 0000000..9c0cddb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue310/Option.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue310;
+
+public final class Option<T> {
+
+    private final T value;
+
+    public static <T> Option<T> valueOf(T v) {
+        return new Option<T>(v);
+    }
+
+    private Option(T v) {
+        this.value = v;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue310/Person.java b/src/test/java/org/yaml/snakeyaml/issues/issue310/Person.java
new file mode 100644
index 0000000..c18cde2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue310/Person.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue310;
+
+public class Person {
+
+    private final int id;
+    private final String name;
+    private final Option<Double> income;
+
+    public Person() {
+        this(0, "", Option.valueOf(0.));
+    }
+
+    public Person(int id, String name, Option<Double> income) {
+        this.id = id;
+        this.name = name;
+        this.income = income;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Option<Double> getIncome() {
+        return income;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue310/PropertyWithPrivateCostructorTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue310/PropertyWithPrivateCostructorTest.java
new file mode 100644
index 0000000..cb52762
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue310/PropertyWithPrivateCostructorTest.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue310;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class PropertyWithPrivateCostructorTest {
+
+    public static class OptionRepresenter extends Representer {
+
+        public OptionRepresenter() {
+            this.representers.put(Option.class, new RepresentOption());
+        }
+
+        private class RepresentOption implements Represent {
+            public Node representData(Object data) {
+                Option<?> opt = (Option<?>) data;
+                return represent(opt.getValue());
+            }
+        }
+
+    }
+
+    @Test
+    public void loadFromString() {
+
+        String yamlStr = "id: 123\n" + "income: 123456.78\n" + "name: Neo Anderson";
+
+        Person loadedPerson = yaml().loadAs(yamlStr, Person.class);
+
+        assertEquals("id", loadedPerson.getId(), 123);
+        assertEquals("name", loadedPerson.getName(), "Neo Anderson");
+        assertEquals("income", loadedPerson.getIncome().getValue().doubleValue(), 123456.78, 0.);
+    }
+
+    @Test
+    public void dumpNload() {
+
+        Person person = new Person(123, "Neo Anderson", Option.valueOf(123456.78));
+
+        String dump = yaml().dumpAsMap(person);
+
+        Person loadedPerson = yaml().loadAs(dump, Person.class);
+
+        assertEquals("id", loadedPerson.getId(), 123);
+        assertEquals("name", loadedPerson.getName(), "Neo Anderson");
+        assertEquals("income", loadedPerson.getIncome().getValue().doubleValue(), 123456.78, 0.);
+    }
+
+    private Yaml yaml() {
+        Yaml _yaml = new Yaml(new OptionRepresenter());
+        _yaml.setBeanAccess(BeanAccess.FIELD);
+        return _yaml;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue311/BeanWithEnum.java b/src/test/java/org/yaml/snakeyaml/issues/issue311/BeanWithEnum.java
new file mode 100644
index 0000000..7bd5e2c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue311/BeanWithEnum.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue311;
+
+public class BeanWithEnum {
+
+    private boolean boolField;
+    private String name;
+    private BooleanEnum enumField;
+
+    public BeanWithEnum() {
+        this(true, "", BooleanEnum.UNKNOWN);
+    }
+
+    public BeanWithEnum(boolean boolField, String name, BooleanEnum enumField) {
+        this.boolField = boolField;
+        this.name = name;
+        this.enumField = enumField;
+    }
+
+    public boolean isBoolField() {
+        return boolField;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public BooleanEnum getEnumField() {
+        return enumField;
+    }
+
+    public void setBoolField(boolean boolField) {
+        this.boolField = boolField;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setEnumField(BooleanEnum enumField) {
+        this.enumField = enumField;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnum.java b/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnum.java
new file mode 100644
index 0000000..f37aabd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnum.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue311;
+
+public enum BooleanEnum {
+
+    TRUE(true), FALSE(false), UNKNOWN();
+
+    private boolean boolValue;
+    private boolean defined;
+
+    BooleanEnum(boolean p) {
+        boolValue = p;
+        defined = true;
+    }
+
+    BooleanEnum() {
+        boolValue = false;
+        defined = false;
+    }
+
+    boolean getBoolValue() {
+        if (!defined)
+            throw new IllegalArgumentException("Undefined has no value");
+        else
+            return boolValue;
+    }
+
+    boolean isDefined() {
+        return defined;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnumTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnumTest.java
new file mode 100644
index 0000000..3f0107a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue311/BooleanEnumTest.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue311;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+import static org.junit.Assert.assertEquals;
+
+public class BooleanEnumTest {
+
+    @Test
+    public void loadEnum() {
+
+        Yaml yaml = new Yaml(new MyConstructor(), new MyRepresenter());
+        BeanWithEnum parsed = yaml.loadAs("{boolField: true, enumField: true, name: '10'}", BeanWithEnum.class);
+        //System.out.println(parsed.getEnumField());
+        assertEquals(BooleanEnum.TRUE, parsed.getEnumField());
+        assertEquals("10", parsed.getName());
+    }
+
+    @Test
+    public void loadEnumUndefined() {
+
+        Yaml yaml = new Yaml(new MyConstructor(), new MyRepresenter());
+        BeanWithEnum parsed = yaml.loadAs("{boolField: true, enumField: nonsense, name: bar}", BeanWithEnum.class);
+        //System.out.println(parsed.getEnumField());
+        assertEquals(BooleanEnum.UNKNOWN, parsed.getEnumField());
+        assertEquals("bar", parsed.getName());
+    }
+
+    @Test
+    public void dumpEnum() {
+
+        BeanWithEnum bean = new BeanWithEnum(true, "10", BooleanEnum.TRUE);
+        Yaml yaml = new Yaml(new MyConstructor(), new MyRepresenter());
+        String output = yaml.dumpAs(bean, Tag.MAP, DumperOptions.FlowStyle.FLOW);
+        assertEquals("{boolField: true, enumField: 'true', name: '10'}\n", output);
+    }
+
+    class MyRepresenter extends Representer {
+        public MyRepresenter() {
+            this.representers.put(BooleanEnum.class, new RepresentEnum());
+        }
+
+        private class RepresentEnum implements Represent {
+            public Node representData(Object data) {
+                BooleanEnum myEnum = (BooleanEnum) data;
+                String value;
+                switch (myEnum) {
+                    case TRUE:
+                        value = "true";
+                        break;
+
+                    case FALSE:
+                        value = "false";
+                        break;
+
+                    case UNKNOWN:
+                        value = "unknown";
+                        break;
+
+                    default:
+                        throw new IllegalArgumentException();
+                }
+                return representScalar(Tag.STR, value);
+            }
+        }
+    }
+
+    class MyConstructor extends Constructor {
+        public MyConstructor() {
+            this.yamlClassConstructors.put(NodeId.scalar, new ConstructEnum());
+        }
+
+        private class ConstructEnum extends ConstructScalar {
+            public Object construct(Node node) {
+                if (node.getType().equals(BooleanEnum.class)) {
+                    String val = (String) constructScalar((ScalarNode) node);
+                    if ("true".equals(val)) {
+                        return BooleanEnum.TRUE;
+                    } else if ("false".equals(val)) {
+                        return BooleanEnum.FALSE;
+                    } else
+                        return BooleanEnum.UNKNOWN;
+                }
+                return super.construct(node);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue318/ContextClassLoaderTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue318/ContextClassLoaderTest.java
new file mode 100644
index 0000000..82f29ee
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue318/ContextClassLoaderTest.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue318;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Properties;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+
+public class ContextClassLoaderTest {
+
+    static public class DomainBean {
+
+        private int value = 0;
+
+        public void setValue(int value) {
+            this.value = value;
+        }
+
+        public int getValue() {
+            return value;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + value;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            DomainBean other = (DomainBean) obj;
+            if (value != other.value)
+                return false;
+            return true;
+        }
+
+    }
+
+    private URLClassLoader yamlCL;
+
+    @Before
+    public void before() throws MalformedURLException {
+        Properties classpath = new Properties();
+        InputStream cpProperties = getClass().getResourceAsStream("classpath.properties");
+        try {
+            classpath.load(cpProperties);
+        } catch (IOException e2) {
+            fail(e2.getLocalizedMessage());
+        }
+
+        File runtimeClassesDir = new File(classpath.getProperty("runtime_classes_dir"));
+
+        yamlCL = new URLClassLoader(new URL[] { runtimeClassesDir.toURI().toURL() }, null);
+    }
+
+    @After
+    public void after() {
+        // URLClassLoader.close is @since 1.7
+        // if (yamlCL != null) {
+        //   try {
+        //     yamlCL.close();
+        //   } catch (IOException e) {
+        //     e.printStackTrace();
+        //   } finally {
+        yamlCL = null;
+        //   }
+        // }
+    }
+
+    @Test(expected = ClassNotFoundException.class)
+    public void expectNoDomainClassInYamlCL() throws ClassNotFoundException {
+        yamlCL.loadClass(DomainBean.class.getName());
+    }
+
+    @Test
+    public void yamlClassInYAMLCL() throws ClassNotFoundException {
+        yamlCL.loadClass(Yaml.class.getName());
+    }
+
+    @Test
+    public void domainInDifferentConstructor() throws ClassNotFoundException,
+            InstantiationException, IllegalAccessException, NoSuchMethodException,
+            SecurityException, IllegalArgumentException, InvocationTargetException {
+
+        Class<?> yamlClass = yamlCL.loadClass(Yaml.class.getName());
+
+        DomainBean bean = new DomainBean();
+        bean.setValue(13);
+
+        Object yaml = yamlClass.newInstance();
+
+        Method dumpMethod = yaml.getClass().getMethod("dump", new Class<?>[] { Object.class });
+        String dump = dumpMethod.invoke(yaml, bean).toString();
+
+        Method loadMethod = yaml.getClass().getMethod("load", new Class<?>[] { String.class });
+        DomainBean object = (DomainBean) loadMethod.invoke(yaml, dump);
+
+        assertEquals(bean, object);
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue332/Data.java b/src/test/java/org/yaml/snakeyaml/issues/issue332/Data.java
new file mode 100644
index 0000000..56eeb4c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue332/Data.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue332;
+
+import java.beans.ConstructorProperties;
+import java.math.BigDecimal;
+
+public class Data {
+    private String label;
+
+    private String unit;
+
+    private BigDecimal value;
+
+    public BigDecimal getValue() {
+        return value;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    @ConstructorProperties({"label", "value", "unit"})
+    public Data(String label, BigDecimal value, String unit) {
+        this.label = label;
+        this.value = value;
+        this.unit = unit;
+    }
+
+//    public void setLabel(String label) {
+//        this.label = label;
+//    }
+//
+//    public void setUnit(String unit) {
+//        this.unit = unit;
+//    }
+//
+//    public void setValue(BigDecimal value) {
+//        this.value = value;
+//    }
+
+    public String getUnit() {
+        return unit;
+    }
+
+    public Data() {
+    }
+
+    @Override
+    public String toString() {
+        return "Data{" +
+                "label='" + label + '\'' +
+                ", unit='" + unit + '\'' +
+                ", value=" + value +
+                '}';
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue332/DataTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue332/DataTest.java
new file mode 100644
index 0000000..6b6098d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue332/DataTest.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue332;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.math.BigDecimal;
+
+import static org.junit.Assert.assertEquals;
+
+public class DataTest {
+
+    @Test
+    public void testGetUnit() throws Exception {
+        Data data = new Data("Voltage", BigDecimal.TEN, "V");
+        assertEquals("!!org.yaml.snakeyaml.issues.issue332.Data {}", new Yaml().dump(data).trim());
+        //TODO assertEquals("!!org.yaml.snakeyaml.issues.issue332.Data {label: Voltage, unit: V, value: !!float '10'}", new Yaml().dump(data).trim());
+    }
+
+    @Test
+    public void testLoad() throws Exception {
+        String doc = "!!org.yaml.snakeyaml.issues.issue332.Data [Voltage, 10, volts]";
+        assertEquals("Data{label='Voltage', unit='volts', value=10}", (new Yaml().load(doc)).toString());
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue38/Bean.java b/src/test/java/org/yaml/snakeyaml/issues/issue38/Bean.java
new file mode 100644
index 0000000..d644561
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue38/Bean.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue38;
+
+public class Bean {
+    int value;
+
+    public Bean() {
+    }
+
+    public Bean(int value) {
+        this.value = value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return toString().equals(obj.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return "Bean " + String.valueOf(value);
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue38/BigNumberIdTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue38/BigNumberIdTest.java
new file mode 100644
index 0000000..d80168a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue38/BigNumberIdTest.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue38;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=38
+ */
+public class BigNumberIdTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testBigNumberFormat() {
+        List<Bean> list = new ArrayList<Bean>(2000);
+        for (int i = 1; i < 1010; i++) {
+            Bean value = new Bean(i);
+            list.add(value);
+            list.add(value);
+        }
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(list);
+        // System.out.println(output);
+        //
+        List<Bean> list2 = (List<Bean>) yaml.load(output);
+        for (Bean bean : list2) {
+            assertTrue(bean.getValue() > 0);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBean.java
new file mode 100644
index 0000000..e0c7dab
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBean.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue40;
+
+import java.math.BigDecimal;
+
+public class DogFoodBean {
+    BigDecimal decimal;
+
+    public DogFoodBean() {
+        decimal = BigDecimal.ZERO;
+    }
+
+    public BigDecimal getDecimal() {
+        return decimal;
+    }
+
+    public void setDecimal(BigDecimal decimal) {
+        this.decimal = decimal;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBeanTest.java
new file mode 100644
index 0000000..f0891e1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue40/DogFoodBeanTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue40;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class DogFoodBeanTest extends TestCase {
+
+    public void testOwnBigDecimal() {
+        DogFoodBean input = new DogFoodBean();
+        input.setDecimal(new BigDecimal("5"));
+        Yaml yaml = new Yaml();
+        String text = yaml.dump(input);
+        // System.out.println(text);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue40.DogFoodBean {decimal: !!float '5'}\n",
+                text);
+        DogFoodBean output = (DogFoodBean) yaml.load(text);
+        assertEquals(output.getDecimal(), input.getDecimal());
+    }
+
+    public void testBigDecimalPrecision() {
+        DogFoodBean input = new DogFoodBean();
+        input.setDecimal(new BigDecimal("5.123"));
+        Yaml yaml = new Yaml();
+        String text = yaml.dump(input);
+        // System.out.println(text);
+        assertEquals("!!org.yaml.snakeyaml.issues.issue40.DogFoodBean {decimal: 5.123}\n", text);
+        DogFoodBean output = (DogFoodBean) yaml.load(text);
+        assertEquals(input.getDecimal(), output.getDecimal());
+    }
+
+    public void testBigDecimalNoRootTag() {
+        DogFoodBean input = new DogFoodBean();
+        input.setDecimal(new BigDecimal("5.123"));
+        Yaml yaml = new Yaml();
+        String text = yaml.dumpAsMap(input);
+        // System.out.println(text);
+        assertEquals("decimal: 5.123\n", text);
+        Yaml loader = new Yaml();
+        DogFoodBean output = loader.loadAs(text, DogFoodBean.class);
+        assertEquals(input.getDecimal(), output.getDecimal());
+    }
+
+    public void testBigDecimal1() {
+        Yaml yaml = new Yaml();
+        String text = yaml.dump(new BigDecimal("5"));
+        assertEquals("!!float '5'\n", text);
+    }
+
+    public void testBigDecimal2() {
+        Yaml yaml = new Yaml();
+        String text = yaml.dump(new BigDecimal("5.123"));
+        assertEquals("5.123\n", text);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue46/FileTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue46/FileTest.java
new file mode 100644
index 0000000..f80f3ad
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue46/FileTest.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue46;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * Issue 46: Dump a java.io.File object
+ */
+public class FileTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void test() {
+        File file = new File("src/test/resources/examples/list-bean-1.yaml");
+        assertTrue(file.exists());
+        Yaml yaml = new Yaml(new MyRepresenter());
+        Map<String, File> map = new HashMap<String, File>();
+        map.put("one", file);
+        String output = yaml.dump(map);
+        // System.out.println(output);
+        assertTrue(output, output.startsWith("{one: !!java.io.File '"));
+        assertTrue(output, output.endsWith("list-bean-1.yaml'}\n"));
+        Map<String, File> parsed = (Map<String, File>) yaml.load(output);
+        File file2 = parsed.get("one");
+        assertTrue(file2.getAbsolutePath(), file2.getAbsolutePath().endsWith("list-bean-1.yaml"));
+    }
+
+    public class MyRepresenter extends Representer {
+        public MyRepresenter() {
+            this.representers.put(File.class, new FileRepresenter());
+        }
+
+        public class FileRepresenter implements Represent {
+            public Node representData(Object data) {
+                File file = (File) data;
+                Node scalar = representScalar(new Tag("!!java.io.File"), file.getAbsolutePath());
+                return scalar;
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue47/IncompleteBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue47/IncompleteBean.java
new file mode 100644
index 0000000..197946f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue47/IncompleteBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue47;
+
+public class IncompleteBean {
+    private int id;
+    private String name;
+
+    public IncompleteBean() {
+        id = 10;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getId() {
+        return id;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue47/ReadOnlyPropertiesTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue47/ReadOnlyPropertiesTest.java
new file mode 100644
index 0000000..a99f63f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue47/ReadOnlyPropertiesTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue47;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+
+public class ReadOnlyPropertiesTest extends TestCase {
+    public void testBean1() {
+        IncompleteBean bean = new IncompleteBean();
+        bean.setName("lunch");
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals("name: lunch\n", output);
+        //
+        Yaml loader = new Yaml();
+        IncompleteBean parsed = loader.loadAs(output, IncompleteBean.class);
+        assertEquals(bean.getName(), parsed.getName());
+    }
+
+    public void testBean2() {
+        IncompleteBean bean = new IncompleteBean();
+        bean.setName("lunch");
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals("id: 10\nname: lunch\n", output);
+        //
+        Yaml loader = new Yaml();
+        try {
+            loader.loadAs(output, IncompleteBean.class);
+            fail("Setter is missing.");
+        } catch (YAMLException e) {
+            String message = e.getMessage();
+            assertTrue(
+                    message,
+                    message.contains("Unable to find property 'id' on class: org.yaml.snakeyaml.issues.issue47.IncompleteBean"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue48/Bean.java b/src/test/java/org/yaml/snakeyaml/issues/issue48/Bean.java
new file mode 100644
index 0000000..cd8ef8a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue48/Bean.java
@@ -0,0 +1,55 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue48;
+
+public class Bean {
+    private int value;
+    private String name;
+
+    public Bean() {
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return toString().equals(obj.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return "Bean " + String.valueOf(value);
+    }
+
+    public int getValue() {
+        return value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue48/SkipJavaBeanPropertyTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue48/SkipJavaBeanPropertyTest.java
new file mode 100644
index 0000000..d537454
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue48/SkipJavaBeanPropertyTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue48;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class SkipJavaBeanPropertyTest extends TestCase {
+    public void testWithNull() {
+        Bean bean = new Bean();
+        bean.setValue(3);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals("name: null\nvalue: 3\n", output);
+    }
+
+    public void testWithoutNull() {
+        Bean bean = new Bean();
+        bean.setValue(5);
+        Yaml yaml = new Yaml(new MyRepresenter());
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals("value: 5\n", output);
+    }
+
+    private class MyRepresenter extends Representer {
+        @Override
+        protected NodeTuple representJavaBeanProperty(Object bean, Property property, Object value,
+                Tag customTag) {
+            if (value != null) {
+                return super.representJavaBeanProperty(bean, property, value, customTag);
+            } else {
+                return null;
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarBean.java
new file mode 100644
index 0000000..50587eb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarBean.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue49;
+
+import java.util.Calendar;
+
+public class CalendarBean {
+    private Calendar calendar;
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Calendar getCalendar() {
+        return calendar;
+    }
+
+    public void setCalendar(Calendar calendar) {
+        this.calendar = calendar;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarTest.java
new file mode 100644
index 0000000..09af4a8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue49/CalendarTest.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue49;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class CalendarTest extends TestCase {
+    /**
+     * Daylight Saving Time is not taken into account
+     */
+    public void testDumpDstIgnored() {
+        CalendarBean bean = new CalendarBean();
+        bean.setName("lunch");
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date(1000000000000L));
+        cal.setTimeZone(TimeZone.getTimeZone("GMT-8:00"));
+        bean.setCalendar(cal);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals("calendar: 2001-09-08T17:46:40-8:00\nname: lunch\n", output);
+        //
+        Yaml loader = new Yaml();
+        CalendarBean parsed = loader.loadAs(output, CalendarBean.class);
+        assertEquals(bean.getCalendar(), parsed.getCalendar());
+    }
+
+    /**
+     * Daylight Saving Time is in effect on this date/time in
+     * America/Los_Angeles Daylight<br/>
+     * Saving Time is not in effect on this date/time in GMT
+     */
+    public void testDumpDstIsNotTheSame() {
+        check(1000000000000L, "America/Los_Angeles", "Must be 7 hours difference.",
+                "2001-09-08T18:46:40-7:00");
+    }
+
+    /**
+     * Daylight Saving Time is not in effect on this date/time in
+     * America/Los_Angeles Daylight<br/>
+     * Saving Time is not in effect on this date/time in GMT
+     */
+    public void testDumpDstIsTheSame() {
+        check(1266833741374L, "America/Los_Angeles", "Must be 8 hours difference.",
+                "2010-02-22T02:15:41.374-8:00");
+    }
+
+    /**
+     * Test odd time zone
+     */
+    public void testNepal() {
+        check(1266833741374L, "Asia/Katmandu", "Must be 5:45 hours difference.",
+                "2010-02-22T16:00:41.374+5:45");
+    }
+
+    public void testMoreThen10hours() {
+        check(1266833741374L, "Asia/Kamchatka", "Must be 12 hours difference.",
+                "2010-02-22T22:15:41.374+12:00");
+    }
+
+    private void check(long time, String timeZone, String warning, String etalon) {
+        CalendarBean bean = new CalendarBean();
+        bean.setName("lunch");
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(new Date(time));
+        cal.setTimeZone(TimeZone.getTimeZone(timeZone));
+        bean.setCalendar(cal);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals(warning, "calendar: " + etalon + "\nname: lunch\n", output);
+        //
+        Yaml loader = new Yaml();
+        CalendarBean parsed = loader.loadAs(output, CalendarBean.class);
+        assertFalse("TimeZone must deviate.", bean.getCalendar().equals(parsed.getCalendar()));
+        assertEquals(bean.getCalendar().getTimeInMillis(), parsed.getCalendar().getTimeInMillis());
+    }
+
+    public void testLoadBean() {
+        Yaml beanLoader = new Yaml();
+        CalendarBean bean = beanLoader.loadAs(
+                "calendar:  2001-12-14t21:59:43.10-05:00\nname: dinner", CalendarBean.class);
+        assertEquals("dinner", bean.getName());
+        Calendar calendar = bean.getCalendar();
+        assertEquals(TimeZone.getTimeZone("GMT-5:00").getOffset(calendar.getTime().getTime()),
+                calendar.getTimeZone().getOffset(calendar.getTime().getTime()));
+        //
+        Yaml yaml = new Yaml();
+        Date date = (Date) yaml.load("2001-12-14t21:59:43.10-05:00");
+        assertEquals(date, calendar.getTime());
+    }
+
+    public void testLoadWithTag() {
+        Yaml yaml = new Yaml();
+        GregorianCalendar calendar = (GregorianCalendar) yaml
+                .load("!!java.util.GregorianCalendar 2001-12-14t21:59:43.10-05:00");
+        assertEquals(TimeZone.getTimeZone("GMT-5:00").getOffset(calendar.getTime().getTime()),
+                calendar.getTimeZone().getOffset(calendar.getTime().getTime()));
+        //
+        Date date = (Date) yaml.load("2001-12-14t21:59:43.10-05:00");
+        assertEquals(date, calendar.getTime());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue50/SnakeyamlTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue50/SnakeyamlTest.java
new file mode 100644
index 0000000..b288f80
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue50/SnakeyamlTest.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue50;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * test issue 50.
+ */
+public class SnakeyamlTest extends TestCase {
+    public static interface SomeBean {
+        String getAttribute1();
+
+        String getAttribute2();
+    }
+
+    /* public */static abstract class BaseSomeBean implements SomeBean {
+        private String attribute1;
+
+        public String getAttribute1() {
+            return attribute1;
+        }
+
+        public void setAttribute1(String attribute1) {
+            this.attribute1 = attribute1;
+        }
+    }
+
+    public static final class SomeBeanImpl extends BaseSomeBean {
+        private String attribute2;
+
+        public SomeBeanImpl(final String attribute1, final String attribute2) {
+            setAttribute1(attribute1);
+            setAttribute2(attribute2);
+        }
+
+        public String getAttribute2() {
+            return attribute2;
+        }
+
+        public void setAttribute2(String attribute2) {
+            this.attribute2 = attribute2;
+        }
+
+        @Override
+        public String toString() {
+            return "SomeBeanImpl";
+        }
+    }
+
+    public void testIntrospector() {
+        SomeBean someBean = new SomeBeanImpl("value1", "value2");
+        Yaml dumper = new Yaml();
+        String output = dumper.dump(someBean);
+        // System.out.println(output);
+        assertEquals(
+                "!!org.yaml.snakeyaml.issues.issue50.SnakeyamlTest$SomeBeanImpl {attribute1: value1,\n  attribute2: value2}\n",
+                output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue51/UnicodeStyleTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue51/UnicodeStyleTest.java
new file mode 100644
index 0000000..4570665
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue51/UnicodeStyleTest.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue51;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * @see <a
+ *      href="http://code.google.com/p/snakeyaml/issues/detail?id=51">Issue</a>
+ */
+public class UnicodeStyleTest extends TestCase {
+    public void testFoldedStyle() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump("í");
+        // System.out.println(output);
+        assertEquals("í\n", output);
+    }
+
+    public void testDoubleQuotedStyle() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump("í");
+        // System.out.println(output);
+        assertEquals("\"í\"\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue52/LineBreakDooubleQuotedTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue52/LineBreakDooubleQuotedTest.java
new file mode 100644
index 0000000..8682fdd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue52/LineBreakDooubleQuotedTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue52;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * @see <a
+ *      href="http://code.google.com/p/snakeyaml/issues/detail?id=52">Issue</a>
+ */
+public class LineBreakDooubleQuotedTest extends TestCase {
+    public void testDoubleQuotedStyle() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        options.setWidth(20);
+        options.setIndent(4);
+        Yaml yaml = new Yaml(options);
+        String etalon = "12345678901234567890\n\n123  456";
+        String output = yaml.dump(etalon);
+        // System.out.println(output);
+        assertEquals("\"12345678901234567890\\n\\\n    \\n123  456\"\n", output);
+        String parsed = (String) yaml.load(output);
+        assertEquals(etalon, parsed);
+    }
+
+    public void testDoubleQuotedStyleNoLineSplit() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        options.setWidth(20);
+        options.setSplitLines(false);
+        options.setIndent(4);
+        Yaml yaml = new Yaml(options);
+        String etalon = "12345678901234567890\n\n123  456";
+        String output = yaml.dump(etalon);
+        // System.out.println(output);
+        assertEquals("\"12345678901234567890\\n\\n123  456\"\n", output);
+        String parsed = (String) yaml.load(output);
+        assertEquals(etalon, parsed);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue55/Blog.java b/src/test/java/org/yaml/snakeyaml/issues/issue55/Blog.java
new file mode 100644
index 0000000..228b35b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue55/Blog.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue55;
+
+import java.util.LinkedList;
+import java.util.List;
+
+class Blog {
+    private List<Post> posts = new LinkedList<Post>();
+
+    public Blog() {
+    }
+
+    public void addPost(Post p) {
+        // do some business logic here
+        posts.add(p);
+    }
+
+    public List<Post> getPosts() {
+        // in production code do not return the original set but a wrapped
+        // unmodifiable set
+        return posts;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue55/FieldListTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue55/FieldListTest.java
new file mode 100644
index 0000000..c339ef4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue55/FieldListTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue55;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+
+public class FieldListTest extends TestCase {
+
+    public void testYaml() {
+        Yaml beanLoader = new Yaml();
+        beanLoader.setBeanAccess(BeanAccess.FIELD);
+        BlogField rehydrated = beanLoader.loadAs(Util.getLocalResource("issues/issue55_2.txt"),
+                BlogField.class);
+        assertEquals(4, rehydrated.getPosts().size());
+    }
+
+    public void testFailureWithoutFieldAccess() {
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(Util.getLocalResource("issues/issue55_2.txt"), BlogField.class);
+            fail("Private field must not be available");
+        } catch (Exception e) {
+            assertTrue(e.getMessage().contains("Unable to find property 'posts'"));
+        }
+    }
+
+    public static class BlogField {
+        private List<Integer> posts;
+
+        public BlogField() {
+            posts = new LinkedList<Integer>();
+        }
+
+        public List<Integer> getPosts() {
+            return posts;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue55/JavaBeanListTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue55/JavaBeanListTest.java
new file mode 100644
index 0000000..1e8966f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue55/JavaBeanListTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue55;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+
+public class JavaBeanListTest extends TestCase {
+
+    public void testYaml() {
+        Yaml beanLoader = new Yaml();
+        beanLoader.setBeanAccess(BeanAccess.FIELD);
+        BlogBean rehydrated = (BlogBean) beanLoader.loadAs(
+                Util.getLocalResource("issues/issue55_2.txt"), BlogBean.class);
+        assertEquals(4, rehydrated.getPosts().size());
+    }
+
+    public void testFailureWithoutFieldAccess() {
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(Util.getLocalResource("issues/issue55_2.txt"), BlogBean.class);
+            fail("Private field must not be available");
+        } catch (Exception e) {
+            assertTrue(e.getMessage().contains("Unable to find property 'posts'"));
+        }
+    }
+
+    public static class BlogBean {
+        private List<Integer> posts;
+
+        public BlogBean() {
+            posts = new LinkedList<Integer>();
+        }
+
+        public List<Integer> getPosts() {
+            return posts;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue55/Post.java b/src/test/java/org/yaml/snakeyaml/issues/issue55/Post.java
new file mode 100644
index 0000000..d9e60cf
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue55/Post.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue55;
+
+class Post {
+    private String title;
+    private String text;
+    public static String description;
+    public transient String cache;
+
+    // TODO empty constructor is required to support 2 step construction
+    protected Post() {
+        description = "I should not be dumped.";
+        cache = "Q34598723SDW234";
+    }
+
+    public Post(String title, String text) {
+        this.title = title;
+        this.text = text;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getText() {
+        return text;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue55/YamlFieldAccessCollectionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue55/YamlFieldAccessCollectionTest.java
new file mode 100644
index 0000000..64c24d4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue55/YamlFieldAccessCollectionTest.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue55;
+
+import java.util.Collection;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class YamlFieldAccessCollectionTest extends TestCase {
+
+    public void testYaml() {
+        Blog original = createTestBlog();
+        Yaml yamlDumper = constructYamlDumper();
+        String serialized = yamlDumper.dumpAsMap(original);
+        // System.out.println(serialized);
+        assertEquals(Util.getLocalResource("issues/issue55_1.txt"), serialized);
+        Yaml blogLoader = new Yaml();
+        blogLoader.setBeanAccess(BeanAccess.FIELD);
+        Blog rehydrated = blogLoader.loadAs(serialized, Blog.class);
+        checkTestBlog(rehydrated);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testYamlWithoutConfiguration() {
+        Yaml yaml = new Yaml();
+        Map<String, Object> map = (Map<String, Object>) yaml.load(Util
+                .getLocalResource("issues/issue55_1.txt"));
+        assertEquals(1, map.size());
+    }
+
+    public void testYamlFailure() {
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(Util.getLocalResource("issues/issue55_1.txt"), Blog.class);
+            fail("BeanAccess.FIELD is required.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Unable to find property 'posts'"));
+        }
+    }
+
+    public void testYamlDefaultWithFeildAccess() {
+        Yaml yaml = new Yaml();
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        Blog original = createTestBlog();
+        String serialized = yaml.dump(original);
+        assertEquals(Util.getLocalResource("issues/issue55_1_rootTag.txt"), serialized);
+        Blog rehydrated = (Blog) yaml.load(serialized);
+        checkTestBlog(rehydrated);
+    }
+
+    protected Yaml constructYamlDumper() {
+        Representer representer = new Representer();
+        representer.getPropertyUtils().setBeanAccess(BeanAccess.FIELD);
+        Yaml yaml = new Yaml(representer);
+        return yaml;
+    }
+
+    protected Yaml constructYamlParser() {
+        Yaml yaml = new Yaml();
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        return yaml;
+    }
+
+    protected Blog createTestBlog() {
+        Post post1 = new Post("Test", "Dummy");
+        Post post2 = new Post("Highly", "Creative");
+        Blog blog = new Blog();
+        blog.addPost(post1);
+        blog.addPost(post2);
+        return blog;
+    }
+
+    protected void checkTestBlog(Blog blog) {
+        Collection<Post> posts = blog.getPosts();
+        assertEquals("Blog contains 2 posts", 2, posts.size());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue56/CodeBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue56/CodeBean.java
new file mode 100644
index 0000000..0004363
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue56/CodeBean.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue56;
+
+public class CodeBean {
+    private String country;
+    private String type;
+    private String value;
+    public static int counter;
+
+    public CodeBean() {
+        counter++;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public void setCountry(String country) {
+        this.country = country;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @Override
+    public String toString() {
+        return "CodeBean: " + getValue();
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue56/PerlTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue56/PerlTest.java
new file mode 100644
index 0000000..1f04b84
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue56/PerlTest.java
@@ -0,0 +1,126 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue56;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.constructor.SafeConstructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class PerlTest extends TestCase {
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public void testMaps() {
+        Yaml yaml = new Yaml(new CustomConstructor());
+        String input = Util.getLocalResource("issues/issue56-1.yaml");
+        int counter = 0;
+        for (Object obj : yaml.loadAll(input)) {
+            // System.out.println(obj);
+            Map<String, Object> map = (Map<String, Object>) obj;
+            Integer oid = (Integer) map.get("oid");
+            if (oid == 123058) {
+                ArrayList a = (ArrayList) map.get("sequences");
+                LinkedHashMap b = (LinkedHashMap) a.get(0);
+                LinkedHashMap c = (LinkedHashMap) b.get("atc");
+                LinkedHashMap d = (LinkedHashMap) c.get("name");
+                LinkedHashMap e = (LinkedHashMap) d.get("canonical");
+                String acidNameDe = e.entrySet().toArray()[1].toString();
+                assertEquals("Unicode escaped sequence must be decoded.",
+                        ":de=Acetylsalicylsäure", acidNameDe);
+            }
+            assertTrue(oid > 10000);
+            counter++;
+        }
+        assertEquals(4, counter);
+        assertEquals(0, CodeBean.counter);
+    }
+
+    private class CustomConstructor extends SafeConstructor {
+        public CustomConstructor() {
+            // define tags which begin with !org.yaml.
+            String prefix = "!de.oddb.org,2007/ODDB";
+            this.yamlMultiConstructors.put(prefix, new ConstructYamlMap());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testJavaBeanWithTypeDescription() {
+        Constructor c = new CustomBeanConstructor();
+        TypeDescription descr = new TypeDescription(CodeBean.class, new Tag(
+                "!de.oddb.org,2007/ODDB::Util::Code"));
+        c.addTypeDescription(descr);
+        Yaml yaml = new Yaml(c);
+        String input = Util.getLocalResource("issues/issue56-1.yaml");
+        int counter = 0;
+        for (Object obj : yaml.loadAll(input)) {
+            // System.out.println(obj);
+            Map<String, Object> map = (Map<String, Object>) obj;
+            Integer oid = (Integer) map.get("oid");
+            assertTrue(oid > 10000);
+            counter++;
+        }
+        assertEquals(4, counter);
+        assertEquals(55, CodeBean.counter);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testJavaBean() {
+        Constructor c = new CustomBeanConstructor();
+        Yaml yaml = new Yaml(c);
+        String input = Util.getLocalResource("issues/issue56-1.yaml");
+        int counter = 0;
+        for (Object obj : yaml.loadAll(input)) {
+            // System.out.println(obj);
+            Map<String, Object> map = (Map<String, Object>) obj;
+            Integer oid = (Integer) map.get("oid");
+            assertTrue(oid > 10000);
+            counter++;
+        }
+        assertEquals(4, counter);
+        assertEquals(55, CodeBean.counter);
+    }
+
+    private class CustomBeanConstructor extends Constructor {
+        public CustomBeanConstructor() {
+            // define tags which begin with !org.yaml.
+            String prefix = "!de.oddb.org,2007/ODDB";
+            this.yamlMultiConstructors.put(prefix, new ConstructYamlMap());
+        }
+
+        protected Construct getConstructor(Node node) {
+            if (node.getTag().equals(new Tag("!de.oddb.org,2007/ODDB::Util::Code"))) {
+                node.setUseClassConstructor(true);
+                node.setType(CodeBean.class);
+            }
+            return super.getConstructor(node);
+        }
+    }
+
+    @Override
+    protected void setUp() {
+        CodeBean.counter = 0;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue58/NullValueDumperTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue58/NullValueDumperTest.java
new file mode 100644
index 0000000..9321c02
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue58/NullValueDumperTest.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue58;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class NullValueDumperTest extends TestCase {
+
+    public static class Foo {
+        private ArrayList<Object> bar = new ArrayList<Object>();
+
+        public ArrayList<Object> getBar() {
+            return bar;
+        }
+
+        public void setBar(ArrayList<Object> bar) {
+            this.bar = bar;
+        }
+    }
+
+    public void testListElement() {
+        final Foo foo = new Foo();
+        foo.bar.add(1);
+        foo.bar.add("A");
+        foo.bar.add(3.14);
+        Yaml yaml = new Yaml();
+        assertEquals("bar:\n- 1\n- A\n- 3.14\n", yaml.dumpAsMap(foo));
+    }
+
+    public void testNullListElement() {
+        final Foo foo = new Foo();
+
+        foo.bar.add(1);
+        foo.bar.add("A");
+        foo.bar.add(null);
+        foo.bar.add(3.14);
+        Yaml yaml = new Yaml();
+        assertEquals("bar:\n- 1\n- A\n- null\n- 3.14\n", yaml.dumpAsMap(foo));
+        assertEquals(
+                "!!org.yaml.snakeyaml.issues.issue58.NullValueDumperTest$Foo\nbar: [1, A, null, 3.14]\n",
+                new Yaml().dump(foo));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java
new file mode 100644
index 0000000..0942b53
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue60/CustomOrderTest.java
@@ -0,0 +1,84 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue60;
+
+import java.beans.IntrospectionException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.introspector.PropertyUtils;
+import org.yaml.snakeyaml.representer.Representer;
+
+//issue 59
+public class CustomOrderTest extends TestCase {
+
+    public void testReversedOrder() {
+        Representer repr = new Representer();
+        repr.setPropertyUtils(new ReversedPropertyUtils());
+        Yaml yaml = new Yaml(repr);
+        String output = yaml.dump(getBean());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue59-1.yaml"), output);
+    }
+
+    private class ReversedPropertyUtils extends PropertyUtils {
+        @Override
+        protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+                throws IntrospectionException {
+            Set<Property> result = new TreeSet<Property>(Collections.reverseOrder());
+            result.addAll(super.createPropertySet(type, bAccess));
+            return result;
+        }
+    }
+
+    public void testUnsorted() {
+        Representer repr = new Representer();
+        repr.setPropertyUtils(new UnsortedPropertyUtils());
+        Yaml yaml = new Yaml(repr);
+        String output = yaml.dump(getBean());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue59-2.yaml"), output);
+    }
+
+    private class UnsortedPropertyUtils extends PropertyUtils {
+        @Override
+        protected Set<Property> createPropertySet(Class<? extends Object> type, BeanAccess bAccess)
+                throws IntrospectionException {
+            Set<Property> result = new LinkedHashSet<Property>(getPropertiesMap(type,
+                    BeanAccess.FIELD).values());
+            result.remove(result.iterator().next());// drop 'listInt' property
+            return result;
+        }
+    }
+
+    private SkipBean getBean() {
+        SkipBean bean = new SkipBean();
+        bean.setText("foo");
+        bean.setListDate(null);
+        bean.setListInt(Arrays.asList(new Integer[] { null, 1, 2, 3 }));
+        bean.setListStr(Arrays.asList(new String[] { "bar", null, "foo", null }));
+        return bean;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBean.java
new file mode 100644
index 0000000..388feed
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBean.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue60;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class SkipBean {
+
+    private List<Integer> listInt;
+    private List<String> listStr;
+    private List<Date> listDate;
+    private List<File> empty = new ArrayList<File>(0);
+    private Map<String, Integer> map = new HashMap<String, Integer>(0);
+    private String text;
+    private Integer number;
+
+    public List<Integer> getListInt() {
+        return listInt;
+    }
+
+    public void setListInt(List<Integer> listInt) {
+        this.listInt = listInt;
+    }
+
+    public List<String> getListStr() {
+        return listStr;
+    }
+
+    public void setListStr(List<String> listStr) {
+        this.listStr = listStr;
+    }
+
+    public List<Date> getListDate() {
+        return listDate;
+    }
+
+    public void setListDate(List<Date> listDate) {
+        this.listDate = listDate;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public void setNumber(Integer number) {
+        this.number = number;
+    }
+
+    public List<File> getEmpty() {
+        return empty;
+    }
+
+    public void setEmpty(List<File> empty) {
+        this.empty = empty;
+    }
+
+    public Map<String, Integer> getMap() {
+        return map;
+    }
+
+    public void setMap(Map<String, Integer> map) {
+        this.map = map;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java
new file mode 100644
index 0000000..8cb6e0e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue60/SkipBeanTest.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue60;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.CollectionNode;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class SkipBeanTest extends TestCase {
+
+    public void testSkipNull() {
+        Yaml yaml = new Yaml(new SkipNullRepresenter());
+        String output = yaml.dump(getBean());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue60-1.yaml"), output);
+    }
+
+    private class SkipNullRepresenter extends Representer {
+        @Override
+        protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+                Object propertyValue, Tag customTag) {
+            if (propertyValue == null) {
+                return null;
+            } else {
+                return super
+                        .representJavaBeanProperty(javaBean, property, propertyValue, customTag);
+            }
+        }
+    }
+
+    public void testSkipEmptyCollections() {
+        Yaml yaml = new Yaml(new SkipEmptyRepresenter());
+        String output = yaml.dump(getBean());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue60-2.yaml"), output);
+    }
+
+    private class SkipEmptyRepresenter extends Representer {
+        @Override
+        protected NodeTuple representJavaBeanProperty(Object javaBean, Property property,
+                Object propertyValue, Tag customTag) {
+            NodeTuple tuple = super.representJavaBeanProperty(javaBean, property, propertyValue,
+                    customTag);
+            Node valueNode = tuple.getValueNode();
+            if (Tag.NULL.equals(valueNode.getTag())) {
+                return null;// skip 'null' values
+            }
+            if (valueNode instanceof CollectionNode) {
+                if (Tag.SEQ.equals(valueNode.getTag())) {
+                    SequenceNode seq = (SequenceNode) valueNode;
+                    if (seq.getValue().isEmpty()) {
+                        return null;// skip empty lists
+                    }
+                }
+                if (Tag.MAP.equals(valueNode.getTag())) {
+                    MappingNode seq = (MappingNode) valueNode;
+                    if (seq.getValue().isEmpty()) {
+                        return null;// skip empty maps
+                    }
+                }
+            }
+            return tuple;
+        }
+    }
+
+    private SkipBean getBean() {
+        SkipBean bean = new SkipBean();
+        bean.setText("foo");
+        bean.setListDate(null);
+        bean.setListInt(Arrays.asList(new Integer[] { null, 1, 2, 3 }));
+        bean.setListStr(Arrays.asList(new String[] { "bar", null, "foo", null }));
+        return bean;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericListBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericListBeanTest.java
new file mode 100644
index 0000000..f872aae
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericListBeanTest.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue61;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericListBeanTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testGenericList() {
+        Yaml yaml = new Yaml();
+        ListProvider<String> listProvider = new ListProvider<String>();
+        listProvider.getList().add("foo");
+        listProvider.getList().add("bar");
+        String s = yaml.dumpAsMap(listProvider);
+        // System.out.println(s);
+        assertEquals("list:\n- foo\n- bar\n", s);
+        // parse
+        Yaml loader = new Yaml();
+        ListProvider<String> listProvider2 = loader.loadAs(s, ListProvider.class);
+        assertEquals("foo", listProvider2.getList().get(0));
+        assertEquals("bar", listProvider2.getList().get(1));
+        assertEquals(listProvider, listProvider2);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public void testGenericBean() {
+        Yaml yaml = new Yaml();
+        ListProvider<Bean> listProvider = new ListProvider<Bean>();
+        Bean foo = new Bean();
+        foo.setName("foo");
+        listProvider.getList().add(foo);
+        Bean bar = new Bean();
+        bar.setName("bar");
+        bar.setNumber(3);
+        listProvider.getList().add(bar);
+        String s = yaml.dumpAsMap(listProvider);
+        // System.out.println(s);
+        String etalon = Util.getLocalResource("issues/issue61-1.yaml");
+        assertEquals(etalon, s);
+        // parse
+        Yaml loader = new Yaml();
+        ListProvider listProvider2 = loader.loadAs(s, ListProvider.class);
+        Bean foo2 = (Bean) listProvider2.getList().get(0);
+        assertEquals("foo", foo2.getName());
+        assertEquals(0, foo2.getNumber());
+        Bean bar2 = (Bean) listProvider2.getList().get(1);
+        assertEquals("bar", bar2.getName());
+        assertEquals(3, bar2.getNumber());
+    }
+
+    public static class ListProvider<T> {
+        private List<T> list = new ArrayList<T>();
+
+        public List<T> getList() {
+            return list;
+        }
+
+        public void setList(List<T> list) {
+            this.list = list;
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ListProvider) {
+                return list.equals(((ListProvider) obj).getList());
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return list.hashCode();
+        }
+    }
+
+    public static class Bean {
+        private String name;
+        private int number;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java
new file mode 100644
index 0000000..fd1eff2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue61/GenericMapBeanTest.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue61;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class GenericMapBeanTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    public void testGenericMap() {
+        Yaml yaml = new Yaml();
+        MapProvider<String, Integer> listProvider = new MapProvider<String, Integer>();
+        listProvider.getMap().put("foo", 17);
+        listProvider.getMap().put("bar", 19);
+        String s = yaml.dumpAsMap(listProvider);
+        // System.out.println(s);
+        assertEquals("map:\n  foo: 17\n  bar: 19\n", s);
+        // parse
+        Yaml loader = new Yaml();
+        MapProvider<String, Integer> listProvider2 = loader.loadAs(s, MapProvider.class);
+        assertEquals(new Integer(17), listProvider2.getMap().get("foo"));
+        assertEquals(new Integer(19), listProvider2.getMap().get("bar"));
+        assertEquals(listProvider, listProvider2);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public void testGenericBean() {
+        Yaml yaml = new Yaml();
+        MapProvider<String, Bean> listProvider = new MapProvider<String, Bean>();
+        Bean foo = new Bean();
+        foo.setName("foo");
+        listProvider.getMap().put("foo", foo);
+        Bean bar = new Bean();
+        bar.setName("bar");
+        bar.setNumber(3);
+        listProvider.getMap().put("bar", bar);
+        String s = yaml.dumpAsMap(listProvider);
+        // System.out.println(s);
+        String etalon = Util.getLocalResource("issues/issue61-2.yaml");
+        assertEquals(etalon, s);
+        // parse
+        Yaml loader = new Yaml();
+        MapProvider listProvider2 = loader.loadAs(s, MapProvider.class);
+        Bean foo2 = (Bean) listProvider2.getMap().get("foo");
+        assertEquals("foo", foo2.getName());
+        assertEquals(0, foo2.getNumber());
+        Bean bar2 = (Bean) listProvider2.getMap().get("bar");
+        assertEquals("bar", bar2.getName());
+        assertEquals(3, bar2.getNumber());
+    }
+
+    public static class MapProvider<K, V> {
+        private Map<K, V> map = new LinkedHashMap<K, V>();
+
+        public Map<K, V> getMap() {
+            return map;
+        }
+
+        public void setMap(Map<K, V> map) {
+            this.map = map;
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof MapProvider) {
+                return map.equals(((MapProvider) obj).getMap());
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return map.hashCode();
+        }
+    }
+
+    public static class Bean {
+        private String name;
+        private int number;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java b/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java
new file mode 100644
index 0000000..74ea7f5
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue64/MethodDesc.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue64;
+
+import java.util.List;
+
+public class MethodDesc {
+    private String name;
+    private List<Class<?>> argTypes;
+
+    public MethodDesc() {
+    }
+
+    public MethodDesc(String name, List<Class<?>> argTypes) {
+        this.name = name;
+        this.argTypes = argTypes;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public List<Class<?>> getArgTypes() {
+        return argTypes;
+    }
+
+    public void setArgTypes(List<Class<?>> argTypes) {
+        this.argTypes = argTypes;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java
new file mode 100644
index 0000000..50c7bc8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue64/ParameterizedTypeTest.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue64;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ParameterizedTypeTest extends TestCase {
+
+    public void testRepresenter() {
+        Yaml yaml = new Yaml(new ClassConstructor(), new ClassRepresenter());
+
+        String methodName = "testMethod";
+        List<Class<?>> argTypes = new LinkedList<Class<?>>();
+        argTypes.add(String.class);
+        argTypes.add(Integer.class);
+        argTypes.add(Boolean.class);
+        MethodDesc methodDesc = new MethodDesc(methodName, argTypes);
+
+        String out = yaml.dump(methodDesc);
+        // System.out.println(out);
+        assertEquals(
+                "!!org.yaml.snakeyaml.issues.issue64.MethodDesc\nargTypes: [!clazz 'String', !clazz 'Integer', !clazz 'Boolean']\nname: testMethod\n",
+                out);
+        MethodDesc parsed = (MethodDesc) yaml.load(out);
+        assertEquals(methodName, parsed.getName());
+        List<Class<?>> argTypes2 = parsed.getArgTypes();
+        assertEquals(3, argTypes2.size());
+        assertEquals(argTypes, argTypes2);
+    }
+
+    static class ClassRepresenter extends Representer {
+        public ClassRepresenter() {
+            this.representers.put(Class.class, new RepresentClass());
+        }
+
+        private class RepresentClass implements Represent {
+            public Node representData(Object data) {
+                Class<?> clazz = (Class<?>) data;
+                return representScalar(new Tag("!clazz"), clazz.getSimpleName());
+            }
+        }
+    }
+
+    static class ClassConstructor extends Constructor {
+        public ClassConstructor() {
+            this.yamlConstructors.put(new Tag("!clazz"), new ConstructClass());
+        }
+
+        private class ConstructClass extends AbstractConstruct {
+
+            public Object construct(Node node) {
+                String clazz = (String) constructScalar((ScalarNode) node);
+                try {
+                    return Class.forName("java.lang." + clazz);
+                } catch (ClassNotFoundException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue67/NonAsciiCharsInClassNameTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue67/NonAsciiCharsInClassNameTest.java
new file mode 100644
index 0000000..1219173
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue67/NonAsciiCharsInClassNameTest.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue67;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+import org.yaml.snakeyaml.scanner.ScannerException;
+
+public class NonAsciiCharsInClassNameTest extends TestCase {
+    private String PREFIX = "!!org.yaml.snakeyaml.issues.issue67.NonAsciiCharsInClassNameTest$";
+
+    public void testDump() {
+        Académico obj = new Académico();
+        obj.setId(1);
+        obj.setName("Foo bar baz");
+        Yaml yaml = new Yaml();
+        String result = yaml.dump(obj);
+        assertEquals(PREFIX + "Acad%C3%A9mico {\n  id: 1, name: Foo bar baz}\n", result);
+    }
+
+    public void testLoad() {
+        Yaml yaml = new Yaml();
+        Académico obj = (Académico) yaml.load(PREFIX + "Acad%C3%A9mico {id: 3, name: Foo bar}");
+        assertEquals(3, obj.getId());
+        assertEquals("Foo bar", obj.getName());
+    }
+
+    public void testLoadInvalidPattern() {
+        try {
+            Yaml yaml = new Yaml();
+            yaml.load(PREFIX + "Acad%WZ%A9mico {id: 3, name: Foo bar}");
+            fail("Illegal hex characters in escape (%) pattern must not be accepted.");
+        } catch (Exception e) {
+            assertEquals(
+                    "while scanning a tag\n"
+                            + " in 'string', line 1, column 1:\n"
+                            + "    !!org.yaml.snakeyaml.issues.issu ... \n"
+                            + "    ^\n"
+                            + "expected URI escape sequence of 2 hexadecimal numbers, but found W(87) and Z(90)\n"
+                            + " in 'string', line 1, column 71:\n"
+                            + "     ... nAsciiCharsInClassNameTest$Acad%WZ%A9mico {id: 3, name: Foo bar}\n"
+                            + "                                         ^\n", e.getMessage());
+        }
+    }
+
+    public static class Académico {
+        public int getId() {
+            return id;
+        }
+
+        public void setId(int id) {
+            this.id = id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        private int id;
+        private String name;
+    }
+
+    public void testDumpCustomTag() {
+        Académico obj = new Académico();
+        obj.setId(123);
+        obj.setName("Foo bar 123");
+        Representer repr = new Representer();
+        repr.addClassTag(Académico.class, new Tag("!foo"));
+        Yaml yaml = new Yaml(repr);
+        String result = yaml.dump(obj);
+        assertEquals("!foo {id: 123, name: Foo bar 123}\n", result);
+    }
+
+    public void testDumpEscapedTag() {
+        Académico obj = new Académico();
+        obj.setId(123);
+        obj.setName("Foo bar 123");
+        Representer repr = new Representer();
+        repr.addClassTag(Académico.class, new Tag("!Académico"));
+        Yaml yaml = new Yaml(repr);
+        String result = yaml.dump(obj);
+        assertEquals("!Acad%C3%A9mico {id: 123, name: Foo bar 123}\n", result);
+    }
+
+    public void testTag() {
+        Tag tag = new Tag("!java/javabean:foo.Bar");
+        assertEquals("!java/javabean:foo.Bar", tag.getValue());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue68/NonAsciiCharacterTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue68/NonAsciiCharacterTest.java
new file mode 100644
index 0000000..9915abc
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue68/NonAsciiCharacterTest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue68;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.YamlDocument;
+
+public class NonAsciiCharacterTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testLoad() {
+        Yaml yaml = new Yaml();
+        Map<String, Map<String, String>> obj = (Map<String, Map<String, String>>) yaml
+                .load("test.string: {en: И}");
+        assertEquals(1, obj.size());
+        assertEquals("Map: " + obj.toString(), "И", obj.get("test.string").get("en"));
+    }
+
+    public void testLoadFromFileWithWrongEncoding() {
+        try {
+            Yaml yaml = new Yaml();
+            InputStream input = new FileInputStream("src/test/resources/issues/issue68.txt");
+            CharsetDecoder decoder = Charset.forName("Cp1252").newDecoder();
+            decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+            Object text = yaml.load(new InputStreamReader(input, decoder));
+            input.close();
+            fail("Invalid UTF-8 must not be accepted: " + text.toString());
+        } catch (Exception e) {
+            assertTrue(e.getMessage().endsWith("Exception: Input length = 1"));
+        }
+    }
+
+    public void testLoadFromFile() throws UnsupportedEncodingException, FileNotFoundException {
+        Yaml yaml = new Yaml();
+        InputStream input = new FileInputStream("src/test/resources/issues/issue68.txt");
+        String text = (String) yaml.load(new InputStreamReader(input, "UTF-8"));
+        assertEquals("И жить торопится и чувствовать спешит...", text);
+    }
+
+    public void testLoadFromInputStream() throws IOException {
+        InputStream input;
+        input = YamlDocument.class.getClassLoader().getResourceAsStream("issues/issue68.txt");
+        if (input == null) {
+            throw new RuntimeException("Can not find issues/issue68.txt");
+        }
+        Yaml yaml = new Yaml();
+        String text = (String) yaml.load(input);// UTF-8 by default
+        assertEquals("И жить торопится и чувствовать спешит...", text);
+        input.close();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue72/CollectionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue72/CollectionTest.java
new file mode 100644
index 0000000..ca817e2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue72/CollectionTest.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue72;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class CollectionTest extends TestCase {
+
+    public void testCollectionList() {
+        CollectionList bean = new CollectionList();
+        Yaml yaml = new Yaml();
+        String doc = yaml.dumpAsMap(bean);
+        // System.out.println(doc);
+        Yaml beanLoader = new Yaml();
+        CollectionList parsed = beanLoader.loadAs(doc, CollectionList.class);
+        assertTrue(parsed.getNames().contains("aaa"));
+        assertTrue(parsed.getNames().contains("bbb"));
+        assertEquals(2, parsed.getNames().size());
+    }
+
+    public static class CollectionList {
+        private Collection<String> names;
+
+        public CollectionList() {
+            names = new ArrayList<String>();
+            names.add("aaa");
+            names.add("bbb");
+        }
+
+        public Collection<String> getNames() {
+            return names;
+        }
+
+        public void setNames(Collection<String> names) {
+            this.names = names;
+        }
+    }
+
+    public void testCollectionSet() {
+        CollectionSet bean = new CollectionSet();
+        Yaml yaml = new Yaml();
+        String doc = yaml.dumpAsMap(bean);
+        // System.out.println(doc);
+        Yaml beanLoader = new Yaml();
+        CollectionSet parsed = beanLoader.loadAs(doc, CollectionSet.class);
+        assertTrue(parsed.getRoles().contains(11));
+        assertTrue(parsed.getRoles().contains(13));
+        assertEquals(2, parsed.getRoles().size());
+    }
+
+    public static class CollectionSet {
+        private Collection<Integer> roles;
+
+        public CollectionSet() {
+            roles = new HashSet<Integer>();
+            roles.add(11);
+            roles.add(13);
+        }
+
+        public Collection<Integer> getRoles() {
+            return roles;
+        }
+
+        public void setRoles(Collection<Integer> roles) {
+            this.roles = roles;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java
new file mode 100644
index 0000000..b86c6b4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/ArrayListTest.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test bean when the implementation is defined: ArrayList instead of just the
+ * interface List
+ */
+public class ArrayListTest extends TestCase {
+    public void testListImplementation() {
+        Bean1 bean = new Bean1();
+        bean.setId("ID123");
+        ArrayList<String> list = new ArrayList<String>(3);
+        list.add("zzz");
+        list.add("xxx");
+        list.add("ccc");
+        bean.setList(list);
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(bean);
+        // System.out.println(doc);
+        Bean1 loaded = (Bean1) yaml.load(doc);
+        assertEquals(3, loaded.getList().size());
+        assertEquals(ArrayList.class, loaded.getList().getClass());
+    }
+
+    public static class Bean1 {
+        private ArrayList<String> list;
+        private String id;
+
+        public ArrayList<String> getList() {
+            return list;
+        }
+
+        public void setList(ArrayList<String> list) {
+            this.list = list;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java
new file mode 100644
index 0000000..1e0be23
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/Blog.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class Blog {
+
+    private String name;
+    private Set<Post> posts = new TreeSet<Post>();
+    public Set<Integer> numbers = new LinkedHashSet<Integer>();
+    private TreeSet<String> labels = new TreeSet<String>();
+
+    public Blog() {
+        name = "SuperBlog";
+    }
+
+    public Blog(String name) {
+        this.name = name;
+    }
+
+    public void addPost(Post p) {
+        posts.add(p);
+    }
+
+    public Set<Post> getPosts() {
+        return posts;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setPosts(Set<Post> posts) {
+        this.posts = posts;
+    }
+
+    public TreeSet<String> getLabels() {
+        return labels;
+    }
+
+    public void setLabels(TreeSet<String> labels) {
+        this.labels = labels;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return name.equals(obj.toString());
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Blog '" + name + "'";
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/DumpSetAsSequenceExampleTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/DumpSetAsSequenceExampleTest.java
new file mode 100644
index 0000000..a656a68
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/DumpSetAsSequenceExampleTest.java
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class DumpSetAsSequenceExampleTest extends TestCase {
+
+    public void testDumpFlow() {
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(new SetRepresenter(), options);
+        String output = yaml.dump(createBlog());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue73-dump7.txt"), output);
+        //
+        check(output);
+    }
+
+    public void testDumpBlock() {
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(new SetRepresenter(), options);
+        String output = yaml.dump(createBlog());
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue73-dump8.txt"), output);
+        //
+        check(output);
+    }
+
+    private class SetRepresenter extends Representer {
+        public SetRepresenter() {
+            this.multiRepresenters.put(Set.class, new RepresentIterable());
+        }
+
+        private class RepresentIterable implements Represent {
+            @SuppressWarnings("unchecked")
+            public Node representData(Object data) {
+                return representSequence(getTag(data.getClass(), Tag.SEQ), (Iterable<Object>) data,
+                        null);
+
+            }
+        }
+    }
+
+    private Blog createBlog() {
+        Blog blog = new Blog("Test Me!");
+        blog.addPost(new Post("Title1", "text 1"));
+        blog.addPost(new Post("Title2", "text text 2"));
+        blog.numbers.add(19);
+        blog.numbers.add(17);
+        TreeSet<String> labels = new TreeSet<String>();
+        labels.add("Java");
+        labels.add("YAML");
+        labels.add("SnakeYAML");
+        blog.setLabels(labels);
+        return blog;
+    }
+
+    private void check(String doc) {
+        Yaml yamlLoader = new Yaml();
+        yamlLoader.setBeanAccess(BeanAccess.FIELD);
+        Blog blog = (Blog) yamlLoader.load(doc);
+        assertEquals("Test Me!", blog.getName());
+        assertEquals(2, blog.numbers.size());
+        assertEquals(2, blog.getPosts().size());
+        for (Post post : blog.getPosts()) {
+            assertEquals(Post.class, post.getClass());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java
new file mode 100644
index 0000000..1739549
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/Post.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+class Post implements Comparable<Post> {
+
+    private String title;
+    private String text;
+
+    protected Post() {
+    }
+
+    public Post(String title, String text) {
+        super();
+        this.title = title;
+        this.text = text;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public int compareTo(Post o) {
+        return title.compareTo(o.title);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Post) {
+            return toString().equals(obj.toString());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Post " + title + " " + text;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java
new file mode 100644
index 0000000..ef8b01f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSetTest.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class RecursiveSetTest extends TestCase {
+    public void testDumpException() {
+        Set<Object> set1 = new HashSet<Object>();
+        Set<Object> set2 = new HashSet<Object>();
+        set1.add(set2);
+        set2.add(set1);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(set1);
+            fail("Recursive sets are not supported.");
+        } catch (StackOverflowError e) {
+            assertEquals(null, e.getMessage());
+        }
+    }
+
+    public void testLoadException() {
+        String doc = Util.getLocalResource("issues/issue73-recursive4.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(doc);
+            fail("Recursive sets are not supported.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Set cannot be recursive."));
+        }
+    }
+
+    /**
+     * XXX: sets can be recursive
+     */
+    @SuppressWarnings("unchecked")
+    public void testLoadRecursiveTest() {
+        String doc = Util.getLocalResource("issues/issue73-recursive5.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        Bean1 obj = (Bean1) yaml.load(doc);
+        Set<Object> set = obj.getSet();
+        // System.out.println(set);
+        assertEquals(LinkedHashSet.class, set.getClass());
+        assertEquals("ID123", obj.getId());
+        assertEquals(3, set.size());
+        assertTrue(set.remove("zzz"));
+        assertTrue(set.remove("ccc"));
+        assertFalse(set.contains("111"));
+        try {
+            set.contains(set);
+            fail("Recursive set fails to provide a hashcode.");
+        } catch (StackOverflowError e) {
+            // ignore
+        }
+        //
+        Set<Object> self = (Set<Object>) set.iterator().next();
+        assertEquals(LinkedHashSet.class, self.getClass());
+        assertEquals(set, self);
+        assertSame(set, self);
+        assertEquals(1, set.size());
+        assertEquals(1, self.size());
+        set.add("111");
+        assertEquals(2, set.size());
+        assertEquals(2, self.size());
+        //
+        self.clear();
+        assertTrue(self.isEmpty());
+        assertTrue(set.isEmpty());
+        assertFalse("Now it should not be recursive any longer (no StackOverflowError).",
+                set.contains(set));
+        //
+        set.add("jjj");
+        assertEquals(1, set.size());
+        assertEquals(1, self.size());
+    }
+
+    public static class Bean1 {
+        private Set<Object> set;
+        private String id;
+
+        public Set<Object> getSet() {
+            return set;
+        }
+
+        public void setSet(Set<Object> set) {
+            this.set = set;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSortedSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSortedSetTest.java
new file mode 100644
index 0000000..5224429
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/RecursiveSortedSetTest.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class RecursiveSortedSetTest extends TestCase {
+    public void testDumpException() {
+        SortedSet<Object> set = new TreeSet<Object>();
+        Bean11 bean = new Bean11();
+        bean.setId("ID555");
+        bean.setSet(set);
+        set.add("ggg");
+        set.add("hhh");
+        set.add(bean);
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(bean);
+        // System.out.println(doc);
+        assertEquals(Util.getLocalResource("issues/issue73-recursive9.txt"), doc);
+    }
+
+    public void testLoadException() {
+        String doc = Util.getLocalResource("issues/issue73-recursive10.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load(doc);
+            fail("Recursive sets are not supported.");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Set cannot be recursive."));
+        }
+    }
+
+    /**
+     * set and JavaBean refer to each other
+     */
+    public void testLoadRecursiveTest() {
+        String doc = Util.getLocalResource("issues/issue73-recursive9.txt");
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        Bean11 beanWithSet = (Bean11) yaml.load(doc);
+        Set<Object> set = beanWithSet.getSet();
+        assertEquals(TreeSet.class, set.getClass());
+        assertEquals("ID555", beanWithSet.getId());
+        assertEquals(3, set.size());
+        assertTrue(set.remove("ggg"));
+        // assertFalse(set.remove("ggg"));???
+        assertTrue(set.remove("hhh"));
+        assertEquals(1, set.size());
+        //
+        Bean11 beanRef = (Bean11) set.iterator().next();
+        assertEquals(beanWithSet, beanRef);
+        assertSame(beanWithSet, beanRef);
+        //
+        assertFalse(set.isEmpty());
+        assertTrue(set.contains(beanWithSet));
+        assertFalse(set.add(beanWithSet));
+        assertTrue(set.remove(beanWithSet));
+        assertFalse(set.remove(beanWithSet));
+        assertTrue(set.isEmpty());
+    }
+
+    public static class Bean11 implements Comparable<Object> {
+        private SortedSet<Object> set;
+        private String id;
+
+        public SortedSet<Object> getSet() {
+            return set;
+        }
+
+        public void setSet(SortedSet<Object> set) {
+            this.set = set;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public int compareTo(Object o) {
+            return toString().compareTo(o.toString());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Bean11) {
+                Bean11 b = (Bean11) obj;
+                return id.equals(b.id);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return toString().hashCode();
+        }
+
+        @Override
+        public String toString() {
+            return "Bean id=" + id + "set=" + System.identityHashCode(set);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java
new file mode 100644
index 0000000..7d3b4af
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/SetAsSequenceTest.java
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+
+public class SetAsSequenceTest extends TestCase {
+
+    public void testDump() {
+        Blog blog = new Blog("Test Me!");
+        blog.addPost(new Post("Title1", "text 1"));
+        blog.addPost(new Post("Title2", "text text 2"));
+        blog.numbers.add(19);
+        blog.numbers.add(17);
+        TreeSet<String> labels = new TreeSet<String>();
+        labels.add("Java");
+        labels.add("YAML");
+        labels.add("SnakeYAML");
+        blog.setLabels(labels);
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(blog);
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue73-1.txt"), output);
+    }
+
+    public void testLoad() {
+        Yaml yaml = new Yaml();
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        String doc = Util.getLocalResource("issues/issue73-1.txt");
+        Blog blog = (Blog) yaml.load(doc);
+        // System.out.println(blog);
+        assertEquals("Test Me!", blog.getName());
+        assertEquals(2, blog.numbers.size());
+        assertEquals(2, blog.getPosts().size());
+        for (Post post : blog.getPosts()) {
+            assertEquals(Post.class, post.getClass());
+        }
+    }
+
+    public void testYaml() {
+        String serialized = Util.getLocalResource("issues/issue73-2.txt");
+        // System.out.println(serialized);
+        Yaml beanLoader = new Yaml();
+        beanLoader.setBeanAccess(BeanAccess.FIELD);
+        Blog rehydrated = beanLoader.loadAs(serialized, Blog.class);
+        checkTestBlog(rehydrated);
+    }
+
+    protected void checkTestBlog(Blog blog) {
+        Set<Post> posts = blog.getPosts();
+        assertEquals("Blog contains 2 posts", 2, posts.size());
+        assertTrue(posts.contains(new Post("Test", "Dummy")));
+        assertTrue(posts.contains(new Post("Highly", "Creative")));
+        assertEquals("No tags!", blog.getName());
+        assertEquals(0, blog.numbers.size());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadRootSet() {
+        Yaml yaml = new Yaml();
+        String doc = Util.getLocalResource("issues/issue73-3.txt");
+        Set<String> strings = (Set<String>) yaml.load(doc);
+        // System.out.println(strings);
+        assertEquals(3, strings.size());
+        assertEquals(HashSet.class, strings.getClass());
+        assertTrue(strings.contains("aaa"));
+        assertTrue(strings.contains("bbb"));
+        assertTrue(strings.contains("ccc"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadRootSet2() {
+        Yaml yaml = new Yaml();
+        String doc = "!!java.util.HashSet {aaa: null, bbb: null, ccc: null}";
+        Set<String> strings = (Set<String>) yaml.load(doc);
+        // System.out.println(strings);
+        assertEquals(3, strings.size());
+        assertEquals(LinkedHashSet.class, strings.getClass());
+        assertTrue(strings.contains("aaa"));
+        assertTrue(strings.contains("bbb"));
+        assertTrue(strings.contains("ccc"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadRootSet3() {
+        Yaml yaml = new Yaml();
+        String doc = "!!java.util.TreeSet {aaa: null, bbb: null, ccc: null}";
+        Set<String> strings = (Set<String>) yaml.load(doc);
+        // System.out.println(strings);
+        assertEquals(3, strings.size());
+        assertEquals(TreeSet.class, strings.getClass());
+        assertTrue(strings.contains("aaa"));
+        assertTrue(strings.contains("bbb"));
+        assertTrue(strings.contains("ccc"));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testLoadRootSet6() {
+        Yaml yaml = new Yaml();
+        String doc = Util.getLocalResource("issues/issue73-6.txt");
+        Set<String> strings = (Set<String>) yaml.load(doc);
+        // System.out.println(strings);
+        assertEquals(3, strings.size());
+        assertEquals(TreeSet.class, strings.getClass());
+        assertTrue(strings.contains("aaa"));
+        assertTrue(strings.contains("bbb"));
+        assertTrue(strings.contains("ccc"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java
new file mode 100644
index 0000000..7d4a3e0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue73/TreeSetTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue73;
+
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test bean when the implementation is defined: TreeSet instead of just the
+ * interface Set
+ */
+public class TreeSetTest extends TestCase {
+    public void testSetImplementation() {
+        Bean1 bean = new Bean1();
+        bean.setId("ID123");
+        TreeSet<String> list = new TreeSet<String>();
+        list.add("zzz");
+        list.add("xxx");
+        list.add("ccc");
+        bean.setSet(list);
+        Yaml yaml = new Yaml();
+        String doc = yaml.dump(bean);
+        // System.out.println(doc);
+        //
+        Bean1 loaded = (Bean1) yaml.load(doc);
+        assertEquals(3, loaded.getSet().size());
+        assertEquals(TreeSet.class, loaded.getSet().getClass());
+        assertTrue(loaded.getSet().contains("zzz"));
+        assertTrue(loaded.getSet().contains("xxx"));
+        assertTrue(loaded.getSet().contains("ccc"));
+    }
+
+    public static class Bean1 {
+        private TreeSet<String> set;
+        private String id;
+
+        public TreeSet<String> getSet() {
+            return set;
+        }
+
+        public void setSet(TreeSet<String> set) {
+            this.set = set;
+        }
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue74/ArrayBeanTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue74/ArrayBeanTest.java
new file mode 100644
index 0000000..716294e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue74/ArrayBeanTest.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue74;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class ArrayBeanTest extends TestCase {
+
+    public void testArrayProperty() {
+        ArrayMember[] members = new ArrayMember[3];
+        members[0] = new ArrayMember("Foo", 21);
+        members[1] = new ArrayMember("Bar", 23);
+        members[2] = new ArrayMember("Hue Long Hair", 25);
+        ArrayBean bean = new ArrayBean();
+        bean.setId("ID123");
+        bean.setNumber(7);
+        bean.setMembers(members);
+        bean.openMembers = new ArrayMember[] { new ArrayMember("OpenFoo", 1000),
+                new ArrayMember("OpenBar", 2000) };
+        List<ArrayMember> list = new ArrayList<ArrayMember>(2);
+        list.add(new ArrayMember("John", 111));
+        list.add(new ArrayMember("Tony", 222));
+        bean.setList(list);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(bean);
+        // System.out.println(output);
+        assertEquals(Util.getLocalResource("issues/issue74-array1.txt"), output);
+        Yaml beanLoader = new Yaml();
+        ArrayBean parsed = beanLoader.loadAs(output, ArrayBean.class);
+        // System.out.println(parsed);
+        assertEquals(3, parsed.getMembers().length);
+        assertEquals(2, parsed.openMembers.length);
+        assertEquals(2, parsed.getList().size());
+        assertEquals("ID123", parsed.getId());
+        assertEquals(7, parsed.getNumber());
+        for (ArrayMember member : parsed.getMembers()) {
+            assertTrue((member.getAge() >= 21) && (member.getAge() <= 25));
+        }
+    }
+
+    public static class ArrayBean {
+        private String id;
+        private int number;
+        private ArrayMember[] members;
+        public ArrayMember[] openMembers;
+        private List<ArrayMember> list;
+
+        public String getId() {
+            return id;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+
+        public ArrayMember[] getMembers() {
+            return members;
+        }
+
+        public void setMembers(ArrayMember[] members) {
+            this.members = members;
+        }
+
+        public List<ArrayMember> getList() {
+            return list;
+        }
+
+        public void setList(List<ArrayMember> list) {
+            this.list = list;
+        }
+    }
+
+    public static class ArrayMember {
+        private String name;
+        private int age;
+
+        public ArrayMember(String name, int age) {
+            this.name = name;
+            this.age = age;
+        }
+
+        public ArrayMember() {
+            this.name = "NoName";
+            this.age = 0;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getAge() {
+            return age;
+        }
+
+        public void setAge(int age) {
+            this.age = age;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ArrayMember) {
+                ArrayMember m = (ArrayMember) obj;
+                return age == m.age;
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return age;
+        }
+
+        @Override
+        public String toString() {
+            return "ArrayMember age=" + age;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue8/Person.java b/src/test/java/org/yaml/snakeyaml/issues/issue8/Person.java
new file mode 100644
index 0000000..1714a91
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue8/Person.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue8;
+
+import java.io.Serializable;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=8
+ */
+public class Person implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private String firstName;
+    private String lastName;
+    private int hatSize;
+
+    public Person() {
+    }
+
+    public Person(String firstName, String lastName, int hatSize) {
+        this.firstName = firstName;
+        this.lastName = lastName;
+        this.hatSize = hatSize;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public int getHatSize() {
+        return hatSize;
+    }
+
+    public void setHatSize(int hatSize) {
+        this.hatSize = hatSize;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (object instanceof Person) {
+            Person person = (Person) object;
+            return firstName.equals(person.firstName) && lastName.equals(person.lastName)
+                    && hatSize == person.hatSize;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return 1;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue8/PrattleRepresenterTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue8/PrattleRepresenterTest.java
new file mode 100644
index 0000000..b51ebb4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue8/PrattleRepresenterTest.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue8;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * to test http://code.google.com/p/snakeyaml/issues/detail?id=8
+ */
+public class PrattleRepresenterTest extends TestCase {
+    public void test() {
+        Yaml yaml = new Yaml();
+        Person person = new Person("Alan", "Gutierrez", 9);
+        String etalon = "!!org.yaml.snakeyaml.issues.issue8.Person {firstName: Alan, hatSize: 9, lastName: Gutierrez}\n";
+        assertEquals(etalon, yaml.dump(person));
+        assertEquals(etalon, yaml.dump(person));
+    }
+
+    public void test2beans() {
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        Person person = new Person("Alan", "Gutierrez", 9);
+        String etalon = "!!org.yaml.snakeyaml.issues.issue8.Person {firstName: Alan, hatSize: 9, lastName: Gutierrez}\n";
+        assertEquals(etalon, yaml.dump(person));
+        Horse horse = new Horse("Tom", person);
+        String etalon2 = "!!org.yaml.snakeyaml.issues.issue8.PrattleRepresenterTest$Horse\nname: Tom\nowner: {firstName: Alan, hatSize: 9, lastName: Gutierrez}\n";
+        assertEquals(etalon2, yaml.dump(horse));
+    }
+
+    public static class Horse {
+        private String name;
+        private Person owner;
+
+        public Horse(String name, Person owner) {
+            super();
+            this.name = name;
+            this.owner = owner;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Person getOwner() {
+            return owner;
+        }
+
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java
new file mode 100644
index 0000000..fb07b90
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue82/PropOrderInfluenceWhenAliasedInGenericCollectionTest.java
@@ -0,0 +1,320 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue82;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * issue 82: property order influence when aliased in generic collection
+ */
+public class PropOrderInfluenceWhenAliasedInGenericCollectionTest extends TestCase {
+
+    public static interface Account {
+    }
+
+    public static class GeneralAccount implements Account {
+        public String name = "General";
+    }
+
+    public static class SuperSaverAccount extends GeneralAccount {
+
+        public SuperSaverAccount() {
+            name = "SuperSaver";
+        }
+    }
+
+    public static class CustomerAB {
+        public Collection<Account> aAll;
+        public Collection<GeneralAccount> bGeneral;
+
+        @Override
+        public String toString() {
+            return "CustomerAB";
+        }
+    }
+
+    public static class CustomerBA {
+        public Collection<GeneralAccount> aGeneral;
+        public Collection<Account> bAll;
+    }
+
+    public static class CustomerAB_MapValue {
+        public Collection<Account> aAll;
+        public Map<String, GeneralAccount> bGeneralMap;
+
+        @Override
+        public String toString() {
+            return "CustomerAB_MapValue";
+        }
+    }
+
+    public static class CustomerAB_MapKey {
+        public Collection<Account> aAll;
+        public Map<GeneralAccount, String> bGeneralMap;
+
+        @Override
+        public String toString() {
+            return "CustomerAB_MapKey";
+        }
+    }
+
+    public static class CustomerAB_Property {
+        public Account acc;
+        public Collection<GeneralAccount> bGeneral;
+
+        @Override
+        public String toString() {
+            return "CustomerAB_Property";
+        }
+    }
+
+    public void testAB() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB customerAB = new CustomerAB();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB.aAll = all;
+        customerAB.bGeneral = general;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(customerAB);
+        // System.out.println(dump);
+        CustomerAB parsed = (CustomerAB) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testAB_Set() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB customerAB = new CustomerAB();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        Set<GeneralAccount> general = new HashSet<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB.aAll = all;
+        customerAB.bGeneral = general;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(customerAB);
+        // System.out.println(dump);
+        CustomerAB parsed = (CustomerAB) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testABWithCustomTag() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB customerAB = new CustomerAB();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB.aAll = all;
+        customerAB.bGeneral = general;
+
+        Constructor constructor = new Constructor();
+        Representer representer = new Representer();
+        Tag generalAccountTag = new Tag("!GA");
+        constructor
+                .addTypeDescription(new TypeDescription(GeneralAccount.class, generalAccountTag));
+        representer.addClassTag(GeneralAccount.class, generalAccountTag);
+
+        Yaml yaml = new Yaml(constructor, representer);
+        String dump = yaml.dump(customerAB);
+        // System.out.println(dump);
+        CustomerAB parsed = (CustomerAB) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testABProperty() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB_Property customerAB_property = new CustomerAB_Property();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB_property.acc = generalAccount;
+        customerAB_property.bGeneral = general;
+
+        Constructor constructor = new Constructor();
+        Representer representer = new Representer();
+
+        Yaml yaml = new Yaml(constructor, representer);
+        String dump = yaml.dump(customerAB_property);
+        // System.out.println(dump);
+        CustomerAB_Property parsed = (CustomerAB_Property) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testABPropertyWithCustomTag() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB_Property customerAB_property = new CustomerAB_Property();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB_property.acc = generalAccount;
+        customerAB_property.bGeneral = general;
+
+        Constructor constructor = new Constructor();
+        Representer representer = new Representer();
+
+        Tag generalAccountTag = new Tag("!GA");
+        constructor
+                .addTypeDescription(new TypeDescription(GeneralAccount.class, generalAccountTag));
+        representer.addClassTag(GeneralAccount.class, generalAccountTag);
+
+        Yaml yaml = new Yaml(constructor, representer);
+        String dump = yaml.dump(customerAB_property);
+        // System.out.println(dump);
+        CustomerAB_Property parsed = (CustomerAB_Property) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testABwithJavaBeanHelpers() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB customerAB = new CustomerAB();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerAB.aAll = all;
+        customerAB.bGeneral = general;
+
+        Yaml yaml = new Yaml();
+        String dump2 = yaml.dumpAsMap(customerAB);
+        // System.out.println(dump2);
+        Yaml loader = new Yaml();
+        CustomerAB parsed = loader.loadAs(dump2, CustomerAB.class);
+        assertNotNull(parsed);
+    }
+
+    public void testAB_asMapValue() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB_MapValue customerAB_mapValue = new CustomerAB_MapValue();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        Map<String, GeneralAccount> generalMap = new HashMap<String, GeneralAccount>();
+        generalMap.put(generalAccount.name, generalAccount);
+        generalMap.put(supersaver.name, supersaver);
+
+        customerAB_mapValue.aAll = all;
+        customerAB_mapValue.bGeneralMap = generalMap;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(customerAB_mapValue);
+        // System.out.println(dump);
+        CustomerAB_MapValue parsed = (CustomerAB_MapValue) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testAB_asMapKey() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerAB_MapKey customerAB_mapKey = new CustomerAB_MapKey();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        Map<GeneralAccount, String> generalMap = new HashMap<GeneralAccount, String>();
+        generalMap.put(generalAccount, generalAccount.name);
+        generalMap.put(supersaver, supersaver.name);
+
+        customerAB_mapKey.aAll = all;
+        customerAB_mapKey.bGeneralMap = generalMap;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(customerAB_mapKey);
+        // System.out.println(dump);
+        CustomerAB_MapKey parsed = (CustomerAB_MapKey) yaml.load(dump);
+        assertNotNull(parsed);
+    }
+
+    public void testBA() {
+        SuperSaverAccount supersaver = new SuperSaverAccount();
+        GeneralAccount generalAccount = new GeneralAccount();
+
+        CustomerBA customerBA = new CustomerBA();
+        ArrayList<Account> all = new ArrayList<Account>();
+        all.add(supersaver);
+        all.add(generalAccount);
+        ArrayList<GeneralAccount> general = new ArrayList<GeneralAccount>();
+        general.add(generalAccount);
+        general.add(supersaver);
+
+        customerBA.aGeneral = general;
+        customerBA.bAll = all;
+
+        Yaml yaml = new Yaml();
+        String dump = yaml.dump(customerBA);
+        // System.out.println(dump);
+        //
+        CustomerBA parsed = (CustomerBA) yaml.load(dump);
+        assertEquals(2, parsed.bAll.size());
+        assertEquals(2, parsed.aGeneral.size());
+        assertFalse(parsed.bAll.equals(parsed.aGeneral));
+        GeneralAccount[] array = parsed.aGeneral.toArray(new GeneralAccount[2]);
+        assertEquals(GeneralAccount.class, array[0].getClass());
+        assertEquals(SuperSaverAccount.class, array[1].getClass());
+        assertEquals("SuperSaver", array[1].name);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean1.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean1.java
new file mode 100644
index 0000000..9d73eb4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean1.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import org.springframework.core.style.ToStringCreator;
+
+public class Bean1 implements IBean {
+
+    private String strVal = "BEAN_1";
+
+    private int intVal = 1;
+
+    public Bean1() {
+        super();
+    }
+
+    public Bean1(int intVal) {
+        this.intVal = intVal;
+    }
+
+    public String getStrVal() {
+        return strVal;
+    }
+
+    public void setStrVal(String strVal) {
+        this.strVal = strVal;
+    }
+
+    public int getIntVal() {
+        return intVal;
+    }
+
+    public void setIntVal(int intVal) {
+        this.intVal = intVal;
+    }
+
+    @Override
+    public String toString() {
+        ToStringCreator builder = new ToStringCreator(this);
+        builder.append(this.strVal);
+        builder.append(this.intVal);
+        return builder.toString();
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean2.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean2.java
new file mode 100644
index 0000000..cc18912
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/Bean2.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import org.springframework.core.style.ToStringCreator;
+
+public class Bean2 implements IBean {
+
+    private String strVal = "BEAN_2";
+
+    private int intVal = 2;
+
+    public Bean2() {
+        super();
+    }
+
+    public String getStrVal() {
+        return strVal;
+    }
+
+    public void setStrVal(String strVal) {
+        this.strVal = strVal;
+    }
+
+    public int getIntVal() {
+        return intVal;
+    }
+
+    public void setIntVal(int intVal) {
+        this.intVal = intVal;
+    }
+
+    @Override
+    public String toString() {
+        ToStringCreator builder = new ToStringCreator(this);
+        builder.append(this.strVal);
+        builder.append(this.intVal);
+        return builder.toString();
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java
new file mode 100644
index 0000000..791fdc7
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class BeanConstructor extends Constructor {
+
+    public BeanConstructor() {
+        super(BeanHolder.class);
+        yamlConstructors.put(new Tag(Bean1.class), new Bean1ScalarConstructor());
+        yamlConstructors.put(new Tag(BeanHolder.class), new BeanHolderScalarConstructor());
+    }
+
+    private class Bean1ScalarConstructor extends ConstructScalar {
+        @Override
+        public Object construct(Node node) {
+            ScalarNode snode = (ScalarNode) node;
+            if (snode.getValue().length() == 0) {
+                return new Bean1();
+            } else {
+                return new Bean1(new Integer(snode.getValue()));
+            }
+        }
+    }
+
+    private class BeanHolderScalarConstructor extends ConstructScalar {
+        @Override
+        public Object construct(Node node) {
+            if (node.getNodeId() == NodeId.scalar) {
+                ScalarNode n = (ScalarNode) node;
+                String value = n.getValue();
+                int i = 3;
+                if (value.length() != 0) {
+                    i = Integer.parseInt(value);
+                }
+                return new BeanHolder(new Bean1(i));
+            } else {
+                return new BeanHolder();
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor3.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor3.java
new file mode 100644
index 0000000..d8984f4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanConstructor3.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class BeanConstructor3 extends Constructor {
+
+    public BeanConstructor3() {
+        yamlConstructors.put(new Tag(BeanHolder.class), new BeanHolderScalarConstructor());
+    }
+
+    private class BeanHolderScalarConstructor extends ConstructScalar {
+        @Override
+        public Object construct(Node node) {
+            return new BeanHolder();
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanHolder.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanHolder.java
new file mode 100644
index 0000000..d274284
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/BeanHolder.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import org.springframework.core.style.ToStringCreator;
+
+public class BeanHolder {
+
+    private IBean bean = new Bean1();
+
+    public BeanHolder() {
+        super();
+    }
+
+    public BeanHolder(IBean bean) {
+        super();
+        this.bean = bean;
+    }
+
+    public IBean getBean() {
+        return bean;
+    }
+
+    public void setBean(IBean bean) {
+        this.bean = bean;
+    }
+
+    @Override
+    public String toString() {
+        ToStringCreator builder = new ToStringCreator(this);
+        builder.append(this.bean);
+        return builder.toString();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/IBean.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/IBean.java
new file mode 100644
index 0000000..d3c1dd6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/IBean.java
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+public interface IBean {
+
+    public abstract String getStrVal();
+
+    public abstract void setStrVal(String strVal);
+
+    public abstract int getIntVal();
+
+    public abstract void setIntVal(int intVal);
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue9/NopropTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue9/NopropTest.java
new file mode 100644
index 0000000..9ee7c78
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue9/NopropTest.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue9;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class NopropTest extends TestCase {
+
+    public void testOK01() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1\n  intVal : 11\n  strVal : HALLO_1 ";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("HALLO_1", beanHolder.getBean().getStrVal());
+        assertEquals(11, beanHolder.getBean().getIntVal());
+    }
+
+    public void testOK02() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean2\n  intVal : 22\n  strVal : HALLO_2 ";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("HALLO_2", beanHolder.getBean().getStrVal());
+        assertEquals(22, beanHolder.getBean().getIntVal());
+    }
+
+    public void testOK03() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1\n  intVal : 1";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(1, beanHolder.getBean().getIntVal());
+    }
+
+    public void testOK04() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean2\n  intVal : 22";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_2", beanHolder.getBean().getStrVal());
+        assertEquals(22, beanHolder.getBean().getIntVal());
+    }
+
+    public void testOK05() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1\n  strVal : HALLO_1 ";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("HALLO_1", beanHolder.getBean().getStrVal());
+        assertEquals(1, beanHolder.getBean().getIntVal());
+    }
+
+    public void testOK06() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean2\n  strVal : HALLO_2 ";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("HALLO_2", beanHolder.getBean().getStrVal());
+        assertEquals(2, beanHolder.getBean().getIntVal());
+    }
+
+    public void testEmptyBean() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1";
+        Iterator<Object> docs = new Yaml(new BeanConstructor()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(1, beanHolder.getBean().getIntVal());
+    }
+
+    public void testEmptyBean2() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1 {}";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(1, beanHolder.getBean().getIntVal());
+    }
+
+    public void testEmptyDoc() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder";
+        Iterator<Object> docs = new Yaml(new BeanConstructor()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(3, beanHolder.getBean().getIntVal());
+    }
+
+    public void testEmptyDoc2() {
+        String yaml = "---";
+        Iterator<Object> docs = new Yaml(new BeanConstructor()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertNotNull(beanHolder);
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(3, beanHolder.getBean().getIntVal());
+        // only space is also null
+        yaml = "--- ";
+        docs = new Yaml(new BeanConstructor()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        beanHolder = (BeanHolder) docs.next();
+        assertNotNull(beanHolder);
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(3, beanHolder.getBean().getIntVal());
+        // only space is also null
+        yaml = "--- '23'";
+        docs = new Yaml(new BeanConstructor()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        beanHolder = (BeanHolder) docs.next();
+        assertNotNull(beanHolder);
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(23, beanHolder.getBean().getIntVal());
+    }
+
+    public void testEmptyDoc3() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder";
+        Iterator<Object> docs = new Yaml(new BeanConstructor3()).loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(1, beanHolder.getBean().getIntVal());
+    }
+
+    public void testNonBean() {
+        String yaml = "--- !!org.yaml.snakeyaml.issues.issue9.BeanHolder\nbean : !!org.yaml.snakeyaml.issues.issue9.Bean1 123";
+        Iterator<Object> docs = new Yaml().loadAll(yaml).iterator();
+        assertTrue(docs.hasNext());
+        BeanHolder beanHolder = (BeanHolder) docs.next();
+        assertEquals("BEAN_1", beanHolder.getBean().getStrVal());
+        assertEquals(123, beanHolder.getBean().getIntVal());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue94/ChangeRuntimeClassTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue94/ChangeRuntimeClassTest.java
new file mode 100644
index 0000000..b9823a0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue94/ChangeRuntimeClassTest.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue94;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+
+public class ChangeRuntimeClassTest {
+
+    @Test
+    public void testWithGlobalTag() {
+        String yamlText = "!!org.yaml.snakeyaml.issues.issue94.Entity\n" + "name: Matt\n"
+                + "nickName: Java\n";
+
+        // Now here that I would like to somehow intercept the constructor of
+        // SnakeYaml and give it
+        // an fresh instance of EntityLoadingProxy(); based on today's
+        // temperature, so to speak...
+        // that is un-preditable statically which proxy I will give it.
+
+        Yaml yaml = new Yaml(new MyConstructor());
+
+        Entity loadedEntity = null;
+        loadedEntity = (Entity) yaml.load(yamlText);
+
+        assertEquals("Matt", loadedEntity.getName());
+
+        // The expectation below is from having intercepted setNickName() with
+        // the artifical subclass and
+        // performed the calculation.
+        assertEquals("JJ-Java", loadedEntity.getNickName());
+        assertEquals(EntityLoadingProxy.class, loadedEntity.getClass());
+    }
+
+    @Test
+    public void testNoTag() {
+        String yamlText = "name: Matt\n" + "nickName: Java\n";
+        Yaml yaml = new Yaml(new MyConstructor(Entity.class));
+        Entity loadedEntity = null;
+        loadedEntity = (Entity) yaml.load(yamlText);
+        assertEquals("Matt", loadedEntity.getName());
+        assertEquals("JJ-Java", loadedEntity.getNickName());
+    }
+
+    /**
+     * @see Constructor.ConstructYamlObject
+     */
+    private class MyConstructor extends Constructor {
+        public MyConstructor() {
+            super();
+            this.yamlConstructors.put(null, new ConstructProxy());
+        }
+
+        public MyConstructor(Class<?> clazz) {
+            super(clazz);
+            this.yamlConstructors.put(null, new ConstructProxy());
+        }
+
+        private class ConstructProxy extends AbstractConstruct {
+            private Construct getConstructor(Node node) {
+                Class<?> cl = getClassForNode(node);
+                if (cl.equals(Entity.class) && true) {
+                    // today's temperature is high :)
+                    cl = EntityLoadingProxy.class;
+                }
+                node.setType(cl);
+                // call the constructor as if the runtime class is defined
+                Construct constructor = yamlClassConstructors.get(node.getNodeId());
+                return constructor;
+            }
+
+            public Object construct(Node node) {
+                return getConstructor(node).construct(node);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue94/Entity.java b/src/test/java/org/yaml/snakeyaml/issues/issue94/Entity.java
new file mode 100644
index 0000000..eb3f7eb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue94/Entity.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue94;
+
+public class Entity {
+    private String name;
+    private String nickName;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getNickName() {
+        return nickName;
+    }
+
+    public void setNickName(String nickName) {
+        this.nickName = nickName;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue94/EntityLoadingProxy.java b/src/test/java/org/yaml/snakeyaml/issues/issue94/EntityLoadingProxy.java
new file mode 100644
index 0000000..e8129a1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue94/EntityLoadingProxy.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue94;
+
+public class EntityLoadingProxy extends Entity {
+
+    @Override
+    public void setNickName(String nickName) {
+        if (nickName.startsWith("J"))
+            nickName = "JJ-" + nickName;
+        super.setNickName(nickName);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue95/ArrayInGenericCollectionTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue95/ArrayInGenericCollectionTest.java
new file mode 100644
index 0000000..b454953
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue95/ArrayInGenericCollectionTest.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue95;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.junit.Assert;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class ArrayInGenericCollectionTest extends TestCase {
+
+    public static class A {
+        private Map<String, String[]> meta = new HashMap<String, String[]>();
+    }
+
+    public static class B {
+        private List<String[]> meta = new ArrayList<String[]>();
+    }
+
+    private A createA() {
+        A a = new A();
+        a.meta.put("met1", new String[] { "whatever" });
+        a.meta.put("met2", new String[] { "something", "something else" });
+        return a;
+    }
+
+    private B createB() {
+        B b = new B();
+        b.meta.add(new String[] { "whatever" });
+        b.meta.add(new String[] { "something", "something else" });
+        return b;
+    }
+
+    public void testArrayAsMapValue() {
+        Yaml yaml2dump = new Yaml();
+        yaml2dump.setBeanAccess(BeanAccess.FIELD);
+        A data = createA();
+        String dump = yaml2dump.dump(data);
+        // System.out.println(dump);
+
+        Yaml yaml2load = new Yaml();
+        yaml2load.setBeanAccess(BeanAccess.FIELD);
+        A loaded = (A) yaml2load.load(dump);
+
+        assertEquals(data.meta.size(), loaded.meta.size());
+        Set<Entry<String, String[]>> loadedMeta = loaded.meta.entrySet();
+        for (Entry<String, String[]> entry : loadedMeta) {
+            assertTrue(data.meta.containsKey(entry.getKey()));
+            Assert.assertArrayEquals(data.meta.get(entry.getKey()), entry.getValue());
+        }
+    }
+
+    public void testArrayAsMapValueWithTypeDespriptor() {
+        Yaml yaml2dump = new Yaml();
+        yaml2dump.setBeanAccess(BeanAccess.FIELD);
+        A data = createA();
+        String dump = yaml2dump.dump(data);
+        // System.out.println(dump);
+
+        TypeDescription aTypeDescr = new TypeDescription(A.class);
+        aTypeDescr.putMapPropertyType("meta", String.class, String[].class);
+
+        Constructor c = new Constructor();
+        c.addTypeDescription(aTypeDescr);
+        Yaml yaml2load = new Yaml(c);
+        yaml2load.setBeanAccess(BeanAccess.FIELD);
+
+        A loaded = (A) yaml2load.load(dump);
+
+        assertEquals(data.meta.size(), loaded.meta.size());
+        Set<Entry<String, String[]>> loadedMeta = loaded.meta.entrySet();
+        for (Entry<String, String[]> entry : loadedMeta) {
+            assertTrue(data.meta.containsKey(entry.getKey()));
+            Assert.assertArrayEquals(data.meta.get(entry.getKey()), entry.getValue());
+        }
+    }
+
+    public void testArrayAsListValue() {
+        Yaml yaml2dump = new Yaml();
+        yaml2dump.setBeanAccess(BeanAccess.FIELD);
+        B data = createB();
+        String dump = yaml2dump.dump(data);
+        // System.out.println(dump);
+
+        Yaml yaml2load = new Yaml();
+        yaml2load.setBeanAccess(BeanAccess.FIELD);
+        B loaded = (B) yaml2load.load(dump);
+
+        Assert.assertArrayEquals(data.meta.toArray(), loaded.meta.toArray());
+    }
+
+    public void testArrayAsListValueWithTypeDespriptor() {
+        Yaml yaml2dump = new Yaml();
+        yaml2dump.setBeanAccess(BeanAccess.FIELD);
+        B data = createB();
+        String dump = yaml2dump.dump(data);
+        // System.out.println(dump);
+
+        TypeDescription aTypeDescr = new TypeDescription(B.class);
+        aTypeDescr.putListPropertyType("meta", String[].class);
+
+        Constructor c = new Constructor();
+        c.addTypeDescription(aTypeDescr);
+        Yaml yaml2load = new Yaml(c);
+        yaml2load.setBeanAccess(BeanAccess.FIELD);
+
+        B loaded = (B) yaml2load.load(dump);
+
+        Assert.assertArrayEquals(data.meta.toArray(), loaded.meta.toArray());
+    }
+
+    public void testNoTags() {
+        Yaml yaml2dump = new Yaml();
+        yaml2dump.setBeanAccess(BeanAccess.FIELD);
+        B data = createB();
+        String dump = yaml2dump.dumpAs(data, Tag.MAP, FlowStyle.AUTO);
+        // System.out.println(dump);
+        assertEquals("meta:\n- [whatever]\n- [something, something else]\n", dump);
+        //
+        Constructor constr = new Constructor(B.class);
+        Yaml yaml2load = new Yaml(constr);
+        yaml2load.setBeanAccess(BeanAccess.FIELD);
+        B loaded = (B) yaml2load.load(dump);
+
+        Assert.assertArrayEquals(data.meta.toArray(), loaded.meta.toArray());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue97/Blog.java b/src/test/java/org/yaml/snakeyaml/issues/issue97/Blog.java
new file mode 100644
index 0000000..8da4dc7
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue97/Blog.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue97;
+
+import java.util.SortedSet;
+
+public class Blog {
+    private SortedSet<Post> posts;
+
+    public void addPost(Post p) {
+        posts.add(p);
+    }
+
+    public SortedSet<Post> getPosts() {
+        return posts;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue97/Post.java b/src/test/java/org/yaml/snakeyaml/issues/issue97/Post.java
new file mode 100644
index 0000000..479055d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue97/Post.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue97;
+
+public class Post implements Comparable<Post> {
+
+    private String title;
+    private String text;
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public int compareTo(Post o) {
+        return title.compareTo(o.title);
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue97/YamlSortedSetTest.java b/src/test/java/org/yaml/snakeyaml/issues/issue97/YamlSortedSetTest.java
new file mode 100644
index 0000000..1b4d3a1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue97/YamlSortedSetTest.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue97;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.introspector.BeanAccess;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeId;
+import org.yaml.snakeyaml.nodes.SequenceNode;
+
+public class YamlSortedSetTest {
+
+    @Test
+    public void testYaml() {
+        String serialized = "!!org.yaml.snakeyaml.issues.issue97.Blog\n" + "posts:\n"
+                + "  - text: Dummy\n" + "    title: Test\n" + "  - text: Creative\n"
+                + "    title: Highly\n";
+        // System.out.println(serialized);
+        Yaml yaml2 = constructYamlParser();
+        Blog rehydrated = (Blog) yaml2.load(serialized);
+        checkTestBlog(rehydrated);
+    }
+
+    protected Yaml constructYamlParser() {
+        Yaml yaml = new Yaml(new SetContructor());
+        yaml.setBeanAccess(BeanAccess.FIELD);
+        return yaml;
+    }
+
+    protected void checkTestBlog(Blog blog) {
+        Set<Post> posts = blog.getPosts();
+        Assert.assertEquals("Blog contains 2 posts", 2, posts.size());
+    }
+
+    private class SetContructor extends Constructor {
+        public SetContructor() {
+            yamlClassConstructors.put(NodeId.sequence, new ConstructSetFromSequence());
+        }
+
+        private class ConstructSetFromSequence extends ConstructSequence {
+            @Override
+            public Object construct(Node node) {
+                if (SortedSet.class.isAssignableFrom(node.getType())) {
+                    if (node.isTwoStepsConstruction()) {
+                        throw new YAMLException("Set cannot be recursive.");
+                    } else {
+                        Collection<Object> result = new TreeSet<Object>();
+                        SetContructor.this.constructSequenceStep2((SequenceNode) node, result);
+                        return result;
+                    }
+                } else {
+                    return super.construct(node);
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/issues/issue99/YamlBase64Test.java b/src/test/java/org/yaml/snakeyaml/issues/issue99/YamlBase64Test.java
new file mode 100644
index 0000000..32e7eef
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/issues/issue99/YamlBase64Test.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue99;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.YamlDocument;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Example for issue 99
+ * 
+ * @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=99"></a>
+ */
+public class YamlBase64Test extends TestCase {
+
+    /**
+     * test base64 decoding
+     */
+    public void testBase64() throws IOException {
+        String text = Util.getLocalResource("issues/issue99-base64_literal.yaml");
+        String[] lines = text.split("\n");
+        String all = "";
+        for (int i = 1; i < lines.length; i++) {// skip first line
+            all = all + lines[i].trim();
+        }
+        // System.out.println(all);
+        byte[] decoded = Base64Coder.decode(all.toCharArray());
+        assertEquals(3737, decoded.length);
+        checkBytes(decoded);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testYamlBase64Loading() throws IOException {
+        Yaml yaml = new Yaml();
+        InputStream inputStream = YamlBase64Test.class
+                .getResourceAsStream("/issues/issue99-base64_double_quoted.yaml");
+        Map<String, Object> bean = (Map<String, Object>) yaml.load(inputStream);
+        byte[] jpeg = (byte[]) bean.get("jpegPhoto");
+        checkBytes(jpeg);
+        inputStream.close();
+    }
+
+    private void checkBytes(byte[] jpeg) throws IOException {
+        InputStream input;
+        input = YamlDocument.class.getClassLoader().getResourceAsStream("issues/issue99.jpeg");
+        BufferedInputStream is = new BufferedInputStream(input);
+        int i = 0;
+        while (i < jpeg.length) {
+            int etalon = is.read();
+            if (jpeg[i] < 0) {
+                assertEquals(etalon, jpeg[i] + 256);
+            } else {
+                assertEquals(etalon, jpeg[i]);
+            }
+            i++;
+        }
+        is.close();
+    }
+
+    /**
+     * In the literal scalar all the line breaks are significant
+     * 
+     * @throws IOException
+     */
+    public void testYamlBase64LoadingLiteral() throws IOException {
+        Yaml yaml = new Yaml();
+        InputStream inputStream = YamlBase64Test.class
+                .getResourceAsStream("/issues/issue99-base64_literal.yaml");
+        try {
+            yaml.load(inputStream);
+            fail("In the literal scalar all the line breaks are significant");
+        } catch (Exception e) {
+            assertEquals("Length of Base64 encoded input string is not a multiple of 4.",
+                    e.getMessage());
+        } finally {
+            inputStream.close();
+        }
+    }
+
+    /**
+     * Redefine the !!binary global tag in a way that it ignores all the white
+     * spaces to be able to use literal scalar
+     */
+    @SuppressWarnings("unchecked")
+    public void testRedefineBinaryTag() throws IOException {
+        Yaml yaml = new Yaml(new SpecialContructor(Tag.BINARY));
+        InputStream inputStream = YamlBase64Test.class
+                .getResourceAsStream("/issues/issue99-base64_literal.yaml");
+        Map<String, Object> bean = (Map<String, Object>) yaml.load(inputStream);
+        byte[] jpeg = (byte[]) bean.get("jpegPhoto");
+        checkBytes(jpeg);
+        inputStream.close();
+    }
+
+    private class SpecialContructor extends Constructor {
+        public SpecialContructor(Tag tag) {
+            this.yamlConstructors.put(tag, new MyBinaryConstructor());
+        }
+
+        private class MyBinaryConstructor extends AbstractConstruct {
+            public Object construct(Node node) {
+                String contentWithNewLines = constructScalar((ScalarNode) node).toString();
+                String noNewLines = contentWithNewLines.replaceAll("\\s", "");
+                byte[] decoded = Base64Coder.decode(noNewLines.toCharArray());
+                return decoded;
+            }
+        }
+    }
+
+    /**
+     * Define a local tag to ignore all the white spaces to be able to use
+     * literal scalar
+     */
+    @SuppressWarnings("unchecked")
+    public void testLocalBinaryTag() throws IOException {
+        Yaml yaml = new Yaml(new SpecialContructor(new Tag("!beautiful")));
+        InputStream inputStream = YamlBase64Test.class
+                .getResourceAsStream("/issues/issue99-base64_literal_custom_tag.yaml");
+        Map<String, Object> bean = (Map<String, Object>) yaml.load(inputStream);
+        byte[] jpeg = (byte[]) bean.get("jpegPhoto");
+        checkBytes(jpeg);
+        inputStream.close();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/ConstructEmptyBeanTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/ConstructEmptyBeanTest.java
new file mode 100644
index 0000000..8dea231
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/ConstructEmptyBeanTest.java
@@ -0,0 +1,131 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import java.io.Serializable;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class ConstructEmptyBeanTest extends TestCase {
+    /**
+     * standard Yaml
+     */
+    public void testEmptyBean() {
+        Yaml yaml = new Yaml();
+        EmptyBean bean = (EmptyBean) yaml
+                .load("!!org.yaml.snakeyaml.javabeans.ConstructEmptyBeanTest$EmptyBean {}");
+        assertNotNull(bean);
+        assertNull(bean.getFirstName());
+        assertEquals(5, bean.getHatSize());
+    }
+
+    /**
+     * global tag is correct (but ignored)
+     */
+    public void testEmptyBean1() {
+        Yaml beanLoader = new Yaml();
+        EmptyBean bean = beanLoader.loadAs(
+                "!!org.yaml.snakeyaml.javabeans.ConstructEmptyBeanTest$EmptyBean {}",
+                EmptyBean.class);
+        assertNotNull(bean);
+        assertNull(bean.getFirstName());
+        assertEquals(5, bean.getHatSize());
+    }
+
+    /**
+     * global tag is ignored
+     */
+    public void testEmptyBean2() {
+        Yaml beanLoader = new Yaml();
+        EmptyBean bean = beanLoader.loadAs("!!Bla-bla-bla {}", EmptyBean.class);
+        assertNotNull(bean);
+        assertNull(bean.getFirstName());
+        assertEquals(5, bean.getHatSize());
+    }
+
+    /**
+     * no tag
+     */
+    public void testEmptyBean3() {
+        Yaml beanLoader = new Yaml();
+        EmptyBean bean = beanLoader.loadAs("{   }", EmptyBean.class);
+        assertNotNull(bean);
+        assertNull(bean.getFirstName());
+        assertEquals(5, bean.getHatSize());
+    }
+
+    /**
+     * empty document
+     */
+    public void testEmptyBean4() {
+        Yaml beanLoader = new Yaml();
+        EmptyBean bean = beanLoader.loadAs("", EmptyBean.class);
+        assertNull(bean);
+    }
+
+    /**
+     * local tag is ignored
+     */
+    public void testEmptyBean5() {
+        Yaml beanLoader = new Yaml();
+        EmptyBean bean = beanLoader.loadAs("!Bla-bla-bla {}", EmptyBean.class);
+        assertNotNull(bean);
+        assertNull(bean.getFirstName());
+        assertEquals(5, bean.getHatSize());
+    }
+
+    /**
+     * invalid document
+     */
+    public void testEmptyBean6() {
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs("{", EmptyBean.class);
+            fail("Invalid document provided.");
+        } catch (Exception e) {
+            assertEquals("while parsing a flow node\n" + " in 'string', line 1, column 2:\n"
+                    + "    {\n" + "     ^\n" + "expected the node content, but found StreamEnd\n"
+                    + " in 'string', line 1, column 2:\n" + "    {\n" + "     ^\n", e.getMessage());
+        }
+    }
+
+    public static class EmptyBean implements Serializable {
+        private static final long serialVersionUID = -8001155967276657180L;
+        private String firstName;
+        private int hatSize = 5;
+
+        public EmptyBean() {
+        }
+
+        public String getFirstName() {
+            return firstName;
+        }
+
+        public void setFirstName(String firstName) {
+            this.firstName = firstName;
+        }
+
+        public int getHatSize() {
+            return hatSize;
+        }
+
+        public void setHatSize(int hatSize) {
+            this.hatSize = hatSize;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/Door.java b/src/test/java/org/yaml/snakeyaml/javabeans/Door.java
new file mode 100644
index 0000000..7e3ad13
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/Door.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public class Door {
+    private String id;
+    private int height;
+
+    public Door(String id, int height) {
+        this.id = id;
+        this.height = height;
+    }
+
+    public Door() {
+        this.height = 3;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Door) {
+            Door door = (Door) obj;
+            return id.equals(door.id);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return "Door id=" + id;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/FrontDoor.java b/src/test/java/org/yaml/snakeyaml/javabeans/FrontDoor.java
new file mode 100644
index 0000000..2b958da
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/FrontDoor.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public class FrontDoor extends Door {
+    private String keytype;
+
+    public FrontDoor() {
+        super();
+    }
+
+    public FrontDoor(String id, int height) {
+        super(id, height);
+    }
+
+    public String getKeytype() {
+        return keytype;
+    }
+
+    public void setKeytype(String keytype) {
+        this.keytype = keytype;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/House.java b/src/test/java/org/yaml/snakeyaml/javabeans/House.java
new file mode 100644
index 0000000..41ba335
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/House.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import java.util.List;
+import java.util.Map;
+
+public class House {
+    private String street;
+    private int number;
+    private List<Room> rooms;
+    private FrontDoor frontDoor;
+    private Map<String, String> reminders;
+
+    public String getStreet() {
+        return street;
+    }
+
+    public void setStreet(String street) {
+        this.street = street;
+    }
+
+    public int getNumber() {
+        return number;
+    }
+
+    public void setNumber(int number) {
+        this.number = number;
+    }
+
+    public List<Room> getRooms() {
+        return rooms;
+    }
+
+    public void setRooms(List<Room> rooms) {
+        this.rooms = rooms;
+    }
+
+    public FrontDoor getFrontDoor() {
+        return frontDoor;
+    }
+
+    public void setFrontDoor(FrontDoor frontDoor) {
+        this.frontDoor = frontDoor;
+    }
+
+    public Map<String, String> getReminders() {
+        return reminders;
+    }
+
+    public void setReminders(Map<String, String> reminders) {
+        this.reminders = reminders;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/HouseTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/HouseTest.java
new file mode 100644
index 0000000..97c6933
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/HouseTest.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class HouseTest extends TestCase {
+    /**
+     * no root global tag
+     */
+    public void testDump1() {
+        House house = new House();
+        FrontDoor frontDoor = new FrontDoor("qaz1", 5);
+        frontDoor.setKeytype("qwerty123");
+        house.setFrontDoor(frontDoor);
+        List<Room> rooms = new ArrayList<Room>();
+        rooms.add(new Room("Hall"));
+        rooms.add(new Room("Kitchen"));
+        house.setRooms(rooms);
+        Map<String, String> reminders = new TreeMap<String, String>();
+        reminders.put("today", "do nothig");
+        reminders.put("tomorrow", "go shoping");
+        house.setReminders(reminders);
+        house.setNumber(1);
+        house.setStreet("Wall Street");
+        Yaml beanDumper = new Yaml();
+        String yaml = beanDumper.dumpAsMap(house);
+        String etalon = Util.getLocalResource("javabeans/house-dump1.yaml");
+        assertEquals(etalon, yaml);
+        // load
+        Yaml beanLoader = new Yaml();
+        House loadedHouse = beanLoader.loadAs(yaml, House.class);
+        assertNotNull(loadedHouse);
+        assertEquals("Wall Street", loadedHouse.getStreet());
+        // dump again
+        String yaml3 = beanDumper.dumpAsMap(loadedHouse);
+        assertEquals(yaml, yaml3);
+    }
+
+    /**
+     * with global root class tag (global tag should be avoided)
+     */
+    public void testDump3() {
+        House house = new House();
+        FrontDoor frontDoor = new FrontDoor("qaz1", 5);
+        frontDoor.setKeytype("qwerty123");
+        house.setFrontDoor(frontDoor);
+        List<Room> rooms = new ArrayList<Room>();
+        rooms.add(new Room("Hall"));
+        rooms.add(new Room("Kitchen"));
+        house.setRooms(rooms);
+        Map<String, String> reminders = new TreeMap<String, String>();
+        reminders.put("today", "do nothig");
+        reminders.put("tomorrow", "go shoping");
+        house.setReminders(reminders);
+        house.setNumber(1);
+        house.setStreet("Wall Street");
+        Yaml beanDumper = new Yaml();
+        String yaml = beanDumper.dumpAsMap(house);
+        String etalon = Util.getLocalResource("javabeans/house-dump3.yaml");
+        assertEquals(etalon, yaml);
+        // load
+        TypeDescription description = new TypeDescription(House.class);
+        description.putListPropertyType("rooms", Room.class);
+        Yaml beanLoader = new Yaml(new Constructor(description));
+        House loadedHouse = (House) beanLoader.load(yaml);
+        House loadedHouse2 = (House) beanLoader.loadAs(yaml, House.class);
+        assertNotNull(loadedHouse);
+        assertFalse(loadedHouse == loadedHouse2);
+        assertEquals("Wall Street", loadedHouse.getStreet());
+        assertEquals(1, loadedHouse.getNumber());
+        assertEquals(1, loadedHouse2.getNumber());
+        FrontDoor fdoor = loadedHouse.getFrontDoor();
+        assertEquals(frontDoor.getId(), fdoor.getId());
+        assertEquals(frontDoor.getHeight(), fdoor.getHeight());
+        assertEquals(frontDoor.getKeytype(), fdoor.getKeytype());
+        assertEquals(frontDoor, fdoor);
+        assertEquals(reminders, loadedHouse.getReminders());
+        List<Room> loadedRooms = loadedHouse.getRooms();
+        assertEquals(rooms, loadedRooms);
+        // dump again
+        String yaml3 = beanDumper.dumpAsMap(loadedHouse);
+        assertEquals(yaml, yaml3);
+    }
+
+    /**
+     * with global root class tag (global tag should be avoided)
+     */
+    public void testDump2() {
+        House house = new House();
+        FrontDoor frontDoor = new FrontDoor("qaz1", 5);
+        frontDoor.setKeytype("qwerty123");
+        house.setFrontDoor(frontDoor);
+        List<Room> rooms = new ArrayList<Room>();
+        rooms.add(new Room("Hall"));
+        rooms.add(new Room("Kitchen"));
+        house.setRooms(rooms);
+        Map<String, String> reminders = new TreeMap<String, String>();
+        reminders.put("today", "do nothig");
+        reminders.put("tomorrow", "go shoping");
+        house.setReminders(reminders);
+        house.setNumber(1);
+        house.setStreet("Wall Street");
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml beanDumper = new Yaml(options);
+        String yaml = beanDumper.dump(house);
+        String etalon = Util.getLocalResource("javabeans/house-dump2.yaml");
+        assertEquals(etalon, yaml);
+        // load
+        Yaml beanLoader = new Yaml();
+        House loadedHouse = beanLoader.loadAs(yaml, House.class);
+        assertNotNull(loadedHouse);
+        assertEquals("Wall Street", loadedHouse.getStreet());
+        // dump again
+        String yaml3 = beanDumper.dump(loadedHouse);
+        assertEquals(yaml, yaml3);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/LongTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/LongTest.java
new file mode 100644
index 0000000..7b7deaf
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/LongTest.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class LongTest extends TestCase {
+    public void testLongFail() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml yaml = new Yaml(options);
+        Foo foo = new Foo();
+        String output = yaml.dump(foo);
+        // System.out.println(output);
+        try {
+            yaml.load(output);
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("argument type mismatch"));
+        }
+    }
+
+    public static class Foo {
+        private Long bar = Long.valueOf(42L);
+
+        public Long getBar() {
+            return bar;
+        }
+
+        public void setBar(Long bar) {
+            this.bar = bar;
+        }
+    }
+
+    public void testLongRepresenter() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Representer repr = new Representer();
+        repr.addClassTag(Long.class, new Tag("!!java.lang.Long"));
+        Yaml yaml = new Yaml(repr, options);
+
+        Foo foo = new Foo();
+        String output = yaml.dump(foo);
+        // System.out.println(output);
+        Foo foo2 = (Foo) yaml.load(output);
+        assertEquals(new Long(42L), foo2.getBar());
+    }
+
+    public void testLongConstructor() {
+        String doc = "!!org.yaml.snakeyaml.javabeans.LongTest$Foo\n\"bar\": !!int \"42\"";
+        // System.out.println(doc);
+        Yaml yaml = new Yaml();
+        Foo foo2 = (Foo) yaml.load(doc);
+        assertEquals(new Long(42L), foo2.getBar());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/Room.java b/src/test/java/org/yaml/snakeyaml/javabeans/Room.java
new file mode 100644
index 0000000..31e0ebd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/Room.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public class Room {
+    private String name;
+
+    public Room() {
+        this.name = "Bedroom";
+    }
+
+    public Room(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof Room) {
+            Room room = (Room) obj;
+            return name.equals(room.name);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Room name=" + name;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/Shape.java b/src/test/java/org/yaml/snakeyaml/javabeans/Shape.java
new file mode 100644
index 0000000..ec086e4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/Shape.java
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public interface Shape {
+
+    public int process();
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/StringArrayTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/StringArrayTest.java
new file mode 100644
index 0000000..5a38049
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/StringArrayTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class StringArrayTest extends TestCase {
+    public void testStrings() {
+        A a = new A();
+        a.setNames(new String[] { "aaa", "bbb", "ccc" });
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(a);
+        assertEquals("!!org.yaml.snakeyaml.javabeans.StringArrayTest$A\nnames: [aaa, bbb, ccc]\n",
+                output);
+        A b = (A) yaml.load(output);
+        assertTrue(Arrays.equals(a.getNames(), b.getNames()));
+    }
+
+    public void testStringsPretty() {
+        A a = new A();
+        a.setNames(new String[] { "aaa", "bbb", "ccc" });
+        DumperOptions options = new DumperOptions();
+        options.setPrettyFlow(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(a);
+        assertEquals(
+                "!!org.yaml.snakeyaml.javabeans.StringArrayTest$A\nnames: [\n  aaa,\n  bbb,\n  ccc]\n",
+                output);
+        A b = (A) yaml.load(output);
+        assertTrue(Arrays.equals(a.getNames(), b.getNames()));
+    }
+
+    public static class A {
+        String[] names;
+
+        public String[] getNames() {
+            return names;
+        }
+
+        public void setNames(String[] names) {
+            this.names = names;
+        }
+
+        public String getName(int index) {
+            return names[index];
+        }
+
+        public void setName(int index, String name) {
+            this.names[index] = name;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/Triangle.java b/src/test/java/org/yaml/snakeyaml/javabeans/Triangle.java
new file mode 100644
index 0000000..10acc6d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/Triangle.java
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public class Triangle implements Shape {
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int process() {
+        return 7;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBean.java b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBean.java
new file mode 100644
index 0000000..6893090
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBean.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+public class TriangleBean {
+    private String name;
+    private Shape shape;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Shape getShape() {
+        return shape;
+    }
+
+    public void setShape(Shape shape) {
+        this.shape = shape;
+    }
+
+    @Override
+    public String toString() {
+        return "TriangleBean name=" + name;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
new file mode 100644
index 0000000..8e84e75
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/javabeans/TriangleBeanTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.javabeans;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class TriangleBeanTest extends TestCase {
+
+    public void testGetTriangle() {
+        Triangle triangle = new Triangle();
+        triangle.setName("Triangle25");
+        TriangleBean bean = new TriangleBean();
+        bean.setShape(triangle);
+        bean.setName("Bean25");
+        Yaml beanDumper = new Yaml();
+        String output = beanDumper.dumpAsMap(bean);
+        assertEquals(
+                "name: Bean25\nshape: !!org.yaml.snakeyaml.javabeans.Triangle\n  name: Triangle25\n",
+                output);
+        Yaml beanLoader = new Yaml();
+        TriangleBean loadedBean = beanLoader.loadAs(output, TriangleBean.class);
+        assertNotNull(loadedBean);
+        assertEquals("Bean25", loadedBean.getName());
+        assertEquals(7, loadedBean.getShape().process());
+    }
+
+    public void testClassNotFound() {
+        String output = "name: Bean25\nshape: !!org.yaml.snakeyaml.javabeans.Triangle777\n  name: Triangle25\n";
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(output, TriangleBean.class);
+            fail("Class not found expected.");
+        } catch (Exception e) {
+            assertTrue(
+                    e.getMessage(),
+                    e.getMessage().contains(
+                            "Class not found: org.yaml.snakeyaml.javabeans.Triangle777"));
+        }
+    }
+
+    /**
+     * Runtime class has less priority then an explicit tag
+     */
+    public void testClassAndTag() {
+        String output = "name: !!whatever Bean25\nshape: !!org.yaml.snakeyaml.javabeans.Triangle\n  name: Triangle25\n";
+        Yaml beanLoader = new Yaml();
+        try {
+            beanLoader.loadAs(output, TriangleBean.class);
+            fail("Runtime class has less priority then an explicit tag");
+        } catch (Exception e) {
+            assertTrue(e.getMessage(), e.getMessage().contains("Class not found: whatever"));
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/json/JsonTest.java b/src/test/java/org/yaml/snakeyaml/json/JsonTest.java
new file mode 100644
index 0000000..4ba2119
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/json/JsonTest.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.json;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.Yaml;
+
+
+import java.util.Map;
+
+public class JsonTest extends TestCase {
+
+    private Yaml loader = new Yaml();
+
+
+    public void testLooksLikeJson() {
+        Map<String, Integer> map = (Map<String, Integer>) loader.load("{a: 1}");
+        assertEquals(new Integer(1), map.get("a"));
+    }
+
+    public void testSpaceAfterColon() {
+        Map<String, Integer> map = (Map<String, Integer>) loader.load("{\"a\": 1}");
+        assertEquals(new Integer(1), map.get("a"));
+    }
+
+    public void testCounterintuitiveColon() {
+        try {
+            loader.load("{a:1}");
+            fail("We agree with libyaml and PyYAML.");
+        } catch (Exception e) {
+            assertTrue("':' in the flow context is a mess.", e.getMessage().contains("Please check http://pyyaml.org/wiki/YAMLColonInFlowContext for details."));
+        }
+    }
+
+    public void testNoSpace() {
+        Map<String, Integer> map = (Map<String, Integer>) loader.load("{\"a\":1}");
+        assertEquals(new Integer(1), map.get("a"));
+    }
+
+    public void testNoSpaceBothDoubleQuoted() {
+        Map<String, Integer> map = (Map<String, Integer>) loader.load("{\"a\":\"1\"}");
+        assertEquals("1", map.get("a"));
+    }
+
+    public void testNoSpaceSingleQouted() {
+        Map<String, Integer> map = (Map<String, Integer>) loader.load("{'a':1}");
+        assertEquals(new Integer(1), map.get("a"));
+    }
+
+    public void testManyValues() {
+        Map<String, Object> map = (Map<String, Object>) loader.load("{\"a\":1,\"b\":true,\"c\":\"foo\"}");
+        assertEquals(3, map.size());
+        assertEquals(new Integer(1), map.get("a"));
+        assertTrue((Boolean) map.get("b"));
+        assertEquals("foo", map.get("c"));
+    }
+
+    public void testConstructNull() {
+        Map<String, Object> map = (Map<String, Object>) loader.load("{a: null}");
+        assertEquals(1, map.size());
+        assertNull(map.get("a"));
+    }
+
+    public void testConstructNullFromEmpty() {
+        Map<String, Object> map = (Map<String, Object>) loader.load("{a: }");
+        assertEquals(1, map.size());
+        assertNull(map.get("a"));
+    }
+
+    public void testConstructBoolean() {
+        Map<String, Object> map = (Map<String, Object>) loader.load("{a: true}");
+        assertEquals(1, map.size());
+        assertEquals(Boolean.TRUE, map.get("a"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/lowlevel/LowLevelApiTest.java b/src/test/java/org/yaml/snakeyaml/lowlevel/LowLevelApiTest.java
new file mode 100644
index 0000000..12d7f3a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/lowlevel/LowLevelApiTest.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.lowlevel;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.nodes.Node;
+
+public class LowLevelApiTest extends TestCase {
+
+    public void testLowLevel() {
+        List<Object> list = new ArrayList<Object>();
+        list.add(1);
+        list.add("abc");
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("name", "Tolstoy");
+        map.put("book", "War and People");
+        list.add(map);
+        Yaml yaml = new Yaml();
+        String etalon = yaml.dump(list);
+        // System.out.println(etalon);
+        //
+        Node node = yaml.represent(list);
+        // System.out.println(node);
+        assertEquals(
+                "Representation tree from an object and from its YAML document must be the same.",
+                yaml.compose(new StringReader(etalon)).toString(), node.toString());
+        //
+        List<Event> events = yaml.serialize(node);
+        int i = 0;
+        for (Event etalonEvent : yaml.parse(new StringReader(etalon))) {
+            Event ev1 = events.get(i++);
+            assertEquals(etalonEvent.getClass(), ev1.getClass());
+            if (etalonEvent instanceof ScalarEvent) {
+                ScalarEvent scalar1 = (ScalarEvent) etalonEvent;
+                ScalarEvent scalar2 = (ScalarEvent) ev1;
+                assertEquals(scalar1.getAnchor(), scalar2.getAnchor());
+                assertEquals(scalar1.getValue(), scalar2.getValue());
+            }
+        }
+        assertEquals(i, events.size());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/MappingNodeTest.java b/src/test/java/org/yaml/snakeyaml/nodes/MappingNodeTest.java
new file mode 100644
index 0000000..a17afbb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/MappingNodeTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import junit.framework.TestCase;
+
+public class MappingNodeTest extends TestCase {
+
+    public void testNullValue() {
+        try {
+            new MappingNode(new Tag("!tag"), true, null, null, null, false);
+            fail("Value is required.");
+        } catch (Exception e) {
+            assertEquals("value in a Node is required.", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/NodeTest.java b/src/test/java/org/yaml/snakeyaml/nodes/NodeTest.java
new file mode 100644
index 0000000..ab7c6ca
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/NodeTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+
+public class NodeTest extends TestCase {
+
+    public void testNode() {
+        try {
+            new ScalarNode(new Tag("!foo"), null, null, null, '"');
+            fail("Value must be required.");
+        } catch (Exception e) {
+            assertEquals("value in a Node is required.", e.getMessage());
+        }
+    }
+
+    public void testSetTag() {
+        try {
+            ScalarNode node = new ScalarNode(new Tag("!foo"), "Value1", null, null, '"');
+            node.setTag((Tag) null);
+            fail("Value must be required.");
+        } catch (Exception e) {
+            assertEquals("tag in a Node is required.", e.getMessage());
+        }
+    }
+
+    public void testGetEndMark() {
+        Mark mark1 = new Mark("name", 5, 2, 12, "afd asd asd", 7);
+        Mark mark2 = new Mark("name", 6, 3, 13, "afd asd asd", 8);
+        Node node = new ScalarNode(new Tag("!foo"), "bla-bla", mark1, mark2, '"');
+        assertEquals(mark1, node.getStartMark());
+        assertEquals(mark2, node.getEndMark());
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/NodeTupleTest.java b/src/test/java/org/yaml/snakeyaml/nodes/NodeTupleTest.java
new file mode 100644
index 0000000..70d0d3a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/NodeTupleTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import junit.framework.TestCase;
+
+public class NodeTupleTest extends TestCase {
+
+    public void testNodeTuple1() {
+        Node node = new ScalarNode(new Tag("!tag"), "value1", null, null, null);
+        try {
+            new NodeTuple(null, node);
+            fail("Node must be provided.");
+        } catch (Exception e) {
+            assertEquals("Nodes must be provided.", e.getMessage());
+        }
+    }
+
+    public void testNodeTuple2() {
+        Node node = new ScalarNode(new Tag("!tag"), "value1", null, null, null);
+        try {
+            new NodeTuple(node, null);
+            fail("Node must be provided.");
+        } catch (Exception e) {
+            assertEquals("Nodes must be provided.", e.getMessage());
+        }
+    }
+
+    public void testToString() {
+        Node key = new ScalarNode(Tag.STR, "key1", null, null, null);
+        Node value = new ScalarNode(Tag.STR, "value1", null, null, null);
+        NodeTuple tuple = new NodeTuple(key, value);
+        assertEquals(
+                "<NodeTuple keyNode=<org.yaml.snakeyaml.nodes.ScalarNode (tag=tag:yaml.org,2002:str, value=key1)>; valueNode=<org.yaml.snakeyaml.nodes.ScalarNode (tag=tag:yaml.org,2002:str, value=value1)>>",
+                tuple.toString());
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/ScalarNodeTest.java b/src/test/java/org/yaml/snakeyaml/nodes/ScalarNodeTest.java
new file mode 100644
index 0000000..501115d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/ScalarNodeTest.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import junit.framework.TestCase;
+
+public class ScalarNodeTest extends TestCase {
+
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testGetNodeId() {
+        Node node = new ScalarNode(new Tag("str"), "text", null, null, '>');
+        assertEquals(NodeId.scalar, node.getNodeId());
+    }
+
+    public void testToString() {
+        Node node = new ScalarNode(new Tag("str"), "text", null, null, '>');
+        assertTrue(node.toString().contains("ScalarNode"));
+        assertTrue(node.toString().contains("tag="));
+        assertTrue(node.toString().contains("value="));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/SequenceNodeTest.java b/src/test/java/org/yaml/snakeyaml/nodes/SequenceNodeTest.java
new file mode 100644
index 0000000..4ab1ce1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/SequenceNodeTest.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+public class SequenceNodeTest extends TestCase {
+
+    public void testGetNodeId() {
+        SequenceNode node = new SequenceNode(new Tag("!foo"), true, new ArrayList<Node>(), null,
+                null, true);
+        assertEquals(NodeId.sequence, node.getNodeId());
+    }
+
+    public void testNullValue() {
+        try {
+            new SequenceNode(new Tag("!foo"), true, null, null, null, true);
+            fail("Value is required.");
+        } catch (Exception e) {
+            assertEquals("value in a Node is required.", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/TagTest.java b/src/test/java/org/yaml/snakeyaml/nodes/TagTest.java
new file mode 100644
index 0000000..467d335
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/TagTest.java
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import junit.framework.TestCase;
+
+public class TagTest extends TestCase {
+
+    public void testCreate() {
+        try {
+            new Tag((String) null);
+            fail();
+        } catch (Exception e) {
+            assertEquals("Tag must be provided.", e.getMessage());
+        }
+        try {
+            new Tag("");
+            fail();
+        } catch (Exception e) {
+            assertEquals("Tag must not be empty.", e.getMessage());
+        }
+        try {
+            new Tag("!Dice ");
+            fail();
+        } catch (Exception e) {
+            assertEquals("Tag must not contain leading or trailing spaces.", e.getMessage());
+        }
+        Tag tag = new Tag(TagTest.class);
+        assertEquals(Tag.PREFIX + "org.yaml.snakeyaml.nodes.TagTest", tag.getValue());
+    }
+
+    public void testCreate2() {
+        try {
+            new Tag((Class<?>) null);
+            fail();
+        } catch (Exception e) {
+            assertEquals("Class for tag must be provided.", e.getMessage());
+        }
+    }
+
+    public void testGetClassName() {
+        Tag tag = new Tag(Tag.PREFIX + "org.yaml.snakeyaml.nodes.TagTest");
+        assertEquals("org.yaml.snakeyaml.nodes.TagTest", tag.getClassName());
+    }
+
+    public void testGetClassNameError() {
+        try {
+            Tag tag = new Tag("!TagTest");
+            tag.getClassName();
+            fail("Class name is only available for global tag");
+        } catch (Exception e) {
+            assertEquals("Invalid tag: !TagTest", e.getMessage());
+        }
+    }
+
+    public void testLength() {
+        String t = Tag.PREFIX + "org.yaml.snakeyaml.nodes.TagTest";
+        Tag tag = new Tag(t);
+        assertEquals(t.length(), tag.getLength());
+    }
+
+    public void testToString() {
+        Tag tag = new Tag("!car");
+        assertEquals("!car", tag.toString());
+    }
+
+    public void testUri1() {
+        Tag tag = new Tag("!Académico");
+        assertEquals("!Acad%C3%A9mico", tag.toString());
+    }
+
+    public void testUri2() {
+        Tag tag = new Tag("!ruby/object:Test::Module::Sub2");
+        assertEquals("!ruby/object:Test::Module::Sub2", tag.getValue());
+    }
+
+    public void testCompare() {
+        Tag tag = new Tag("!car");
+        assertEquals(0, tag.compareTo(new Tag("!car")));
+    }
+
+    public void testEqualsObject() {
+        Tag tag = new Tag("!car");
+        assertEquals(tag, tag);
+        assertEquals(tag, new Tag("!car"));
+        assertFalse(tag.equals(new Tag("!!str")));
+        assertFalse(tag.equals(null));
+        assertFalse(tag.equals(25));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/nodes/TagsTest.java b/src/test/java/org/yaml/snakeyaml/nodes/TagsTest.java
new file mode 100644
index 0000000..5155623
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/nodes/TagsTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.nodes;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+public class TagsTest extends TestCase {
+
+    public void testGetGlobalTagForClass() {
+        assertEquals(new Tag("tag:yaml.org,2002:java.lang.String"), new Tag(String.class));
+        assertEquals(new Tag("tag:yaml.org,2002:org.yaml.snakeyaml.nodes.TagsTest"), new Tag(
+                TagsTest.class));
+    }
+
+    /**
+     * test fix for issue 18 -
+     * http://code.google.com/p/snakeyaml/issues/detail?id=18
+     */
+    public void testLong() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        Yaml yaml = new Yaml(options);
+        Foo foo = new Foo();
+        String output = yaml.dump(foo);
+        // System.out.println(output);
+        Foo foo2 = (Foo) yaml.load(output);
+        assertEquals(new Long(42L), foo2.getBar());
+    }
+
+    public static class Foo {
+        private Long bar = Long.valueOf(42L);
+
+        public Long getBar() {
+            return bar;
+        }
+
+        public void setBar(Long bar) {
+            this.bar = bar;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/parser/ParserImplTest.java b/src/test/java/org/yaml/snakeyaml/parser/ParserImplTest.java
new file mode 100644
index 0000000..d8bd3c8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/parser/ParserImplTest.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import java.util.LinkedList;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.events.DocumentEndEvent;
+import org.yaml.snakeyaml.events.DocumentStartEvent;
+import org.yaml.snakeyaml.events.Event;
+import org.yaml.snakeyaml.events.ImplicitTuple;
+import org.yaml.snakeyaml.events.MappingEndEvent;
+import org.yaml.snakeyaml.events.MappingStartEvent;
+import org.yaml.snakeyaml.events.ScalarEvent;
+import org.yaml.snakeyaml.events.SequenceEndEvent;
+import org.yaml.snakeyaml.events.SequenceStartEvent;
+import org.yaml.snakeyaml.events.StreamEndEvent;
+import org.yaml.snakeyaml.events.StreamStartEvent;
+import org.yaml.snakeyaml.reader.StreamReader;
+
+public class ParserImplTest extends TestCase {
+
+    private void check(LinkedList<Event> etalonEvents, Parser parser) {
+        while (parser.checkEvent(null)) {
+            Event event = parser.getEvent();
+            if (etalonEvents.isEmpty()) {
+                fail("unexpected event: " + event);
+            }
+            assertEquals(etalonEvents.removeFirst(), event);
+        }
+        assertFalse("Must contain no more events: " + parser.getEvent(), parser.checkEvent(null));
+    }
+
+    public void testGetEvent() {
+        String data = "string: abcd";
+        StreamReader reader = new StreamReader(data);
+        Parser parser = new ParserImpl(reader);
+        Mark dummyMark = new Mark("dummy", 0, 0, 0, "", 0);
+        LinkedList<Event> etalonEvents = new LinkedList<Event>();
+        etalonEvents.add(new StreamStartEvent(dummyMark, dummyMark));
+        etalonEvents.add(new DocumentStartEvent(dummyMark, dummyMark, false, null, null));
+        etalonEvents.add(new MappingStartEvent(null, null, true, dummyMark, dummyMark,
+                Boolean.FALSE));
+        etalonEvents.add(new ScalarEvent(null, null, new ImplicitTuple(true, false), "string",
+                dummyMark, dummyMark, (char) 0));
+        etalonEvents.add(new ScalarEvent(null, null, new ImplicitTuple(true, false), "abcd",
+                dummyMark, dummyMark, (char) 0));
+        etalonEvents.add(new MappingEndEvent(dummyMark, dummyMark));
+        etalonEvents.add(new DocumentEndEvent(dummyMark, dummyMark, false));
+        etalonEvents.add(new StreamEndEvent(dummyMark, dummyMark));
+        check(etalonEvents, parser);
+    }
+
+    public void testGetEvent2() {
+        String data = "american:\n  - Boston Red Sox";
+        StreamReader reader = new StreamReader(data);
+        Parser parser = new ParserImpl(reader);
+        Mark dummyMark = new Mark("dummy", 0, 0, 0, "", 0);
+        LinkedList<Event> etalonEvents = new LinkedList<Event>();
+        etalonEvents.add(new StreamStartEvent(dummyMark, dummyMark));
+        etalonEvents.add(new DocumentStartEvent(dummyMark, dummyMark, false, null, null));
+        etalonEvents
+                .add(new MappingStartEvent(null, null, true, dummyMark, dummyMark, Boolean.TRUE));
+        etalonEvents.add(new ScalarEvent(null, null, new ImplicitTuple(true, false), "american",
+                dummyMark, dummyMark, (char) 0));
+        etalonEvents.add(new SequenceStartEvent(null, null, true, dummyMark, dummyMark,
+                Boolean.FALSE));
+        etalonEvents.add(new ScalarEvent(null, null, new ImplicitTuple(true, false),
+                "Boston Red Sox", dummyMark, dummyMark, (char) 0));
+        etalonEvents.add(new SequenceEndEvent(dummyMark, dummyMark));
+        etalonEvents.add(new MappingEndEvent(dummyMark, dummyMark));
+        etalonEvents.add(new DocumentEndEvent(dummyMark, dummyMark, false));
+        etalonEvents.add(new StreamEndEvent(dummyMark, dummyMark));
+        check(etalonEvents, parser);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/parser/VersionTagsTupleTest.java b/src/test/java/org/yaml/snakeyaml/parser/VersionTagsTupleTest.java
new file mode 100644
index 0000000..abd072b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/parser/VersionTagsTupleTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.parser;
+
+import java.util.HashMap;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions.Version;
+
+public class VersionTagsTupleTest extends TestCase {
+
+    public void testToString() {
+        VersionTagsTuple tuple = new VersionTagsTuple(Version.V1_1, new HashMap<String, String>());
+        assertEquals("VersionTagsTuple<Version: 1.1, {}>", tuple.toString());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/partialconstruct/DeveloperBean.java b/src/test/java/org/yaml/snakeyaml/partialconstruct/DeveloperBean.java
new file mode 100644
index 0000000..f9a1e57
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/partialconstruct/DeveloperBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.partialconstruct;
+
+public class DeveloperBean {
+    String name;
+    String language;
+
+    public String getName() {
+        return name;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposer.java b/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposer.java
new file mode 100644
index 0000000..eccf57e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposer.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.partialconstruct;
+
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.nodes.MappingNode;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.parser.Parser;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+class FragmentComposer extends Composer {
+    String nodeName;
+
+    public FragmentComposer(Parser parser, Resolver resolver, String nodeName) {
+        super(parser, resolver);
+        this.nodeName = nodeName;
+    }
+
+    @Override
+    public Node getSingleNode() {
+        Node node = super.getSingleNode();
+        if (!MappingNode.class.isAssignableFrom(node.getClass())) {
+            throw new RuntimeException(
+                    "Document is not structured as expected.  Root element should be a map!");
+        }
+        MappingNode root = (MappingNode) node;
+        for (NodeTuple tuple : root.getValue()) {
+            Node keyNode = tuple.getKeyNode();
+            if (ScalarNode.class.isAssignableFrom(keyNode.getClass())) {
+                if (((ScalarNode) keyNode).getValue().equals(nodeName)) {
+                    return tuple.getValueNode();
+                }
+            }
+        }
+        throw new RuntimeException("Did not find key \"" + nodeName + "\" in document-level map");
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposerTest.java b/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposerTest.java
new file mode 100644
index 0000000..c2bb990
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/partialconstruct/FragmentComposerTest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.partialconstruct;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.composer.Composer;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.parser.ParserImpl;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class FragmentComposerTest extends TestCase {
+
+    public void testFragment() {
+        String document = "foo:  blargle\n"
+                + "developer:  { name: \"Bjarne Stroustrup\", language: \"C++\"}\n"
+                + "gee:  [ \"whiz\", \"bang\"]\n";//
+
+        StreamReader reader = new StreamReader(document);
+        Composer composer = new FragmentComposer(new ParserImpl(reader), new Resolver(),
+                "developer");
+        Constructor constructor = new Constructor();
+        constructor.setComposer(composer);
+        DeveloperBean developer = (DeveloperBean) constructor.getSingleData(DeveloperBean.class);
+        assertEquals("Bjarne Stroustrup", developer.name);
+        assertEquals("C++", developer.language);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/reader/IoReaderTest.java b/src/test/java/org/yaml/snakeyaml/reader/IoReaderTest.java
new file mode 100644
index 0000000..625f735
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/reader/IoReaderTest.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class IoReaderTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testCheckPrintable() throws IOException {
+        Yaml yaml = new Yaml();
+        Reader reader = new FileReader("src/test/resources/specification/example2_1.yaml");
+        List<String> list = (List<String>) yaml.load(reader);
+        reader.close();
+        assertEquals(3, list.size());
+    }
+
+    /**
+     * test input which is longer then internal buffer - 1k
+     */
+    public void testBigInput() throws IOException {
+        Yaml yaml = new Yaml();
+        Reader reader = new FileReader("src/test/resources/reader/large.yaml");
+        @SuppressWarnings("unchecked")
+        List<Object> list = (List<Object>) yaml.load(reader);
+        reader.close();
+        assertEquals(37, list.size());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/reader/ReaderBomTest.java b/src/test/java/org/yaml/snakeyaml/reader/ReaderBomTest.java
new file mode 100644
index 0000000..ffde7dd
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/reader/ReaderBomTest.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+
+import junit.framework.TestCase;
+
+public class ReaderBomTest extends TestCase {
+
+    public void testReader() {
+        Reader input = new StringReader("test");
+        StreamReader reader = new StreamReader(input);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+    }
+
+    public void testNoBom() throws IOException {
+        byte[] data = "test".getBytes("UTF-8");
+        ByteArrayInputStream input = new ByteArrayInputStream(data);
+        Reader r = new UnicodeReader(input);
+        StreamReader reader = new StreamReader(r);
+        assertEquals('t', reader.peek());
+        assertEquals(Charset.forName("UTF-8"), reader.getEncoding());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+        r.close();
+    }
+
+    public void testUtf8Bom() throws IOException {
+        File file = new File("src/test/resources/reader/utf-8.txt");
+        assertTrue("Test file not found: " + file.getAbsolutePath(), file.exists());
+        InputStream input = new FileInputStream(file);
+        StreamReader reader = new StreamReader(new UnicodeReader(input));
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+        assertEquals(Charset.forName("UTF-8"), reader.getEncoding());
+        input.close();
+    }
+
+    public void testUnicodeLeBom() throws IOException {
+        File file = new File("src/test/resources/reader/unicode-16le.txt");
+        assertTrue("Test file not found: " + file.getAbsolutePath(), file.exists());
+        InputStream input = new FileInputStream(file);
+        StreamReader reader = new StreamReader(new UnicodeReader(input));
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+        assertEquals(Charset.forName("UTF-16LE"), reader.getEncoding());
+        input.close();
+    }
+
+    public void testUnicodeBeBom() throws IOException {
+        File file = new File("src/test/resources/reader/unicode-16be.txt");
+        assertTrue("Test file not found: " + file.getAbsolutePath(), file.exists());
+        InputStream input = new FileInputStream(file);
+        StreamReader reader = new StreamReader(new UnicodeReader(input));
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+        assertEquals(Charset.forName("UTF-16BE"), reader.getEncoding());
+        input.close();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/reader/ReaderStringTest.java b/src/test/java/org/yaml/snakeyaml/reader/ReaderStringTest.java
new file mode 100644
index 0000000..282b7e6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/reader/ReaderStringTest.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.reader;
+
+import java.util.regex.Matcher;
+
+import junit.framework.TestCase;
+
+public class ReaderStringTest extends TestCase {
+
+    public void testCheckPrintable() {
+        StreamReader reader = new StreamReader("test");
+        reader.checkPrintable("test");
+        Matcher matcher = StreamReader.NON_PRINTABLE.matcher("test");
+        assertFalse(matcher.find());
+
+        try {
+            reader.checkPrintable("test".toCharArray(), 0, 4);
+        } catch (ReaderException e) {
+            fail();
+        }
+
+    }
+
+    public void testCheckNonPrintable() {
+        Matcher matcher = StreamReader.NON_PRINTABLE.matcher("test\u0005 fail");
+        assertTrue(matcher.find());
+        try {
+            new StreamReader("test\u0005 fail");
+            fail("Non printable Unicode characters must not be accepted.");
+        } catch (ReaderException e) {
+            assertEquals(
+                    "unacceptable character '' (0x5) special characters are not allowed\nin \"'string'\", position 4",
+                    e.toString());
+        }
+    }
+
+    /**
+     * test that regular expression and array check work the same
+     */
+    public void testCheckAll() {
+        StreamReader streamReader = new StreamReader("");
+        for (char i = 0; i < 256 * 256 - 1; i++) {
+            char[] chars = new char[1];
+            chars[0] = i;
+            String str = new String(chars);
+            Matcher matcher = StreamReader.NON_PRINTABLE.matcher(str);
+            boolean regularExpressionResult = !matcher.find();
+
+            boolean charsArrayResult = true;
+            try {
+                streamReader.checkPrintable(chars, 0, 1);
+            } catch (Exception e) {
+                String error = e.getMessage();
+                assertTrue(
+                        error,
+                        error.startsWith("unacceptable character")
+                                || error.equals("special characters are not allowed"));
+                charsArrayResult = false;
+            }
+            assertEquals("Failed for #" + i, regularExpressionResult, charsArrayResult);
+        }
+    }
+
+    public void testForward() {
+        StreamReader reader = new StreamReader("test");
+        while (reader.peek() != '\u0000') {
+            reader.forward(1);
+        }
+        reader = new StreamReader("test");
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('e', reader.peek());
+        reader.forward(1);
+        assertEquals('s', reader.peek());
+        reader.forward(1);
+        assertEquals('t', reader.peek());
+        reader.forward(1);
+        assertEquals('\u0000', reader.peek());
+    }
+
+    public void testPeekInt() {
+        StreamReader reader = new StreamReader("test");
+        assertEquals('t', reader.peek(0));
+        assertEquals('e', reader.peek(1));
+        assertEquals('s', reader.peek(2));
+        assertEquals('t', reader.peek(3));
+        reader.forward(1);
+        assertEquals('e', reader.peek(0));
+        assertEquals('s', reader.peek(1));
+        assertEquals('t', reader.peek(2));
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java b/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java
new file mode 100644
index 0000000..a0ee333
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/AbstractHuman.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.Date;
+
+public abstract class AbstractHuman {
+    private String name;
+    private Date birthday;
+    private String birthPlace;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getBirthday() {
+        return birthday;
+    }
+
+    public void setBirthday(Date birthday) {
+        this.birthday = birthday;
+    }
+
+    public String getBirthPlace() {
+        return birthPlace;
+    }
+
+    public void setBirthPlace(String birthPlace) {
+        this.birthPlace = birthPlace;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((birthPlace == null) ? 0 : birthPlace.hashCode());
+        result = prime * result + ((birthday == null) ? 0 : birthday.hashCode());
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        AbstractHuman other = (AbstractHuman) obj;
+        if (birthPlace == null) {
+            if (other.birthPlace != null)
+                return false;
+        } else if (!birthPlace.equals(other.birthPlace))
+            return false;
+        if (birthday == null) {
+            if (other.birthday != null)
+                return false;
+        } else if (!birthday.equals(other.birthday))
+            return false;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        return true;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human.java b/src/test/java/org/yaml/snakeyaml/recursive/Human.java
new file mode 100644
index 0000000..1ff91ae
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class Human extends AbstractHuman {
+
+    private Human father;
+    private Human mother;
+    private Human partner;
+    private Human bankAccountOwner;
+    protected Set<Human> children;
+
+    public Human() {
+        children = new HashSet<Human>();
+    }
+
+    public Human getFather() {
+        return father;
+    }
+
+    public void setFather(Human father) {
+        this.father = father;
+    }
+
+    public Human getMother() {
+        return mother;
+    }
+
+    public void setMother(Human mother) {
+        this.mother = mother;
+    }
+
+    public Human getPartner() {
+        return partner;
+    }
+
+    public void setPartner(Human partner) {
+        this.partner = partner;
+    }
+
+    public Human getBankAccountOwner() {
+        return bankAccountOwner;
+    }
+
+    public void setBankAccountOwner(Human bankAccountOwner) {
+        this.bankAccountOwner = bankAccountOwner;
+    }
+
+    public Set<Human> getChildren() {
+        return children;
+    }
+
+    public void setChildren(Set<Human> children) {
+        this.children = children;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human2.java b/src/test/java/org/yaml/snakeyaml/recursive/Human2.java
new file mode 100644
index 0000000..d056c72
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human2.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Human2 extends AbstractHuman {
+
+    private Human2 father;
+    private Human2 mother;
+    private Human2 partner;
+    private Human2 bankAccountOwner;
+    protected Map<Human2, String> children;
+
+    public Human2() {
+        children = new HashMap<Human2, String>();
+    }
+
+    public Human2 getFather() {
+        return father;
+    }
+
+    public void setFather(Human2 father) {
+        this.father = father;
+    }
+
+    public Human2 getMother() {
+        return mother;
+    }
+
+    public void setMother(Human2 mother) {
+        this.mother = mother;
+    }
+
+    public Human2 getPartner() {
+        return partner;
+    }
+
+    public void setPartner(Human2 partner) {
+        this.partner = partner;
+    }
+
+    public Human2 getBankAccountOwner() {
+        return bankAccountOwner;
+    }
+
+    public void setBankAccountOwner(Human2 bankAccountOwner) {
+        this.bankAccountOwner = bankAccountOwner;
+    }
+
+    public Map<Human2, String> getChildren() {
+        return children;
+    }
+
+    public void setChildren(Map<Human2, String> children) {
+        this.children = children;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human3.java b/src/test/java/org/yaml/snakeyaml/recursive/Human3.java
new file mode 100644
index 0000000..41bda1f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human3.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Human3 extends AbstractHuman {
+
+    private Human3 father;
+    private Human3 mother;
+    private Human3 partner;
+    private Human3 bankAccountOwner;
+    protected List<Human3> children;
+
+    public Human3() {
+        children = new ArrayList<Human3>();
+    }
+
+    public Human3 getFather() {
+        return father;
+    }
+
+    public void setFather(Human3 father) {
+        this.father = father;
+    }
+
+    public Human3 getMother() {
+        return mother;
+    }
+
+    public void setMother(Human3 mother) {
+        this.mother = mother;
+    }
+
+    public Human3 getPartner() {
+        return partner;
+    }
+
+    public void setPartner(Human3 partner) {
+        this.partner = partner;
+    }
+
+    public Human3 getBankAccountOwner() {
+        return bankAccountOwner;
+    }
+
+    public void setBankAccountOwner(Human3 bankAccountOwner) {
+        this.bankAccountOwner = bankAccountOwner;
+    }
+
+    public List<Human3> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<Human3> children) {
+        this.children = children;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java b/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java
new file mode 100644
index 0000000..eab247e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/HumanTest.java
@@ -0,0 +1,658 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class HumanTest extends TestCase {
+
+    public void testNoChildren() {
+        Human father = new Human();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        Human mother = new Human();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(father);
+        String etalon = Util.getLocalResource("recursive/no-children-1.yaml");
+        assertEquals(etalon, output);
+        //
+        Human father2 = (Human) yaml.load(output);
+        assertNotNull(father2);
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", father2.getPartner().getName());
+        assertEquals("Father", father2.getBankAccountOwner().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+    }
+
+    public void testNoChildrenPretty() {
+        Human father = new Human();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        Human mother = new Human();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        DumperOptions options = new DumperOptions();
+        options.setPrettyFlow(true);
+        options.setDefaultFlowStyle(FlowStyle.FLOW);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(father);
+        String etalon = Util.getLocalResource("recursive/no-children-1-pretty.yaml");
+        assertEquals(etalon, output);
+        //
+        Human father2 = (Human) yaml.load(output);
+        assertNotNull(father2);
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", father2.getPartner().getName());
+        assertEquals("Father", father2.getBankAccountOwner().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+    }
+
+    public void testChildren() {
+        Human father = new Human();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human mother = new Human();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human son = new Human();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human daughter = new Human();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        Set<Human> children = new LinkedHashSet<Human>(2);
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+        Yaml beanDumper = new Yaml();
+        String output = beanDumper.dumpAsMap(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children.yaml");
+        assertEquals(etalon, output);
+        TypeDescription humanDescription = new TypeDescription(Human.class);
+        humanDescription.putMapPropertyType("children", Human.class, Object.class);
+        Yaml beanLoader = new Yaml(new Constructor(humanDescription));
+        //
+        Human son2 = beanLoader.loadAs(output, Human.class);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        Human father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Set<Human> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human.class, child.getClass());
+        }
+
+        // check if hashCode is correct
+        validateSet(children2);
+    }
+
+    public void testChildrenPretty() {
+        Human father = new Human();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human mother = new Human();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human son = new Human();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human daughter = new Human();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        Set<Human> children = new LinkedHashSet<Human>(2);
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.FLOW);
+        options.setPrettyFlow(true);
+        Yaml beanDumper = new Yaml(options);
+        String output = beanDumper.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children-pretty.yaml");
+        assertEquals(etalon, output);
+        TypeDescription humanDescription = new TypeDescription(Human.class);
+        humanDescription.putMapPropertyType("children", Human.class, Object.class);
+        Yaml beanLoader = new Yaml(new Constructor(humanDescription));
+        //
+        Human son2 = beanLoader.loadAs(output, Human.class);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        Human father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Set<Human> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human.class, child.getClass());
+        }
+
+        // check if hashCode is correct
+        validateSet(children2);
+    }
+
+    public void testChildren2() {
+        Human2 father = new Human2();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human2 mother = new Human2();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human2 son = new Human2();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human2 daughter = new Human2();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        HashMap<Human2, String> children = new LinkedHashMap<Human2, String>(2);
+        children.put(son, "son");
+        children.put(daughter, "daughter");
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor(Human2.class);
+        TypeDescription humanDescription = new TypeDescription(Human2.class);
+        humanDescription.putMapPropertyType("children", Human2.class, String.class);
+        constructor.addTypeDescription(humanDescription);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children-2.yaml");
+        assertEquals(etalon, output);
+        //
+        Human2 son2 = (Human2) yaml.load(output);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        Human2 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Map<Human2, String> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        validateMapKeys(children2);
+    }
+
+    public void testChildren3() {
+        Human3 father = new Human3();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human3 mother = new Human3();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human3 son = new Human3();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human3 daughter = new Human3();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        ArrayList<Human3> children = new ArrayList<Human3>();
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor(Human3.class);
+        TypeDescription Human3Description = new TypeDescription(Human3.class);
+        Human3Description.putListPropertyType("children", Human3.class);
+        constructor.addTypeDescription(Human3Description);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children-3.yaml");
+        assertEquals(etalon, output);
+        //
+        Human3 son2 = (Human3) yaml.load(output);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        Human3 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        List<Human3> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human3.class, child.getClass());
+        }
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is set
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenSetAsRoot() {
+        String etalon = Util.getLocalResource("recursive/with-children-as-set.yaml");
+
+        Constructor constructor = new Constructor();
+        TypeDescription humanDescription = new TypeDescription(Human.class);
+        humanDescription.putMapPropertyType("children", Human.class, Object.class);
+        constructor.addTypeDescription(humanDescription);
+
+        Yaml yaml = new Yaml(constructor);
+        Set<Human> children2 = (Set<Human>) yaml.load(etalon);
+        assertNotNull(children2);
+        assertEquals(2, children2.size());
+
+        Human firstChild = children2.iterator().next();
+
+        Human father2 = firstChild.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", firstChild.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), firstChild.getMother());
+        assertSame(father2, firstChild.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human.class, child.getClass());
+        }
+
+        validateSet(children2);
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is map
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenMapAsRoot() {
+        String etalon = Util.getLocalResource("recursive/with-children-as-map.yaml");
+
+        Constructor constructor = new Constructor();
+        TypeDescription Human2Description = new TypeDescription(Human2.class);
+        Human2Description.putMapPropertyType("children", Human2.class, String.class);
+        constructor.addTypeDescription(Human2Description);
+
+        Yaml yaml = new Yaml(constructor);
+        Map<Human2, String> children2 = (Map<Human2, String>) yaml.load(etalon);
+        assertNotNull(children2);
+        assertEquals(2, children2.size());
+
+        Entry<Human2, String> firstEntry = children2.entrySet().iterator().next();
+        Human2 firstChild = firstEntry.getKey();
+
+        Human2 father2 = firstChild.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", firstChild.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), firstChild.getMother());
+        assertSame(father2, firstChild.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        validateMapKeys(children2);
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is list
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenListRoot() {
+        Human3 father = new Human3();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human3 mother = new Human3();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human3 son = new Human3();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human3 daughter = new Human3();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        ArrayList<Human3> children = new ArrayList<Human3>();
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor();
+        TypeDescription Human3Description = new TypeDescription(Human3.class);
+        Human3Description.putListPropertyType("children", Human3.class);
+        constructor.addTypeDescription(Human3Description);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(father.getChildren());
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children-as-list.yaml");
+        assertEquals(etalon, output);
+        //
+        List<Human3> children2 = (List<Human3>) yaml.load(output);
+        assertNotNull(children2);
+        Human3 son2 = children2.iterator().next();
+        assertEquals(2, children2.size());
+
+        Human3 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human3.class, child.getClass());
+        }
+    }
+
+    public void testBeanRing() {
+        Human man1 = new Human();
+        man1.setName("Man 1");
+        Human man2 = new Human();
+        man2.setName("Man 2");
+        Human man3 = new Human();
+        man3.setName("Man 3");
+        man1.setBankAccountOwner(man2);
+        man2.setBankAccountOwner(man3);
+        man3.setBankAccountOwner(man1);
+        //
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(man1);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/beanring-3.yaml");
+        assertEquals(etalon, output);
+        //
+        Human loadedMan1 = (Human) yaml.load(output);
+        assertNotNull(loadedMan1);
+        assertEquals("Man 1", loadedMan1.getName());
+        Human loadedMan2 = loadedMan1.getBankAccountOwner();
+        Human loadedMan3 = loadedMan2.getBankAccountOwner();
+        assertSame(loadedMan1, loadedMan3.getBankAccountOwner());
+    }
+
+    public void qtestCollectionRing() {
+        // Set<Object> set = new HashSet<Object>();
+        // List<Object> list = new ArrayList<Object>();
+        // Map<Object, Object> map = new HashMap<Object, Object>();
+        // set.add(list);
+        // list.add(map);
+        // map.put("1", set);
+        // //
+        // try {
+        // Yaml yaml = new Yaml();
+        // String output = yaml.dump(set);
+        // // String etalon = Util.getLocalResource("recursive/???.yaml");
+        // // assertEquals(etalon, output);
+        // //
+        // // Set<Object> loadedSet = (Set<Object>) yaml.load(output);
+        // } catch (StackOverflowError e) {
+        // fail("Cannot dump recursive collections.");
+        // }
+    }
+
+    /**
+     * Checks if object was put into the set after full construction. So the
+     * hashCode was calculated correctly (if it depends on internal object's
+     * state).
+     * 
+     * @param set
+     */
+    private void validateSet(Set<?> set) {
+        for (Object object : set) {
+            assertTrue(set.contains(object));
+        }
+    }
+
+    /**
+     * Checks if object was put into the map as key after full construction. So
+     * the hashCode was calculated correctly (if it depends on internal object's
+     * state).
+     * 
+     * @param map
+     */
+    private void validateMapKeys(Map<?, ?> map) {
+        for (Map.Entry<?, ?> entry : map.entrySet()) {
+            assertTrue(map.containsKey(entry.getKey()));
+        }
+    }
+
+    public void testChildrenWithoutRootTag() {
+        Human father = new Human();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human mother = new Human();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human son = new Human();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human daughter = new Human();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        Set<Human> children = new LinkedHashSet<Human>(2);
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+        Yaml beanDumper = new Yaml();
+        String output = beanDumper.dumpAsMap(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-children-no-root-tag.yaml");
+        assertEquals(etalon, output);
+        TypeDescription humanDescription = new TypeDescription(Human.class);
+        humanDescription.putMapPropertyType("children", Human.class, Object.class);
+        Yaml beanLoader = new Yaml(new Constructor(humanDescription));
+        //
+        Human son2 = beanLoader.loadAs(output, Human.class);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        Human father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Set<Human> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            // check if type descriptor was correct
+            assertSame(Human.class, child.getClass());
+        }
+
+        // check if hashCode is correct
+        validateSet(children2);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/Human_WithArrayOfChildrenTest.java b/src/test/java/org/yaml/snakeyaml/recursive/Human_WithArrayOfChildrenTest.java
new file mode 100644
index 0000000..b0d013c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/Human_WithArrayOfChildrenTest.java
@@ -0,0 +1,183 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive;
+
+import java.util.Date;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class Human_WithArrayOfChildrenTest extends TestCase {
+
+    public static class Human_WithArrayOfChildren extends AbstractHuman {
+
+        private Human_WithArrayOfChildren father;
+        private Human_WithArrayOfChildren mother;
+        private Human_WithArrayOfChildren partner;
+        private Human_WithArrayOfChildren bankAccountOwner;
+        protected Human_WithArrayOfChildren[] children;
+
+        public Human_WithArrayOfChildren() {
+            children = new Human_WithArrayOfChildren[0];
+        }
+
+        public Human_WithArrayOfChildren getFather() {
+            return father;
+        }
+
+        public void setFather(Human_WithArrayOfChildren father) {
+            this.father = father;
+        }
+
+        public Human_WithArrayOfChildren getMother() {
+            return mother;
+        }
+
+        public void setMother(Human_WithArrayOfChildren mother) {
+            this.mother = mother;
+        }
+
+        public Human_WithArrayOfChildren getPartner() {
+            return partner;
+        }
+
+        public void setPartner(Human_WithArrayOfChildren partner) {
+            this.partner = partner;
+        }
+
+        public Human_WithArrayOfChildren getBankAccountOwner() {
+            return bankAccountOwner;
+        }
+
+        public void setBankAccountOwner(Human_WithArrayOfChildren bankAccountOwner) {
+            this.bankAccountOwner = bankAccountOwner;
+        }
+
+        public Human_WithArrayOfChildren[] getChildren() {
+            return children;
+        }
+
+        public void setChildren(Human_WithArrayOfChildren[] children) {
+            this.children = children;
+        }
+
+    }
+
+    private Human_WithArrayOfChildren createSon() {
+        Human_WithArrayOfChildren father = new Human_WithArrayOfChildren();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        Human_WithArrayOfChildren mother = new Human_WithArrayOfChildren();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        Human_WithArrayOfChildren son = new Human_WithArrayOfChildren();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        Human_WithArrayOfChildren daughter = new Human_WithArrayOfChildren();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        Human_WithArrayOfChildren[] children = new Human_WithArrayOfChildren[] { son, daughter };
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+        return son;
+    }
+
+    private void checkSon(Human_WithArrayOfChildren son) {
+        assertNotNull(son);
+        assertEquals("Son", son.getName());
+
+        Human_WithArrayOfChildren father2 = son.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son.getMother());
+        assertSame(father2, son.getMother().getPartner());
+
+        Human_WithArrayOfChildren[] fathersChildren = father2.getChildren();
+        assertEquals(2, fathersChildren.length);
+        Human_WithArrayOfChildren[] mothersChildren = father2.getPartner().getChildren();
+        assertEquals(2, mothersChildren.length);
+        assertSame(mothersChildren, fathersChildren);
+
+        for (Object child : fathersChildren) {
+            // check if type descriptor was correct
+            assertSame(Human_WithArrayOfChildren.class, child.getClass());
+        }
+    }
+
+    public void testChildrenArray() {
+        Constructor constructor = new Constructor(Human_WithArrayOfChildren.class);
+        TypeDescription HumanWithChildrenArrayDescription = new TypeDescription(
+                Human_WithArrayOfChildren.class);
+        HumanWithChildrenArrayDescription.putListPropertyType("children",
+                Human_WithArrayOfChildren.class);
+        constructor.addTypeDescription(HumanWithChildrenArrayDescription);
+        Human_WithArrayOfChildren son = createSon();
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-childrenArray.yaml");
+        assertEquals(etalon, output);
+        //
+        Human_WithArrayOfChildren son2 = (Human_WithArrayOfChildren) yaml.load(output);
+        checkSon(son2);
+    }
+
+    public void testDumpChildrenArrayWithoutRootTag() {
+        Yaml yaml = new Yaml();
+        Human_WithArrayOfChildren son = createSon();
+        String output = yaml.dumpAsMap(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/with-childrenArray-no-root-tag.yaml");
+        assertEquals(etalon, output);
+    }
+
+    public void testParseChildrenArrayWithoutRootTag() {
+        Constructor constructor = new Constructor(Human_WithArrayOfChildren.class);
+        TypeDescription HumanWithChildrenArrayDescription = new TypeDescription(
+                Human_WithArrayOfChildren.class);
+        HumanWithChildrenArrayDescription.putListPropertyType("children",
+                Human_WithArrayOfChildren.class);
+        constructor.addTypeDescription(HumanWithChildrenArrayDescription);
+        Yaml yaml = new Yaml(constructor);
+        String doc = Util.getLocalResource("recursive/with-childrenArray-no-root-tag.yaml");
+        Human_WithArrayOfChildren son2 = (Human_WithArrayOfChildren) yaml.load(doc);
+        checkSon(son2);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHumanGen.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHumanGen.java
new file mode 100644
index 0000000..8a6a01b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/AbstractHumanGen.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive.generics;
+
+import java.util.Date;
+
+public abstract class AbstractHumanGen<T, K extends AbstractHumanGen<T, ?>> {
+    private String name;
+    private Date birthday;
+    private String birthPlace;
+    private K father;
+    private K mother;
+    private K partner;
+    private K bankAccountOwner;
+    protected T children;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getBirthday() {
+        return birthday;
+    }
+
+    public void setBirthday(Date birthday) {
+        this.birthday = birthday;
+    }
+
+    public String getBirthPlace() {
+        return birthPlace;
+    }
+
+    public K getFather() {
+        return father;
+    }
+
+    public void setFather(K father) {
+        this.father = father;
+    }
+
+    public K getMother() {
+        return mother;
+    }
+
+    public void setMother(K mother) {
+        this.mother = mother;
+    }
+
+    public void setBirthPlace(String birthPlace) {
+        this.birthPlace = birthPlace;
+    }
+
+    public T getChildren() {
+        return children;
+    }
+
+    public void setChildren(T children) {
+        this.children = children;
+    }
+
+    public K getPartner() {
+        return partner;
+    }
+
+    public void setPartner(K partner) {
+        this.partner = partner;
+    }
+
+    public K getBankAccountOwner() {
+        return bankAccountOwner;
+    }
+
+    public void setBankAccountOwner(K bankAccountOwner) {
+        this.bankAccountOwner = bankAccountOwner;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen.java
new file mode 100644
index 0000000..bf14d47
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive.generics;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class HumanGen extends AbstractHumanGen<Set<HumanGen>, HumanGen> {
+    public HumanGen() {
+        children = new LinkedHashSet<HumanGen>();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen2.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen2.java
new file mode 100644
index 0000000..0045680
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen2.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive.generics;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class HumanGen2 extends AbstractHumanGen<Map<HumanGen2, String>, HumanGen2> {
+
+    public HumanGen2() {
+        children = new HashMap<HumanGen2, String>();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen3.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen3.java
new file mode 100644
index 0000000..b05d136
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGen3.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive.generics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HumanGen3 extends AbstractHumanGen<List<HumanGen3>, HumanGen3> {
+
+    public HumanGen3() {
+        children = new ArrayList<HumanGen3>();
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java
new file mode 100644
index 0000000..5bc1a0e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/recursive/generics/HumanGenericsTest.java
@@ -0,0 +1,499 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.recursive.generics;
+
+import java.beans.IntrospectionException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.generics.GenericsBugDetector;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class HumanGenericsTest extends TestCase {
+
+    public void testNoChildren() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen father = new HumanGen();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        HumanGen mother = new HumanGen();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(father);
+        String etalon = Util.getLocalResource("recursive/generics/no-children-1.yaml");
+        assertEquals(etalon, output);
+        //
+        HumanGen father2 = (HumanGen) yaml.load(output);
+        assertNotNull(father2);
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", father2.getPartner().getName());
+        assertEquals("Father", father2.getBankAccountOwner().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+    }
+
+    /**
+     * the YAML document should contain no global tags
+     * 
+     * @throws IntrospectionException
+     */
+    public void testNoChildren2() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen father = new HumanGen();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        HumanGen mother = new HumanGen();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        Yaml yaml = new Yaml();
+        String output = yaml.dumpAsMap(father);
+        String etalon = Util.getLocalResource("recursive/generics/no-children-2.yaml");
+        assertEquals(etalon, output);
+        //
+        Yaml loader = new Yaml();
+        HumanGen father2 = (HumanGen) loader.loadAs(etalon, HumanGen.class);
+        assertNotNull(father2);
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", father2.getPartner().getName());
+        assertEquals("Father", father2.getBankAccountOwner().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+    }
+
+    public void testChildren() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen father = new HumanGen();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        HumanGen mother = new HumanGen();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        HumanGen son = new HumanGen();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        HumanGen daughter = new HumanGen();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        Set<HumanGen> children = new LinkedHashSet<HumanGen>(2);
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor();
+        TypeDescription humanDescription = new TypeDescription(HumanGen.class);
+        humanDescription.putMapPropertyType("children", HumanGen.class, Object.class);
+        constructor.addTypeDescription(humanDescription);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/generics/with-children.yaml");
+        assertEquals(etalon, output);
+        //
+        HumanGen son2 = (HumanGen) yaml.load(output);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        HumanGen father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Set<HumanGen> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            assertSame(HumanGen.class, child.getClass()); // check if type
+            // descriptor was correct
+        }
+    }
+
+    public void testChildren2() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen2 father = new HumanGen2();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        HumanGen2 mother = new HumanGen2();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        HumanGen2 son = new HumanGen2();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        HumanGen2 daughter = new HumanGen2();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        HashMap<HumanGen2, String> children = new LinkedHashMap<HumanGen2, String>(2);
+        children.put(son, "son");
+        children.put(daughter, "daughter");
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+        Representer representer = new Representer();
+        representer.addClassTag(HumanGen2.class, Tag.MAP);
+        Yaml yaml = new Yaml(representer);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/generics/with-children-2.yaml");
+        assertEquals(etalon, output);
+        // load
+        TypeDescription humanDescription = new TypeDescription(HumanGen2.class);
+        humanDescription.putMapPropertyType("children", HumanGen2.class, String.class);
+        Yaml beanLoader = new Yaml(new Constructor(humanDescription));
+        //
+        HumanGen2 son2 = beanLoader.loadAs(output, HumanGen2.class);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        HumanGen2 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        Map<HumanGen2, String> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+    }
+
+    public void testChildren3() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen3 father = new HumanGen3();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        HumanGen3 mother = new HumanGen3();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        HumanGen3 son = new HumanGen3();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        HumanGen3 daughter = new HumanGen3();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        ArrayList<HumanGen3> children = new ArrayList<HumanGen3>();
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor();
+        TypeDescription Human3Description = new TypeDescription(HumanGen3.class);
+        Human3Description.putListPropertyType("children", HumanGen3.class);
+        constructor.addTypeDescription(Human3Description);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(son);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/generics/with-children-3.yaml");
+        assertEquals(etalon, output);
+        //
+        HumanGen3 son2 = (HumanGen3) yaml.load(output);
+        assertNotNull(son2);
+        assertEquals("Son", son.getName());
+
+        HumanGen3 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        List<HumanGen3> children2 = father2.getChildren();
+        assertEquals(2, children2.size());
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            assertSame(HumanGen3.class, child.getClass()); // check if type
+            // descriptor was
+            // correct
+        }
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is set
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenSetAsRoot() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-set.yaml");
+
+        Constructor constructor = new Constructor();
+        TypeDescription humanDescription = new TypeDescription(HumanGen.class);
+        humanDescription.putMapPropertyType("children", HumanGen.class, Object.class);
+        constructor.addTypeDescription(humanDescription);
+
+        Yaml yaml = new Yaml(constructor);
+        Set<HumanGen> children2 = (Set<HumanGen>) yaml.load(etalon);
+        assertNotNull(children2);
+        assertEquals(2, children2.size());
+
+        HumanGen firstChild = children2.iterator().next();
+
+        HumanGen father2 = firstChild.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", firstChild.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), firstChild.getMother());
+        assertSame(father2, firstChild.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            assertSame(HumanGen.class, child.getClass()); // check if type
+            // descriptor was correct
+        }
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is map
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenMapAsRoot() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-map.yaml");
+
+        Constructor constructor = new Constructor();
+        TypeDescription Human2Description = new TypeDescription(HumanGen2.class);
+        Human2Description.putMapPropertyType("children", HumanGen2.class, String.class);
+        constructor.addTypeDescription(Human2Description);
+
+        Yaml yaml = new Yaml(constructor);
+        Map<HumanGen2, String> children2 = (Map<HumanGen2, String>) yaml.load(etalon);
+        assertNotNull(children2);
+        assertEquals(2, children2.size());
+
+        Entry<HumanGen2, String> firstEntry = children2.entrySet().iterator().next();
+        HumanGen2 firstChild = firstEntry.getKey();
+
+        HumanGen2 father2 = firstChild.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", firstChild.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), firstChild.getMother());
+        assertSame(father2, firstChild.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+    }
+
+    /*
+     * Loads same structure as created in testChildren. But root object is list
+     * of children
+     */
+    @SuppressWarnings("unchecked")
+    public void testChildrenListRoot() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen3 father = new HumanGen3();
+        father.setName("Father");
+        father.setBirthday(new Date(1000000000));
+        father.setBirthPlace("Leningrad");
+        father.setBankAccountOwner(father);
+        //
+        HumanGen3 mother = new HumanGen3();
+        mother.setName("Mother");
+        mother.setBirthday(new Date(100000000000L));
+        mother.setBirthPlace("Saint-Petersburg");
+        father.setPartner(mother);
+        mother.setPartner(father);
+        mother.setBankAccountOwner(father);
+        //
+        HumanGen3 son = new HumanGen3();
+        son.setName("Son");
+        son.setBirthday(new Date(310000000000L));
+        son.setBirthPlace("Munich");
+        son.setBankAccountOwner(father);
+        son.setFather(father);
+        son.setMother(mother);
+        //
+        HumanGen3 daughter = new HumanGen3();
+        daughter.setName("Daughter");
+        daughter.setBirthday(new Date(420000000000L));
+        daughter.setBirthPlace("New York");
+        daughter.setBankAccountOwner(father);
+        daughter.setFather(father);
+        daughter.setMother(mother);
+        //
+        ArrayList<HumanGen3> children = new ArrayList<HumanGen3>();
+        children.add(son);
+        children.add(daughter);
+        father.setChildren(children);
+        mother.setChildren(children);
+        //
+
+        Constructor constructor = new Constructor();
+        TypeDescription Human3Description = new TypeDescription(HumanGen3.class);
+        Human3Description.putListPropertyType("children", HumanGen3.class);
+        constructor.addTypeDescription(Human3Description);
+
+        Yaml yaml = new Yaml(constructor);
+        String output = yaml.dump(father.getChildren());
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/generics/with-children-as-list.yaml");
+        assertEquals(etalon, output);
+        //
+        List<HumanGen3> children2 = (List<HumanGen3>) yaml.load(output);
+        assertNotNull(children2);
+        HumanGen3 son2 = children2.iterator().next();
+        assertEquals(2, children2.size());
+
+        HumanGen3 father2 = son2.getFather();
+        assertEquals("Father", father2.getName());
+        assertEquals("Mother", son2.getMother().getName());
+        assertSame(father2, father2.getBankAccountOwner());
+        assertSame(father2.getPartner(), son2.getMother());
+        assertSame(father2, son2.getMother().getPartner());
+
+        assertSame(father2.getPartner().getChildren(), children2);
+
+        for (Object child : children2) {
+            assertSame(HumanGen3.class, child.getClass()); // check if type
+            // descriptor was
+            // correct
+        }
+    }
+
+    public void testBeanRing() throws IOException, IntrospectionException {
+        if (!GenericsBugDetector.isProperIntrospection()) {
+            return;
+        }
+        HumanGen man1 = new HumanGen();
+        man1.setName("Man 1");
+        HumanGen man2 = new HumanGen();
+        man2.setName("Man 2");
+        HumanGen man3 = new HumanGen();
+        man3.setName("Man 3");
+        man1.setBankAccountOwner(man2);
+        man2.setBankAccountOwner(man3);
+        man3.setBankAccountOwner(man1);
+        //
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(man1);
+        // System.out.println(output);
+        String etalon = Util.getLocalResource("recursive/generics/beanring-3.yaml");
+        assertEquals(etalon, output);
+        //
+        HumanGen loadedMan1 = (HumanGen) yaml.load(output);
+        assertNotNull(loadedMan1);
+        assertEquals("Man 1", loadedMan1.getName());
+        HumanGen loadedMan2 = loadedMan1.getBankAccountOwner();
+        HumanGen loadedMan3 = loadedMan2.getBankAccountOwner();
+        assertSame(loadedMan1, loadedMan3.getBankAccountOwner());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/DumpStackTraceTest.java b/src/test/java/org/yaml/snakeyaml/representer/DumpStackTraceTest.java
new file mode 100644
index 0000000..38a5256
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/DumpStackTraceTest.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class DumpStackTraceTest extends TestCase {
+
+    public void testJavaStackTrace() {
+        Yaml yaml = new Yaml();
+        String input = Util.getLocalResource("representer/stacktrace1.txt");
+        String result = yaml.dump(input);
+        // System.out.println(result);
+        assertEquals(result, yaml.dump(yaml.load(result)));
+    }
+
+    public void testJavaStackTraceWithNoSpecialCharacters() {
+        DumperOptions options = new DumperOptions();
+        options.setWidth(50);
+        Yaml yaml = new Yaml(options);
+        String input = Util.getLocalResource("representer/stacktrace2.txt");
+        assertEquals(-1, input.indexOf(':'));
+        assertEquals(-1, input.indexOf('\t'));
+        String result = yaml.dump(input);
+        // System.out.println(result);
+        assertEquals(result, yaml.dump(yaml.load(result)));
+    }
+
+    public void testJavaStackTraceWithTabs() {
+        Yaml yaml = new Yaml();
+        String input = Util.getLocalResource("representer/stacktrace3.txt");
+        assertEquals(-1, input.indexOf(':'));
+        assertTrue("Tabs must be used.", input.indexOf('\t') > 0);
+        String result = yaml.dump(input);
+        // System.out.println(result);
+        assertEquals(result, yaml.dump(yaml.load(result)));
+    }
+
+    public void testJavaStackTraceWithoutTabs() {
+        Yaml yaml = new Yaml();
+        String input = Util.getLocalResource("representer/stacktrace1.txt");
+        String result = (String) yaml.dump(input);
+        // System.out.println(result);
+        String etalon = Util.getLocalResource("representer/stacktrace1.yaml");
+        // http://code.google.com/p/snakeyaml/issues/detail?id=66
+        assertEquals(etalon, result);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/FilterPropertyToDumpTest.java b/src/test/java/org/yaml/snakeyaml/representer/FilterPropertyToDumpTest.java
new file mode 100644
index 0000000..41c6bf2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/FilterPropertyToDumpTest.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.beans.IntrospectionException;
+import java.util.Set;
+import java.util.TreeSet;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.Property;
+
+public class FilterPropertyToDumpTest extends TestCase {
+
+    public void testFilterPropertyInJavaBeanDumper() {
+        BeanToRemoveProperty bean = new BeanToRemoveProperty();
+        bean.setNumber(24);
+        bean.setId("ID124");
+        Yaml d = new Yaml();
+        String dump = d.dumpAsMap(bean);
+        // System.out.println(dump);
+        assertEquals("id: ID124\nnumber: 24\n", dump);
+    }
+
+    public void testFilterPropertyInYaml() {
+        BeanToRemoveProperty bean = new BeanToRemoveProperty();
+        bean.setNumber(25);
+        bean.setId("ID125");
+        Yaml yaml = new Yaml(new MyRepresenter());
+        String dump = yaml.dumpAsMap(bean);
+        // System.out.println(dump);
+        assertEquals("number: 25\n", dump);
+    }
+
+    public void testDoNotFilterPropertyIncludeReadOnly() {
+        BeanToRemoveProperty bean = new BeanToRemoveProperty();
+        bean.setNumber(26);
+        bean.setId("ID126");
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        String dump = yaml.dump(bean);
+        // System.out.println(dump);
+        assertEquals(
+                "!!org.yaml.snakeyaml.representer.FilterPropertyToDumpTest$BeanToRemoveProperty {id: ID126,\n  number: 26, something: true}\n",
+                dump);
+    }
+
+    public class BeanToRemoveProperty {
+        private int number;
+        private String id;
+
+        public boolean isSomething() {
+            return true;
+        }
+
+        public int getNumber() {
+            return number;
+        }
+
+        public void setNumber(int number) {
+            this.number = number;
+        }
+
+        public void setId(String id) {
+            this.id = id;
+        }
+
+        public String getId() {
+            return id;
+        }
+    }
+
+    private class MyRepresenter extends Representer {
+        @Override
+        protected Set<Property> getProperties(Class<? extends Object> type)
+                throws IntrospectionException {
+            Set<Property> set = super.getProperties(type);
+            Set<Property> filtered = new TreeSet<Property>();
+            if (type.equals(BeanToRemoveProperty.class)) {
+                // filter properties
+                for (Property prop : set) {
+                    String name = prop.getName();
+                    if (!name.equals("id")) {
+                        filtered.add(prop);
+                    }
+                }
+            }
+            return filtered;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/RepresentFieldTest.java b/src/test/java/org/yaml/snakeyaml/representer/RepresentFieldTest.java
new file mode 100644
index 0000000..feff49e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/RepresentFieldTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public class RepresentFieldTest extends TestCase {
+
+    public void testRepresent1() {
+        Yaml yaml = new Yaml();
+        WrongJavaBean bean = new WrongJavaBean();
+        bean.packageField = "Value";// the field is present
+        bean.publicField = "Michael Jackson";
+        WrongJavaBean.staticField = "Another value";
+        String output = yaml.dump(bean);
+        assertEquals(
+                "!!org.yaml.snakeyaml.representer.WrongJavaBean {publicField: Michael Jackson}\n",
+                output);
+    }
+
+    public void testWrongNotPublicField() {
+        Yaml yaml = new Yaml();
+        WrongJavaBean bean = new WrongJavaBean();
+        bean.packageField = "Value";// the field is present
+        try {
+            yaml.load("!!org.yaml.snakeyaml.representer.WrongJavaBean {packageField: Gnome}\n");
+            fail("Only public fields can be used.");
+        } catch (Exception e) {
+            // TODO improve the error message - the pointer should be at the
+            // property name, not value
+            assertTrue(e.getMessage().startsWith(
+                    "Cannot create property=packageField for JavaBean=WrongJavaBean"));
+            assertEquals(
+                    "Unable to find property 'packageField' on class: org.yaml.snakeyaml.representer.WrongJavaBean",
+                    e.getCause().getMessage());
+        }
+    }
+
+    public void testStaticField() {
+        Yaml yaml = new Yaml();
+        WrongJavaBean.staticField = "Value";// the field is present
+        try {
+            yaml.load("!!org.yaml.snakeyaml.representer.WrongJavaBean {staticField: Gnome}\n");
+            fail("Static fields cannot be used.");
+        } catch (Exception e) {
+            // TODO improve the error message - the pointer should be at the
+            // property name, not value
+            assertTrue(e.getMessage().startsWith(
+                    "Cannot create property=staticField for JavaBean=WrongJavaBean"));
+            assertEquals(
+                    "Unable to find property 'staticField' on class: org.yaml.snakeyaml.representer.WrongJavaBean",
+                    e.getCause().getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/RepresentIterableTest.java b/src/test/java/org/yaml/snakeyaml/representer/RepresentIterableTest.java
new file mode 100644
index 0000000..339e71d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/RepresentIterableTest.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test @see <a href="http://code.google.com/p/snakeyaml/issues/detail?id=69">issue 69</a>
+ */
+public class RepresentIterableTest extends TestCase {
+
+    public void testIterable() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(new CounterFactory());
+            fail("Iterable should not be treated as sequence by default.");
+        } catch (Exception e) {
+            assertEquals(
+                    "No JavaBean properties found in org.yaml.snakeyaml.representer.RepresentIterableTest$CounterFactory",
+                    e.getMessage());
+        }
+    }
+
+    public void testIterator() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(new Counter(7));
+        assertEquals("[0, 1, 2, 3, 4, 5, 6]\n", output);
+    }
+
+    private class CounterFactory implements Iterable<Integer> {
+        public Iterator<Integer> iterator() {
+            return new Counter(10);
+        }
+    }
+
+    private class Counter implements Iterator<Integer> {
+        private int max = 0;
+        private int counter = 0;
+
+        public Counter(int max) {
+            this.max = max;
+        }
+
+        public boolean hasNext() {
+            return counter < max;
+        }
+
+        public Integer next() {
+            return counter++;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java b/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java
new file mode 100644
index 0000000..a68beaf
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/RepresentTest.java
@@ -0,0 +1,90 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class RepresentTest extends TestCase {
+
+    public void testCustomRepresenter() {
+        Yaml yaml = new Yaml(new MyConstructor(), new MyRepresenter());
+        CustomBean etalon = new CustomBean("A", 1);
+        String output = yaml.dump(etalon);
+        assertEquals("!!Dice 'Ad1'\n", output);
+        CustomBean bean = (CustomBean) yaml.load(output);
+        assertEquals("A", bean.getPrefix());
+        assertEquals(1, bean.getSuffix());
+        assertEquals(etalon, bean);
+    }
+
+    class CustomBean {
+        private String prefix;
+        private int suffix;
+
+        public CustomBean(String prefix, int suffix) {
+            this.prefix = prefix;
+            this.suffix = suffix;
+        }
+
+        public String getPrefix() {
+            return prefix;
+        }
+
+        public int getSuffix() {
+            return suffix;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            CustomBean bean = (CustomBean) obj;
+            return prefix.equals(bean.getPrefix()) && suffix == bean.getSuffix();
+        }
+    }
+
+    class MyRepresenter extends Representer {
+        public MyRepresenter() {
+            this.representers.put(CustomBean.class, new RepresentDice());
+        }
+
+        private class RepresentDice implements Represent {
+            public Node representData(Object data) {
+                CustomBean coin = (CustomBean) data;
+                String value = coin.getPrefix() + "d" + coin.getSuffix();
+                return representScalar(new Tag("!!Dice"), value);
+            }
+        }
+    }
+
+    class MyConstructor extends Constructor {
+        public MyConstructor() {
+            this.yamlConstructors.put(new Tag(Tag.PREFIX + "Dice"), new ConstructDice());
+        }
+
+        private class ConstructDice extends AbstractConstruct {
+            public Object construct(Node node) {
+                String val = (String) constructScalar((ScalarNode) node);
+                return new CustomBean(val.substring(0, 1), new Integer(val.substring(2)));
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/RepresenterTest.java b/src/test/java/org/yaml/snakeyaml/representer/RepresenterTest.java
new file mode 100644
index 0000000..db31062
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/RepresenterTest.java
@@ -0,0 +1,192 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class RepresenterTest extends TestCase {
+
+    public void testRepresenter() {
+        MyBean bean = new MyBean();
+        bean.setName("Gnome");
+        bean.setValid(true);
+        bean.setPrimitive(true);
+        Yaml yaml = new Yaml();
+        assertEquals(
+                "!!org.yaml.snakeyaml.representer.RepresenterTest$MyBean {name: Gnome, primitive: true}\n",
+                yaml.dump(bean));
+    }
+
+    public static class MyBean {
+        private String name;
+        private Boolean valid;
+        private boolean primitive;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Boolean isValid() {
+            return valid;
+        }
+
+        public void setValid(Boolean valid) {
+            this.valid = valid;
+        }
+
+        public boolean isPrimitive() {
+            return primitive;
+        }
+
+        public void setPrimitive(boolean primitive) {
+            this.primitive = primitive;
+        }
+    }
+
+    public void testRepresenterNoConstructorAvailable() {
+        MyBean2 bean = new MyBean2("Gnome", true);
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        assertEquals("!!org.yaml.snakeyaml.representer.RepresenterTest$MyBean2 {valid: true}\n",
+                yaml.dump(bean));
+    }
+
+    public static class MyBean2 {
+        private String name;
+        private Boolean valid;
+
+        public MyBean2(String name, Boolean valid) {
+            this();
+            this.name = name;
+            this.valid = valid;
+        }
+
+        private MyBean2() {
+            super();
+        }
+
+        private String getName() {
+            return name;
+        }
+
+        public Boolean getValid() {
+            return valid;
+        }
+
+        @Override
+        public String toString() {
+            return getName() + " " + getValid();
+        }
+    }
+
+    public void testRepresenterGetterWithException() {
+        MyBean3 bean = new MyBean3("Gnome", false);
+        DumperOptions options = new DumperOptions();
+        options.setAllowReadOnlyProperties(true);
+        Yaml yaml = new Yaml(options);
+        try {
+            String str = yaml.dump(bean);
+            fail("Exception must be reported: " + str);
+        } catch (Exception e) {
+            assertTrue(true);
+        }
+        // no exception
+        MyBean3 bean2 = new MyBean3("Gnome", true);
+        String str = yaml.dump(bean2);
+        // isValid is no JavaBean property (it must be a primitive then)
+        assertEquals(
+                "isValid property must not be dumped.",
+                "!!org.yaml.snakeyaml.representer.RepresenterTest$MyBean3 {boolProperty: true, name: Gnome}\n",
+                str);
+    }
+
+    public static class MyBean3 {
+        private String name;
+        private Boolean valid;
+        private boolean boolProperty;
+
+        public MyBean3(String name, Boolean valid) {
+            this.name = name;
+            this.valid = valid;
+            boolProperty = true;
+        }
+
+        public String getName() {
+            if (valid) {
+                return name;
+            } else {
+                throw new UnsupportedOperationException("Test.");
+            }
+        }
+
+        public Boolean isValid() {
+            return valid;
+        }
+
+        public boolean isBoolProperty() {
+            return boolProperty;
+        }
+
+        @Override
+        public String toString() {
+            return "MyBean3<" + name + ", " + isValid() + ">";
+        }
+    }
+
+    public void testRepresenterAddNull() {
+        Representer representer = new Representer();
+        try {
+            representer.addClassTag(EmptyBean.class, (Tag) null);
+            fail("Tag must be provided.");
+        } catch (Exception e) {
+            assertEquals("Tag must be provided.", e.getMessage());
+        }
+    }
+
+    public void testRepresenterEmptyBean() {
+        EmptyBean bean = new EmptyBean();
+        Yaml yaml = new Yaml();
+        try {
+            yaml.dump(bean);
+            fail("EmptyBean has empty representation.");
+        } catch (Exception e) {
+            assertEquals(
+                    "No JavaBean properties found in org.yaml.snakeyaml.representer.RepresenterTest$EmptyBean",
+                    e.getMessage());
+        }
+    }
+
+    public static class EmptyBean {
+        private int number;
+
+        public void process() {
+            number += 1;
+        }
+
+        public int obtain() {
+            return number;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/SafeRepresenterTest.java b/src/test/java/org/yaml/snakeyaml/representer/SafeRepresenterTest.java
new file mode 100644
index 0000000..719cf54
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/SafeRepresenterTest.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.reader.StreamReader;
+
+public class SafeRepresenterTest extends TestCase {
+
+    public void testBinaryPattern() {
+        Pattern pattern = StreamReader.NON_PRINTABLE;
+        assertFalse(pattern.matcher("\tAndrey\r\n").find());
+        assertTrue(pattern.matcher("\u0005Andrey").find());
+    }
+
+    public void testFloat() {
+        assertEquals("1.0E12", String.valueOf(new Double("1e12")));
+    }
+
+    public void testNumber() {
+        List<Number> list = new ArrayList<Number>();
+        list.add(new Byte((byte) 3));
+        list.add(new Short((short) 4));
+        list.add(new Integer(5));
+        list.add(new BigInteger("6"));
+        list.add(new Long(7L));
+        list.add(Double.POSITIVE_INFINITY);
+        list.add(Double.NEGATIVE_INFINITY);
+        list.add(Double.NaN);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(list);
+        assertEquals("[3, 4, 5, 6, 7, .inf, -.inf, .NaN]\n", output);
+    }
+
+    public void testDate() {
+        List<Date> list = new ArrayList<Date>();
+        list.add(new Date(1229684761159L));
+        list.add(new Date(1229684761059L));
+        list.add(new Date(1229684761009L));
+        list.add(new Date(1229684761150L));
+        list.add(new Date(1229684761100L));
+        list.add(new Date(1229684761000L));
+        list.add(new Date(1229684760000L));
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(list);
+        assertEquals(
+                "- 2008-12-19T11:06:01.159Z\n- 2008-12-19T11:06:01.059Z\n- 2008-12-19T11:06:01.009Z\n- 2008-12-19T11:06:01.150Z\n- 2008-12-19T11:06:01.100Z\n- 2008-12-19T11:06:01Z\n- 2008-12-19T11:06:00Z\n",
+                output);
+    }
+
+    public void testEmptyArray() {
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(new String[0]);
+        assertEquals("[]\n", output);
+    }
+
+    public void testStyle() {
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(new Integer(1));
+        list.add(new Integer(1));
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("list", list);
+        map.put("name", "Ubuntu");
+        map.put("age", 5);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        assertTrue(output.contains("\"age\": !!int \"5\""));
+        assertTrue(output.contains("\"name\": \"Ubuntu\""));
+        assertTrue(output.contains("- !!int \"1\""));
+    }
+
+    public void testStyle2() {
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(new Integer(1));
+        list.add(new Integer(1));
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put("age", 5);
+        map.put("name", "Ubuntu");
+        map.put("list", list);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        assertEquals("{'age': !!int '5', 'name': 'Ubuntu', 'list': [!!int '1', !!int '1']}\n",
+                output);
+    }
+
+    public void testStyle2Pretty() {
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(new Integer(1));
+        list.add(new Integer(1));
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put("age", 5);
+        map.put("name", "Ubuntu");
+        map.put("list", list);
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
+        options.setDefaultFlowStyle(DumperOptions.FlowStyle.FLOW);
+        options.setPrettyFlow(true);
+        Yaml yaml = new Yaml(options);
+        String output = yaml.dump(map);
+        assertEquals(
+                "{\n  'age': !!int '5',\n  'name': 'Ubuntu',\n  'list': [\n    !!int '1',\n    !!int '1']\n  \n}\n",
+                output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/representer/WrongJavaBean.java b/src/test/java/org/yaml/snakeyaml/representer/WrongJavaBean.java
new file mode 100644
index 0000000..729fc06
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/representer/WrongJavaBean.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.representer;
+
+public class WrongJavaBean {
+    String packageField;
+    static String staticField;
+    public transient String dynamo;
+    public String publicField;
+    private int privateValue;
+
+    public WrongJavaBean() {
+        method();
+    }
+
+    private void method() {
+        privateValue++;
+    }
+
+    @Override
+    public String toString() {
+        return "WrongJavaBean";
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/resolver/ImplicitResolverTest.java b/src/test/java/org/yaml/snakeyaml/resolver/ImplicitResolverTest.java
new file mode 100644
index 0000000..10ff70e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/resolver/ImplicitResolverTest.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.resolver;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Construct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+
+/**
+ * Custom implicit resolver does not apply inside JavaBean declaration <a href=
+ * "http://groups.google.com/group/snakeyaml-core/browse_frm/thread/c75c35a3d9cfcaba"
+ * >mailing list</a> for more information
+ */
+public class ImplicitResolverTest extends TestCase {
+    private static final Tag CFG = new Tag("!cfg");
+
+    public static class ConfigurationConstructor extends Constructor {
+        protected Map<String, String> config = null;
+
+        public ConfigurationConstructor(Map<String, String> config) {
+            this.config = config;
+            this.yamlConstructors.put(CFG, new ConfigObjectConstruct());
+        }
+
+        private class ConfigObjectConstruct extends AbstractConstruct {
+            public Object construct(Node node) {
+                String val = (String) constructScalar((ScalarNode) node);
+                val = val.substring(2, val.length() - 1);
+                return config.get(val);
+            }
+        }
+
+        protected Construct getConstructor(Node node) {
+            if (CFG.equals(node.getTag())) {
+                node.setUseClassConstructor(false);
+            }
+            return super.getConstructor(node);
+        }
+    }
+
+    public static class TestBean {
+        String myval;
+
+        public String getMyval() {
+            return myval;
+        }
+
+        public void setMyval(String myval) {
+            this.myval = myval;
+        }
+
+        public String toString() {
+            return "MyVal: " + myval;
+        }
+    }
+
+    public void testMain() {
+        Map<String, String> config = new HashMap<String, String>();
+        config.put("user.home", "HOME");
+        Constructor constructor = new ConfigurationConstructor(config);
+        constructor.addTypeDescription(new TypeDescription(TestBean.class, "!testbean"));
+        Yaml yaml = new Yaml(constructor);
+        yaml.addImplicitResolver(CFG, Pattern.compile("\\$\\([a-zA-Z\\d\\u002E\\u005F]+\\)"), "$");
+        TestBean bean = (TestBean) yaml.load("!testbean {myval: !cfg $(user.home)}");
+        // System.out.println(bean.toString());
+        assertEquals("Explicit tag must be respected", "HOME", bean.getMyval());
+        bean = (TestBean) yaml.load("!testbean {myval: $(user.home)}");
+        // System.out.println(bean.toString());
+        assertEquals("Implicit tag must be respected", "HOME", bean.getMyval());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java
new file mode 100644
index 0000000..c9a252d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTest.java
@@ -0,0 +1,150 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.resolver;
+
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.AbstractConstruct;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class ResolverTest extends TestCase {
+
+    @SuppressWarnings("unchecked")
+    public void testAddImplicitResolver() {
+        Yaml yaml = new Yaml(new MyConstructor(), new MyRepresenter());
+        Pattern regexp = Pattern.compile("\\d\\d-\\d\\d-\\d\\d\\d");
+        yaml.addImplicitResolver(new Tag(Tag.PREFIX + "Phone"), regexp, "0123456789");
+        Phone phone1 = new Phone("12-34-567");
+        Phone phone2 = new Phone("11-22-333");
+        Phone phone3 = new Phone("44-55-777");
+        List<Phone> etalonList = new ArrayList<Phone>();
+        etalonList.add(phone1);
+        etalonList.add(phone2);
+        etalonList.add(phone3);
+        String output = yaml.dump(etalonList);
+        assertEquals("[12-34-567, 11-22-333, 44-55-777]\n", output);
+        List<Phone> parsedList = (List<Phone>) yaml.load(output);
+        assertEquals(3, parsedList.size());
+        assertEquals(phone1, parsedList.get(0));
+        assertEquals(phone2, parsedList.get(1));
+        assertEquals(phone3, parsedList.get(2));
+        assertEquals(etalonList, parsedList);
+    }
+
+    public void testAddImplicitResolver2() {
+        Yaml yaml = new Yaml(new PointRepresenter());
+        Pattern regexp = Pattern.compile("\\d\\d-\\d\\d-\\d\\d\\d");
+        yaml.addImplicitResolver(new Tag(Tag.PREFIX + "Phone"), regexp, "\0");
+        Pattern regexp2 = Pattern.compile("x\\d_y\\d");
+        // try any scalar, and not only those which start with 'x'
+        yaml.addImplicitResolver(new Tag(Tag.PREFIX + "Point"), regexp2, null);
+        Map<String, Object> map = new LinkedHashMap<String, Object>();
+        map.put("a", new Phone("12-34-567"));
+        map.put("b", new Point(1, 5));
+        String output = yaml.dump(map);
+        assertEquals("{a: 12-34-567, b: x1_y5}\n", output);
+    }
+
+    class Phone {
+        private String number;
+
+        public Phone(String n) {
+            this.number = n;
+        }
+
+        public String getNumber() {
+            return number;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Phone)) {
+                return false;
+            }
+            return toString().equals(obj.toString());
+        }
+
+        @Override
+        public String toString() {
+            return "Phone: " + number;
+        }
+    }
+
+    class MyRepresenter extends Representer {
+        public MyRepresenter() {
+            this.representers.put(Phone.class, new RepresentPhone());
+        }
+
+        private class RepresentPhone implements Represent {
+            public Node representData(Object data) {
+                Phone phone = (Phone) data;
+                String value = phone.getNumber();
+                return representScalar(new Tag(Tag.PREFIX + "Phone"), value);
+            }
+        }
+    }
+
+    class MyConstructor extends Constructor {
+        public MyConstructor() {
+            this.yamlConstructors.put(new Tag(Tag.PREFIX + "Phone"), new ConstructPhone());
+        }
+
+        private class ConstructPhone extends AbstractConstruct {
+            public Object construct(Node node) {
+                String val = (String) constructScalar((ScalarNode) node);
+                return new Phone(val);
+            }
+        }
+    }
+
+    class PointRepresenter extends Representer {
+        public PointRepresenter() {
+            this.representers.put(Point.class, new RepresentPoint());
+            this.representers.put(Phone.class, new RepresentPhone());
+        }
+
+        private class RepresentPoint implements Represent {
+            public Node representData(Object data) {
+                Point phone = (Point) data;
+                String value = "x" + (int) phone.getX() + "_y" + (int) phone.getY();
+                return representScalar(new Tag(Tag.PREFIX + "Point"), value);
+            }
+        }
+
+        private class RepresentPhone implements Represent {
+            public Node representData(Object data) {
+                Phone phone = (Phone) data;
+                String value = phone.getNumber();
+                return representScalar(new Tag(Tag.PREFIX + "Phone"), value);
+            }
+        }
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/resolver/ResolverTupleTest.java b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTupleTest.java
new file mode 100644
index 0000000..4cac651
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/resolver/ResolverTupleTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.resolver;
+
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.nodes.Tag;
+
+public class ResolverTupleTest extends TestCase {
+
+    public void testToString() {
+        ResolverTuple tuple = new ResolverTuple(new Tag("dice"), Pattern.compile("\\d+"));
+        assertEquals("Tuple tag=dice regexp=\\d+", tuple.toString());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/ruby/RubyTest.java b/src/test/java/org/yaml/snakeyaml/ruby/RubyTest.java
new file mode 100644
index 0000000..7eaae71
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/ruby/RubyTest.java
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.ruby;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class RubyTest extends TestCase {
+
+    public void testParse() {
+        TestObject result = parseObject(Util.getLocalResource("ruby/ruby1.yaml"));
+        assertNotNull(result);
+        assertEquals(0, result.getSub1().getAtt2());
+        assertEquals("MyString", result.getSub2().getAtt1());
+        assertEquals(1, result.getSub2().getAtt2().size());
+        assertEquals(12345, result.getSub2().getAtt3());
+    }
+
+    public void testEmitNoTags() {
+        TestObject result = parseObject(Util.getLocalResource("ruby/ruby1.yaml"));
+        DumperOptions options = new DumperOptions();
+        options.setExplicitStart(true);
+        Yaml yaml2 = new Yaml(options);
+        String output = yaml2.dumpAsMap(result);
+        assertFalse("No tags expected.", output.contains("Sub1"));
+        // System.out.println(output);
+        // parse back. Without tags it shall still work
+        Yaml beanLoader = new Yaml();
+        TestObject result2 = beanLoader.loadAs(output, TestObject.class);
+        assertEquals(0, result2.getSub1().getAtt2());
+        assertEquals("MyString", result2.getSub2().getAtt1());
+        assertEquals(1, result2.getSub2().getAtt2().size());
+        assertEquals(12345, result2.getSub2().getAtt3());
+    }
+
+    public void testEmitWithTags() {
+        TestObject result = parseObject(Util.getLocalResource("ruby/ruby1.yaml"));
+        DumperOptions options = new DumperOptions();
+        options.setExplicitStart(true);
+        Representer repr = new Representer();
+        repr.addClassTag(TestObject.class, new Tag("!ruby/object:Test::Module::Object"));
+        repr.addClassTag(Sub1.class, new Tag("!ruby/object:Test::Module::Sub1"));
+        repr.addClassTag(Sub2.class, new Tag("!ruby/object:Test::Module::Sub2"));
+        Yaml yaml2 = new Yaml(repr, options);
+        String output = yaml2.dump(result);
+        // System.out.println(output);
+        assertTrue("Tags must be present.",
+                output.startsWith("--- !ruby/object:Test::Module::Object"));
+        assertTrue("Tags must be present: " + output,
+                output.contains("!ruby/object:Test::Module::Sub1"));
+        assertTrue("Tags must be present.", output.contains("!ruby/object:Test::Module::Sub2"));
+        // parse back.
+        TestObject result2 = parseObject(output);
+        assertEquals(0, result2.getSub1().getAtt2());
+        assertEquals("MyString", result2.getSub2().getAtt1());
+        assertEquals(1, result2.getSub2().getAtt2().size());
+        assertEquals(12345, result2.getSub2().getAtt3());
+    }
+
+    public void testEmitWithTags2WithoutTagForParentJavabean() {
+        TestObject result = parseObject(Util.getLocalResource("ruby/ruby1.yaml"));
+        DumperOptions options = new DumperOptions();
+        options.setExplicitStart(true);
+        Representer repr = new Representer();
+        repr.addClassTag(Sub1.class, new Tag("!ruby/object:Test::Module::Sub1"));
+        repr.addClassTag(Sub2.class, new Tag("!ruby/object:Test::Module::Sub2"));
+        Yaml yaml2 = new Yaml(repr, options);
+        String output = yaml2.dump(result);
+        // System.out.println(output);
+        assertTrue("Tags must be present.",
+                output.startsWith("--- !!org.yaml.snakeyaml.ruby.TestObject"));
+        assertTrue("Tags must be present: " + output,
+                output.contains("!ruby/object:Test::Module::Sub1"));
+        assertTrue("Tags must be present.", output.contains("!ruby/object:Test::Module::Sub2"));
+        // parse back.
+        TestObject result2 = parseObject(output);
+        assertEquals(0, result2.getSub1().getAtt2());
+        assertEquals("MyString", result2.getSub2().getAtt1());
+        assertEquals(1, result2.getSub2().getAtt2().size());
+        assertEquals(12345, result2.getSub2().getAtt3());
+    }
+
+    private TestObject parseObject(String input) {
+        Constructor con = new Constructor(TestObject.class);
+        con.addTypeDescription(new TypeDescription(TestObject.class,
+                "!ruby/object:Test::Module::Object"));
+        con.addTypeDescription(new TypeDescription(Sub1.class, "!ruby/object:Test::Module::Sub1"));
+        con.addTypeDescription(new TypeDescription(Sub2.class, "!ruby/object:Test::Module::Sub2"));
+
+        Yaml yaml = new Yaml(con);
+        return (TestObject) yaml.load(input);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/ruby/Sub1.java b/src/test/java/org/yaml/snakeyaml/ruby/Sub1.java
new file mode 100644
index 0000000..25618f4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/ruby/Sub1.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.ruby;
+
+import java.util.List;
+
+public class Sub1 {
+    private List<Integer> att1;
+    private int att2;
+    private List<Integer> att3;
+
+    public List<Integer> getAtt1() {
+        return att1;
+    }
+
+    public void setAtt1(List<Integer> att1) {
+        this.att1 = att1;
+    }
+
+    public int getAtt2() {
+        return att2;
+    }
+
+    public void setAtt2(int att2) {
+        this.att2 = att2;
+    }
+
+    public List<Integer> getAtt3() {
+        return att3;
+    }
+
+    public void setAtt3(List<Integer> att3) {
+        this.att3 = att3;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/ruby/Sub2.java b/src/test/java/org/yaml/snakeyaml/ruby/Sub2.java
new file mode 100644
index 0000000..d8548f6
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/ruby/Sub2.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.ruby;
+
+import java.util.List;
+
+public class Sub2 {
+    private String att1;
+    private List<String> att2;
+    private int att3;
+
+    public String getAtt1() {
+        return att1;
+    }
+
+    public void setAtt1(String att1) {
+        this.att1 = att1;
+    }
+
+    public List<String> getAtt2() {
+        return att2;
+    }
+
+    public void setAtt2(List<String> att2) {
+        this.att2 = att2;
+    }
+
+    public int getAtt3() {
+        return att3;
+    }
+
+    public void setAtt3(int att3) {
+        this.att3 = att3;
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/ruby/TestObject.java b/src/test/java/org/yaml/snakeyaml/ruby/TestObject.java
new file mode 100644
index 0000000..2885d09
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/ruby/TestObject.java
@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.ruby;
+
+public class TestObject {
+    private Sub1 sub1;
+    private Sub2 sub2;
+
+    public Sub1 getSub1() {
+        return sub1;
+    }
+
+    public void setSub1(Sub1 sub1) {
+        this.sub1 = sub1;
+    }
+
+    public Sub2 getSub2() {
+        return sub2;
+    }
+
+    public void setSub2(Sub2 sub2) {
+        this.sub2 = sub2;
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/scanner/ConstantTest.java b/src/test/java/org/yaml/snakeyaml/scanner/ConstantTest.java
new file mode 100644
index 0000000..8a39857
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/scanner/ConstantTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import junit.framework.TestCase;
+
+public class ConstantTest extends TestCase {
+
+    public void testHasChar() {
+        assertTrue(Constant.LINEBR.has('\n'));
+        assertTrue(Constant.LINEBR.has('\u0085'));
+        assertFalse(Constant.LINEBR.has(' '));
+    }
+
+    public void testHasStringChar() {
+        assertTrue(Constant.LINEBR.has(' ', " "));
+    }
+
+    public void testHas0() {
+        assertTrue(Constant.LINEBR.has((char) 0, "\0"));
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/scanner/ScannerImplTest.java b/src/test/java/org/yaml/snakeyaml/scanner/ScannerImplTest.java
new file mode 100644
index 0000000..4e19412
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/scanner/ScannerImplTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import java.util.LinkedList;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.reader.StreamReader;
+import org.yaml.snakeyaml.tokens.BlockEndToken;
+import org.yaml.snakeyaml.tokens.BlockMappingStartToken;
+import org.yaml.snakeyaml.tokens.KeyToken;
+import org.yaml.snakeyaml.tokens.ScalarToken;
+import org.yaml.snakeyaml.tokens.StreamEndToken;
+import org.yaml.snakeyaml.tokens.StreamStartToken;
+import org.yaml.snakeyaml.tokens.Token;
+import org.yaml.snakeyaml.tokens.ValueToken;
+
+public class ScannerImplTest extends TestCase {
+
+    public void testGetToken() {
+        String data = "string: abcd";
+        StreamReader reader = new StreamReader(data);
+        Scanner scanner = new ScannerImpl(reader);
+        Mark dummy = new Mark("dummy", 0, 0, 0, "", 0);
+        LinkedList<Token> etalonTokens = new LinkedList<Token>();
+        etalonTokens.add(new StreamStartToken(dummy, dummy));
+        etalonTokens.add(new BlockMappingStartToken(dummy, dummy));
+        etalonTokens.add(new KeyToken(dummy, dummy));
+        etalonTokens.add(new ScalarToken("string", true, dummy, dummy, (char) 0));
+        etalonTokens.add(new ValueToken(dummy, dummy));
+        etalonTokens.add(new ScalarToken("abcd", true, dummy, dummy, (char) 0));
+        etalonTokens.add(new BlockEndToken(dummy, dummy));
+        etalonTokens.add(new StreamEndToken(dummy, dummy));
+        while (!etalonTokens.isEmpty() && scanner.checkToken(etalonTokens.get(0).getTokenId())) {
+            assertEquals(etalonTokens.removeFirst(), scanner.getToken());
+        }
+        assertFalse("Must contain no more tokens: " + scanner.getToken(),
+                scanner.checkToken(new Token.ID[0]));
+    }
+
+    public void testWrongTab() {
+        Yaml yaml = new Yaml();
+        try {
+            yaml.load("\t  data: 1");
+            fail("TAB cannot start a token.");
+        } catch (Exception e) {
+            assertEquals(
+                    "while scanning for the next token\n"
+                            + "found character '\\t(TAB)' that cannot start any token. (Do not use \\t(TAB) for indentation)\n"
+                            + " in 'string', line 1, column 1:\n" + "    \t  data: 1\n" + "    ^\n",
+                    e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/scanner/SimpleKeyTest.java b/src/test/java/org/yaml/snakeyaml/scanner/SimpleKeyTest.java
new file mode 100644
index 0000000..8c577f3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/scanner/SimpleKeyTest.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.scanner;
+
+import junit.framework.TestCase;
+
+public class SimpleKeyTest extends TestCase {
+
+    public void testToString() {
+        SimpleKey key = new SimpleKey(1, false, 5, 3, 2, null);
+        assertTrue(key.toString().contains("SimpleKey"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/serializer/AnchorGeneratorTest.java b/src/test/java/org/yaml/snakeyaml/serializer/AnchorGeneratorTest.java
new file mode 100644
index 0000000..82c04fb
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/serializer/AnchorGeneratorTest.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import junit.framework.TestCase;
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AnchorGeneratorTest extends TestCase {
+
+    public void testNext() {
+        AnchorGenerator generator = new NumberAnchorGenerator(0);
+        assertEquals("id001", generator.nextAnchor(null));
+        assertEquals("id002", generator.nextAnchor(null));
+    }
+
+    public void testCustomGenerator() {
+        List<Object> list = new ArrayList<Object>();
+        list.add("data123");
+        list.add(list);
+        Yaml yaml1 = new Yaml();
+        String output = yaml1.dump(list);
+        assertEquals("&id001\n" +
+                "- data123\n" +
+                "- *id001\n", output);
+
+
+        DumperOptions options = new DumperOptions();
+        Yaml yaml2 = new Yaml(options);
+        options.setAnchorGenerator(new Gener(3));
+        String output2 = yaml2.dump(list);
+        assertEquals("&list-id004\n" +
+                "- data123\n" +
+                "- *list-id004\n", output2);
+    }
+
+    class Gener extends NumberAnchorGenerator {
+
+        public Gener(int lastAnchorId) {
+            super(lastAnchorId);
+        }
+
+        public String nextAnchor(Node node) {
+            if (node.getTag() == Tag.SEQ)
+                return "list-" + super.nextAnchor(node);
+            else
+                return super.nextAnchor(node);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/serializer/SerializerTest.java b/src/test/java/org/yaml/snakeyaml/serializer/SerializerTest.java
new file mode 100644
index 0000000..69eaca4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/serializer/SerializerTest.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.serializer;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.emitter.Emitter;
+import org.yaml.snakeyaml.nodes.ScalarNode;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.resolver.Resolver;
+
+public class SerializerTest extends TestCase {
+    private Serializer serializer;
+
+    @Override
+    protected void setUp() {
+        DumperOptions config = new DumperOptions();
+        StringWriter writer = new StringWriter();
+        serializer = new Serializer(new Emitter(writer, config), new Resolver(), config, null);
+    }
+
+    public void testSerializerIsAlreadyOpened() throws IOException {
+        serializer.open();
+        try {
+            serializer.open();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals("serializer is already opened", e.getMessage());
+        }
+    }
+
+    public void testSerializerIsClosed1() throws IOException {
+        serializer.open();
+        serializer.close();
+        try {
+            serializer.open();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals("serializer is closed", e.getMessage());
+        }
+    }
+
+    public void testSerializerIsClosed2() throws IOException {
+        serializer.open();
+        serializer.close();
+        try {
+            serializer.serialize(new ScalarNode(new Tag("!foo"), "bar", null, null, (char) 0));
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals("serializer is closed", e.getMessage());
+        }
+    }
+
+    public void testSerializerIsClosed3() throws IOException {
+        serializer.open();
+        serializer.close();
+        serializer.close();// no problem to close twice
+    }
+
+    public void testSerializerIsNotOpened1() throws IOException {
+        try {
+            serializer.close();
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals("serializer is not opened", e.getMessage());
+        }
+    }
+
+    public void testSerializerIsNotOpened2() throws IOException {
+        try {
+            serializer.serialize(new ScalarNode(new Tag("!foo"), "bar", null, null, (char) 0));
+            fail();
+        } catch (RuntimeException e) {
+            assertEquals("serializer is not opened", e.getMessage());
+        }
+    }
+
+    public void testGenerateAnchor() {
+        NumberFormat format = NumberFormat.getNumberInstance();
+        format.setMinimumIntegerDigits(3);
+        String anchor = format.format(3L);
+        assertEquals("003", anchor);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/stress/ParallelTest.java b/src/test/java/org/yaml/snakeyaml/stress/ParallelTest.java
new file mode 100644
index 0000000..3bae53f
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/stress/ParallelTest.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.stress;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Invoice;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Test that Yaml instances are independent and can be used in multiple threads.
+ */
+public class ParallelTest extends TestCase {
+    private int progress = 0;
+    private int MAX = 5;
+
+    public void testPerfomance() {
+        String doc = Util.getLocalResource("specification/example2_27.yaml");
+        for (int i = 0; i < MAX; i++) {
+            Worker worker = new Worker(i, doc);
+            Thread thread = new Thread(worker);
+            thread.start();
+        }
+        while (progress < MAX - 1) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                fail(e.getMessage());
+            }
+        }
+    }
+
+    private class Worker implements Runnable {
+        private int id;
+        private String doc;
+
+        public Worker(int id, String doc) {
+            this.id = id;
+            this.doc = doc;
+        }
+
+        public void run() {
+            System.out.println("Started: " + id);
+            Yaml loader = new Yaml();
+            long time1 = System.nanoTime();
+            int cycles = 200;
+            for (int i = 0; i < cycles; i++) {
+                Invoice invoice = loader.loadAs(doc, Invoice.class);
+                assertNotNull(invoice);
+            }
+            long time2 = System.nanoTime();
+            float duration = ((time2 - time1) / 1000000) / (float) cycles;
+            System.out.println("Duration of " + id + " was " + duration + " ms/load.");
+            progress++;
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/stress/StressEmitterTest.java b/src/test/java/org/yaml/snakeyaml/stress/StressEmitterTest.java
new file mode 100644
index 0000000..5185e02
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/stress/StressEmitterTest.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.stress;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.yaml.snakeyaml.Invoice;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+
+public class StressEmitterTest extends TestCase {
+
+    public static void main(String args[]) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(StressEmitterTest.class);
+    }
+
+    public void testPerformance() {
+        Yaml loader = new Yaml();
+        Invoice invoice = loader.loadAs(Util.getLocalResource("specification/example2_27.yaml"),
+                Invoice.class);
+        Yaml dumper = new Yaml();
+        long time1 = System.nanoTime();
+        dumper.dumpAsMap(invoice);
+        long time2 = System.nanoTime();
+        float duration = (time2 - time1) / 1000000;
+        System.out.println("\nSingle dump was " + duration + " ms.");
+
+        int[] range = new int[] { 1000, 2000 /* , 8000 */};
+        System.out.println("\nOne instance.");
+        for (int number : range) {
+            time1 = System.nanoTime();
+            for (int i = 0; i < number; i++) {
+                dumper.dump(invoice);
+            }
+            time2 = System.nanoTime();
+            duration = ((time2 - time1) / 1000000) / (float) number;
+            System.out.println("Duration for r=" + number + " was " + duration + " ms/dump.");
+            // cobertura may make it very slow
+            if (duration > 3) {
+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);
+            }
+        }
+
+        System.out.println("\nMany instances.");
+        for (int number : range) {
+            time1 = System.nanoTime();
+            for (int i = 0; i < number; i++) {
+                dumper = new Yaml();
+                dumper.dumpAsMap(invoice);
+            }
+            time2 = System.nanoTime();
+            duration = ((time2 - time1) / 1000000) / (float) number;
+            System.out.println("Duration for r=" + number + " was " + duration + " ms/dump.");
+            // cobertura may make it very slow
+            if (duration > 3) {
+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);
+            }
+            // assertTrue("duration=" + duration, duration < 3);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/stress/StressTest.java b/src/test/java/org/yaml/snakeyaml/stress/StressTest.java
new file mode 100644
index 0000000..299d1a3
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/stress/StressTest.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.stress;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.yaml.snakeyaml.Invoice;
+import org.yaml.snakeyaml.Util;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.constructor.Constructor;
+
+public class StressTest extends TestCase {
+    String doc;
+
+    public static void main(String args[]) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(StressTest.class);
+    }
+
+    public void setUp() {
+        doc = Util.getLocalResource("specification/example2_27.yaml");
+    }
+
+    public void testPerformance() {
+        long time1 = System.nanoTime();
+        new Yaml(new Constructor(Invoice.class));
+        long time2 = System.nanoTime();
+        float duration = (time2 - time1) / 1000000;
+        System.out.println("Init was " + duration + " ms.");
+
+        Yaml loader = new Yaml();
+        time1 = System.nanoTime();
+        loader.loadAs(doc, Invoice.class);
+        time2 = System.nanoTime();
+        duration = (time2 - time1) / 1000000;
+        System.out.println("\nSingle load was " + duration + " ms.");
+
+        loader = new Yaml();
+        int[] range = new int[] { 1000, 2000 /* , 4000, 8000 */};
+        System.out.println("\nOne instance.");
+        for (int number : range) {
+            time1 = System.nanoTime();
+            for (int i = 0; i < number; i++) {
+                loader.loadAs(doc, Invoice.class);
+            }
+            time2 = System.nanoTime();
+            duration = ((time2 - time1) / 1000000) / (float) number;
+            System.out.println("Duration for r=" + number + " was " + duration + " ms/load.");
+            // cobertura may make it very slow
+            if (duration > 3) {
+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);
+            }
+            // assertTrue("duration=" + duration, duration < 3);
+        }
+
+        System.out.println("\nMany instances.");
+        for (int number : range) {
+            time1 = System.nanoTime();
+            for (int i = 0; i < number; i++) {
+                loader = new Yaml();
+                loader.loadAs(doc, Invoice.class);
+            }
+            time2 = System.nanoTime();
+            duration = ((time2 - time1) / 1000000) / (float) number;
+            System.out.println("Duration for r=" + number + " was " + duration + " ms/load.");
+            // cobertura may make it very slow
+            if (duration > 3) {
+                System.err.println("!!!!!! Too long. Expected <1 but was " + duration);
+            }
+            // assertTrue("duration=" + duration, duration < 3);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/AliasTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/AliasTokenTest.java
new file mode 100644
index 0000000..da0fd42
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/AliasTokenTest.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class AliasTokenTest extends TestCase {
+
+    public void testEquals() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        AliasToken token = new AliasToken("*id123", mark, mark);
+        assertFalse(token.equals(mark));
+    }
+
+    public void testGetArguments() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        AliasToken token = new AliasToken("*id123", mark, mark);
+        assertEquals("value=*id123", token.getArguments());
+    }
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        AliasToken token = new AliasToken("&id123", mark, mark);
+        assertEquals(ID.Alias, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/AnchorTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/AnchorTokenTest.java
new file mode 100644
index 0000000..472f7d2
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/AnchorTokenTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class AnchorTokenTest extends TestCase {
+
+    public void testGetArguments() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        AnchorToken token = new AnchorToken("&id123", mark, mark);
+        assertEquals("value=&id123", token.getArguments());
+    }
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        AnchorToken token = new AnchorToken("&id123", mark, mark);
+        assertEquals(ID.Anchor, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/BlockEndTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/BlockEndTokenTest.java
new file mode 100644
index 0000000..3c96859
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/BlockEndTokenTest.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class BlockEndTokenTest extends TestCase {
+
+    public void testGetArguments() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        BlockEndToken token = new BlockEndToken(mark, mark);
+        assertEquals("", token.getArguments());
+    }
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        BlockEndToken token = new BlockEndToken(mark, mark);
+        assertEquals(ID.BlockEnd, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/BlockEntryTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/BlockEntryTokenTest.java
new file mode 100644
index 0000000..651a230
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/BlockEntryTokenTest.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class BlockEntryTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        BlockEntryToken token = new BlockEntryToken(mark, mark);
+        assertEquals(ID.BlockEntry, token.getTokenId());
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/BlockSequenceStartTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/BlockSequenceStartTokenTest.java
new file mode 100644
index 0000000..69203d0
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/BlockSequenceStartTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class BlockSequenceStartTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        BlockSequenceStartToken token = new BlockSequenceStartToken(mark, mark);
+        assertEquals(ID.BlockSequenceStart, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/DirectiveTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/DirectiveTokenTest.java
new file mode 100644
index 0000000..f1e8ff1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/DirectiveTokenTest.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class DirectiveTokenTest extends TestCase {
+
+    public void testGetArguments() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        DirectiveToken<Integer> token = new DirectiveToken<Integer>("YAML", null, mark, mark);
+        assertEquals("name=YAML", token.getArguments());
+    }
+
+    public void testInvalidList() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(new Integer(1));
+        try {
+            new DirectiveToken<Integer>("YAML", list, mark, mark);
+            fail("List must have 2 values.");
+        } catch (Exception e) {
+            assertEquals("Two strings must be provided instead of 1", e.getMessage());
+        }
+    }
+
+    public void testTag() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        List<String> list = new ArrayList<String>();
+        list.add("!foo");
+        list.add("!bar");
+        DirectiveToken<String> token = new DirectiveToken<String>("TAG", list, mark, mark);
+        assertEquals("name=TAG, value=[!foo, !bar]", token.getArguments());
+    }
+
+    public void testList() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        List<Integer> list = new ArrayList<Integer>();
+        list.add(new Integer(1));
+        list.add(new Integer(1));
+        DirectiveToken<Integer> token = new DirectiveToken<Integer>("YAML", list, mark, mark);
+        assertEquals("name=YAML, value=[1, 1]", token.getArguments());
+    }
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        DirectiveToken<Integer> token = new DirectiveToken<Integer>("YAML", null, mark, mark);
+        assertEquals(ID.Directive, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/DocumentEndTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/DocumentEndTokenTest.java
new file mode 100644
index 0000000..a6955ba
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/DocumentEndTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class DocumentEndTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        DocumentEndToken token = new DocumentEndToken(mark, mark);
+        assertEquals(ID.DocumentEnd, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/DocumentStartTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/DocumentStartTokenTest.java
new file mode 100644
index 0000000..f7fa01e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/DocumentStartTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class DocumentStartTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        DocumentStartToken token = new DocumentStartToken(mark, mark);
+        assertEquals(ID.DocumentStart, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/FlowEntryTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/FlowEntryTokenTest.java
new file mode 100644
index 0000000..3293919
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/FlowEntryTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class FlowEntryTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        FlowEntryToken token = new FlowEntryToken(mark, mark);
+        assertEquals(ID.FlowEntry, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/FlowMappingStartTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/FlowMappingStartTokenTest.java
new file mode 100644
index 0000000..4548552
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/FlowMappingStartTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class FlowMappingStartTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        FlowMappingStartToken token = new FlowMappingStartToken(mark, mark);
+        assertEquals(ID.FlowMappingStart, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/FlowSequenceStartTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/FlowSequenceStartTokenTest.java
new file mode 100644
index 0000000..b62e6a8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/FlowSequenceStartTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class FlowSequenceStartTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        FlowSequenceStartToken token = new FlowSequenceStartToken(mark, mark);
+        assertEquals(ID.FlowSequenceStart, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/StreamStartTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/StreamStartTokenTest.java
new file mode 100644
index 0000000..8ae19a4
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/StreamStartTokenTest.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class StreamStartTokenTest extends TestCase {
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        StreamStartToken token = new StreamStartToken(mark, mark);
+        assertEquals(ID.StreamStart, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/tokens/TagTokenTest.java b/src/test/java/org/yaml/snakeyaml/tokens/TagTokenTest.java
new file mode 100644
index 0000000..1194088
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/tokens/TagTokenTest.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.tokens;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.error.Mark;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.yaml.snakeyaml.tokens.Token.ID;
+
+public class TagTokenTest extends TestCase {
+
+    public void testGetArguments() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        TagToken token = new TagToken(new TagTuple("!foo", "!bar"), mark, mark);
+        assertEquals("value=[!foo, !bar]", token.getArguments());
+    }
+
+    public void testNoMarks() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        try {
+            new TagToken(new TagTuple("!foo", "!bar"), null, mark);
+            fail("Token without start mark should not be accepted.");
+        } catch (YAMLException e) {
+            assertEquals("Token requires marks.", e.getMessage());
+        }
+        try {
+            new TagToken(new TagTuple("!foo", "!bar"), mark, null);
+            fail("Token without end mark should not be accepted.");
+        } catch (YAMLException e) {
+            assertEquals("Token requires marks.", e.getMessage());
+        }
+    }
+
+    public void testNoTag() {
+        try {
+            Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+            new TagToken(new TagTuple("!foo", null), mark, mark);
+            fail("Marks must be provided.");
+        } catch (NullPointerException e) {
+            assertEquals("Suffix must be provided.", e.getMessage());
+        }
+    }
+
+    public void testGetTokenId() {
+        Mark mark = new Mark("test1", 0, 0, 0, "*The first line.\nThe last line.", 0);
+        TagToken token = new TagToken(new TagTuple("!foo", "!bar"), mark, mark);
+        assertEquals(ID.Tag, token.getTokenId());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/AbstractTest.java b/src/test/java/org/yaml/snakeyaml/types/AbstractTest.java
new file mode 100644
index 0000000..91fd03c
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/AbstractTest.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.yaml.snakeyaml.Yaml;
+
+public abstract class AbstractTest extends TestCase {
+    @SuppressWarnings("unchecked")
+    protected Map<String, Object> getMap(String data) {
+        Yaml yaml = new Yaml();
+        Map<String, Object> nativeData = (Map<String, Object>) yaml.load(data);
+        return nativeData;
+    }
+
+    protected Object load(String data) {
+        Yaml yaml = new Yaml();
+        Object obj = yaml.load(data);
+        return obj;
+    }
+
+    protected String dump(Object data) {
+        Yaml yaml = new Yaml();
+        return yaml.dump(data);
+    }
+
+    protected Object getMapValue(String data, String key) {
+        Map<String, Object> nativeData = getMap(data);
+        return nativeData.get(key);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/BinaryTagTest.java b/src/test/java/org/yaml/snakeyaml/types/BinaryTagTest.java
new file mode 100644
index 0000000..82ff947
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/BinaryTagTest.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @see <a href="http://yaml.org/type/binary.html"></a>
+ */
+public class BinaryTagTest extends AbstractTest {
+    String line1 = "R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5";
+    String line2 = "OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+";
+    String line3 = "+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC";
+    String line4 = "AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=";
+    String content = line1 + line2 + line3 + line4;
+
+    public void testBinary() {
+        byte[] binary = (byte[]) getMapValue("canonical: !!binary " + content, "canonical");
+        assertEquals((byte) 'G', binary[0]);
+        assertEquals((byte) 'I', binary[1]);
+        assertEquals((byte) 'F', binary[2]);
+        assertEquals((byte) '8', binary[3]);
+        assertEquals((byte) '9', binary[4]);
+    }
+
+    public void testBinary2() {
+        byte[] binary = (byte[]) load("!!binary \"MQ==\"");
+        assertEquals(1, binary.length);
+        assertEquals((byte) '1', binary[0]);
+    }
+
+    public void testBinaryTag() {
+        byte[] binary = (byte[]) getMapValue("canonical: !<tag:yaml.org,2002:binary> " + content,
+                "canonical");
+        assertEquals((byte) 'G', binary[0]);
+        assertEquals((byte) 'I', binary[1]);
+        assertEquals((byte) 'F', binary[2]);
+        assertEquals((byte) '8', binary[3]);
+        assertEquals((byte) '9', binary[4]);
+    }
+
+    public void testBinaryOut() throws IOException {
+        byte[] data = "GIF89\tbi\u0003\u0000nary\n\u001Fimage\n".getBytes("ISO-8859-1");
+        Map<String, String> map = new HashMap<String, String>();
+        String value = new String(data, "ISO-8859-1");
+        map.put("canonical", value);
+        String output = dump(map);
+        assertEquals("canonical: !!binary |-\n  R0lGODkJYmkDAG5hcnkKH2ltYWdlCg==\n", output);
+    }
+
+    public void testByteArray() {
+        byte[] data = { 8, 14, 15, 10, 126, 32, 65, 65, 65 };
+        String output = dump(data);
+        assertEquals("!!binary |-\n  CA4PCn4gQUFB\n", output);
+        byte[] parsed = (byte[]) load(output);
+        assertEquals(data.length, parsed.length);
+        for (int i = 0; i < data.length; i++) {
+            assertEquals(data[i], parsed[i]);
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/BoolTagTest.java b/src/test/java/org/yaml/snakeyaml/types/BoolTagTest.java
new file mode 100644
index 0000000..96b2b3a
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/BoolTagTest.java
@@ -0,0 +1,133 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * @see <a href="http://yaml.org/type/bool.html"></a>
+ */
+public class BoolTagTest extends AbstractTest {
+    public void testBool() {
+        assertEquals(Boolean.TRUE, getMapValue("canonical: true", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("answer: NO", "answer"));
+        assertEquals(Boolean.TRUE, getMapValue("logical: True", "logical"));
+        assertEquals(Boolean.TRUE, getMapValue("option: on", "option"));
+    }
+
+    public void testBoolCanonical() {
+        assertEquals(Boolean.TRUE, getMapValue("canonical: Yes", "canonical"));
+        assertEquals(Boolean.TRUE, getMapValue("canonical: yes", "canonical"));
+        assertEquals(Boolean.TRUE, getMapValue("canonical: YES", "canonical"));
+        assertEquals("yES", getMapValue("canonical: yES", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: No", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: NO", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: no", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: off", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: Off", "canonical"));
+        assertEquals(Boolean.FALSE, getMapValue("canonical: OFF", "canonical"));
+        assertEquals(Boolean.TRUE, getMapValue("canonical: ON", "canonical"));
+        assertEquals(Boolean.TRUE, getMapValue("canonical: On", "canonical"));
+        assertEquals(Boolean.TRUE, getMapValue("canonical: on", "canonical"));
+        // it looks like it is against the specification but it is like in
+        // PyYAML
+        assertEquals("n", getMapValue("canonical: n", "canonical"));
+        assertEquals("N", getMapValue("canonical: N", "canonical"));
+        assertEquals("y", getMapValue("canonical: y", "canonical"));
+        assertEquals("Y", getMapValue("canonical: Y", "canonical"));
+    }
+
+    public void testBoolShorthand() {
+        assertEquals(Boolean.TRUE, getMapValue("boolean: !!bool true", "boolean"));
+    }
+
+    public void testBoolTag() {
+        assertEquals(Boolean.TRUE,
+                getMapValue("boolean: !<tag:yaml.org,2002:bool> true", "boolean"));
+    }
+
+    public void testBoolOut() {
+        Map<String, Boolean> map = new HashMap<String, Boolean>();
+        map.put("boolean", Boolean.TRUE);
+        String output = dump(map);
+        assertTrue(output, output.contains("boolean: true"));
+    }
+
+    public void testBoolOutAsYes() {
+        Yaml yaml = new Yaml(new BoolRepresenter("YES"));
+        String output = yaml.dump(true);
+        assertEquals("YES\n", output);
+    }
+
+    /**
+     * test flow style
+     */
+    public void testBoolOutAsEmpty2() {
+        Yaml yaml = new Yaml(new BoolRepresenter("on"));
+        Map<String, Boolean> map = new HashMap<String, Boolean>();
+        map.put("aaa", false);
+        map.put("bbb", true);
+        String output = yaml.dump(map);
+        assertEquals("{aaa: false, bbb: on}\n", output);
+    }
+
+    /**
+     * test block style
+     */
+    public void testBoolOutAsEmpty3() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(new BoolRepresenter("True"), options);
+        Map<String, Boolean> map = new HashMap<String, Boolean>();
+        map.put("aaa", false);
+        map.put("bbb", true);
+        String output = yaml.dump(map);
+        assertEquals("aaa: false\nbbb: True\n", output);
+    }
+
+    private class BoolRepresenter extends Representer {
+        private String value;
+
+        public BoolRepresenter(String value) {
+            super();
+            this.value = value;
+            this.representers.put(Boolean.class, new RepresentBool());
+        }
+
+        // possible values are here http://yaml.org/type/bool.html
+        // y, Y, n, N should not be used
+        private class RepresentBool implements Represent {
+            public Node representData(Object data) {
+                String v;
+                if (Boolean.TRUE.equals(data)) {
+                    v = value;
+                } else {
+                    v = "false";
+                }
+                return representScalar(Tag.BOOL, v);
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/FloatTagTest.java b/src/test/java/org/yaml/snakeyaml/types/FloatTagTest.java
new file mode 100644
index 0000000..c517f66
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/FloatTagTest.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @see <a href="http://yaml.org/type/float.html"></a>
+ */
+public class FloatTagTest extends AbstractTest {
+
+    public void testFloat() {
+        assertEquals(new Double(6.8523015e+5), getMapValue("canonical: 6.8523015e+5", "canonical"));
+        assertEquals(new Double(6.8523015e+5),
+                getMapValue("exponentioal: 685.230_15e+03", "exponentioal"));
+        assertEquals(new Double(6.8523015e+5), getMapValue("fixed: 685_230.15", "fixed"));
+        assertEquals(new Double(6.8523015e+5),
+                getMapValue("sexagesimal: 190:20:30.15", "sexagesimal"));
+        assertEquals(Double.NEGATIVE_INFINITY,
+                getMapValue("negative infinity: -.inf", "negative infinity"));
+        assertEquals(Double.NaN, getMapValue("not a number: .NaN", "not a number"));
+    }
+
+    public void testFloatShorthand() {
+        assertEquals(new Double(1), getMapValue("number: !!float 1", "number"));
+    }
+
+    public void testFloatTag() {
+        assertEquals(new Double(1), getMapValue("number: !<tag:yaml.org,2002:float> 1", "number"));
+    }
+
+    public void testFloatOut() {
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("number", new Double(1));
+        String output = dump(map);
+        assertEquals("{number: 1.0}\n", output);
+    }
+
+    public void testBasicDoubleScalarLoad() {
+        assertEquals(new Double(47.0), load("47.0"));
+        assertEquals(new Double(0.0), load("0.0"));
+        assertEquals(new Double(-1.0), load("-1.0"));
+    }
+
+    public void testDumpStr() {
+        assertEquals("'1.0'\n", dump("1.0"));
+    }
+
+    public void testDump() {
+        assertEquals("1.0\n", dump(1.0));
+    }
+
+    /**
+     * to test http://code.google.com/p/snakeyaml/issues/detail?id=130
+     */
+    public void testScientificFloatWithoutDecimalDot() {
+        assertEquals(new Double(8e-06), load("8e-06"));
+        assertEquals(new Double(8e06), load("8e06"));
+        assertEquals(new Double(8e06), load("8e+06"));
+        assertEquals(new Double(8000e06), load("8_000e06"));
+        assertEquals(new Double(123e-06), load("123e-06"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/IntTagTest.java b/src/test/java/org/yaml/snakeyaml/types/IntTagTest.java
new file mode 100644
index 0000000..2adb9c8
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/IntTagTest.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @see <a href="http://yaml.org/type/int.html"></a>
+ */
+public class IntTagTest extends AbstractTest {
+
+    public void testInt() {
+        assertEquals(new Integer(685230), getMapValue("canonical: 685230", "canonical"));
+        assertEquals(new Integer(685230), getMapValue("number: 685_230", "number"));
+        assertEquals(new Integer(685230), getMapValue("decimal: +685230", "decimal"));
+        assertEquals(new Integer(-685230), getMapValue("number: -685230", "number"));
+        assertEquals(new Integer(685230), getMapValue("octal: 02472256", "octal"));
+        assertEquals(new Integer(685230), getMapValue("hexadecimal: 0x_0A_74_AE", "hexadecimal"));
+        assertEquals(new Integer(685230),
+                getMapValue("binary: 0b1010_0111_0100_1010_1110", "binary"));
+        assertEquals(new Integer(685230), getMapValue("sexagesimal: 190:20:30", "sexagesimal"));
+        assertEquals(new Integer(0), load("0"));
+        assertEquals(new Integer(0), load("-0"));
+        assertEquals(new Integer(0), load("+0"));
+        assertEquals(Integer.MIN_VALUE, load(dump(Integer.MIN_VALUE)));
+        assertEquals(Integer.MAX_VALUE, load(dump(Integer.MAX_VALUE)));
+    }
+
+    public void testBigInt() {
+        assertEquals(new Long(922337203685477580L), load("922337203685477580"));
+        assertEquals(new BigInteger("9223372036854775809999999999"),
+                load("9223372036854775809999999999"));
+        assertEquals(Long.MIN_VALUE, load("-9223372036854775808"));
+    }
+
+    public void testIntShorthand() {
+        assertEquals(new Integer(1), getMapValue("number: !!int 1", "number"));
+    }
+
+    public void testIntTag() {
+        assertEquals(new Integer(1), getMapValue("number: !<tag:yaml.org,2002:int> 1", "number"));
+    }
+
+    public void testIntOut() {
+        Map<String, Integer> map = new HashMap<String, Integer>();
+        map.put("number", new Integer(1));
+        String output = dump(map);
+        assertTrue(output.contains("number: 1"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/MapTagTest.java b/src/test/java/org/yaml/snakeyaml/types/MapTagTest.java
new file mode 100644
index 0000000..b6449a7
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/MapTagTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.Map;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/map.html"></a>
+ */
+public class MapTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testMap() {
+        YamlDocument document = new YamlDocument("types/map.yaml");
+        Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) document
+                .getNativeData();
+        assertEquals(2, map.size());
+        Map<String, String> map1 = (Map<String, String>) map.get("Block style");
+        assertEquals(3, map1.size());
+        assertEquals("Evans", map1.get("Clark"));
+        assertEquals("Ingerson", map1.get("Brian"));
+        assertEquals("Ben-Kiki", map1.get("Oren"));
+        //
+        Map<String, String> map2 = (Map<String, String>) map.get("Flow style");
+        assertEquals(3, map2.size());
+        assertEquals("Evans", map2.get("Clark"));
+        assertEquals("Ingerson", map2.get("Brian"));
+        assertEquals("Ben-Kiki", map2.get("Oren"));
+        //
+        assertEquals(map1, map2);
+        assertNotSame(map1, map2);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testMapYaml11() {
+        YamlDocument document = new YamlDocument("types/map_mixed_tags.yaml");
+        Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) document
+                .getNativeData();
+        assertEquals(2, map.size());
+        Map<String, String> map1 = (Map<String, String>) map.get("Block style");
+        assertEquals(3, map1.size());
+        assertEquals("Evans", map1.get("Clark"));
+        assertEquals("Ingerson", map1.get("Brian"));
+        assertEquals("Ben-Kiki", map1.get("Oren"));
+        //
+        Map<String, String> map2 = (Map<String, String>) map.get("Flow style");
+        assertEquals(3, map2.size());
+        assertEquals("Evans", map2.get("Clark"));
+        assertEquals("Ingerson", map2.get("Brian"));
+        assertEquals("Ben-Kiki", map2.get("Oren"));
+        //
+        assertEquals(map1, map2);
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/MergeTagTest.java b/src/test/java/org/yaml/snakeyaml/types/MergeTagTest.java
new file mode 100644
index 0000000..9af5d47
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/MergeTagTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/merge.html"></a>
+ */
+public class MergeTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testMerge() {
+        YamlDocument document = new YamlDocument("types/merge.yaml");
+        List<Object> list = (List<Object>) document.getNativeData();
+        assertEquals(8, list.size());
+        Map<Object, Object> center = (Map<Object, Object>) list.get(0);
+        assertEquals(2, center.size());
+        assertEquals(new Integer(1), center.get("x"));
+        assertEquals(new Integer(2), center.get("y"));
+        //
+        Map<Object, Object> left = (Map<Object, Object>) list.get(1);
+        assertEquals(2, left.size());
+        assertEquals(left.get("x").getClass().toString(), new Integer(0), left.get("x"));
+        assertEquals(new Integer(2), left.get("y"));
+        //
+        Map<Object, Object> big = (Map<Object, Object>) list.get(2);
+        assertEquals(1, big.size());
+        assertEquals(new Integer(10), big.get("r"));
+        //
+        Map<Object, Object> small = (Map<Object, Object>) list.get(3);
+        assertEquals(1, small.size());
+        assertEquals(new Integer(1), small.get("r"));
+        // Explicit keys
+        Map<Object, Object> explicit = (Map<Object, Object>) list.get(4);
+        assertEquals(4, explicit.size());
+        assertEquals(new Integer(1), explicit.get("x"));
+        assertEquals(new Integer(2), explicit.get("y"));
+        assertEquals(new Integer(10), explicit.get("r"));
+        assertEquals("center/big", explicit.get("label"));
+        // Merge one map
+        Map<Object, Object> merged1 = (Map<Object, Object>) list.get(5);
+        assertEquals(explicit, merged1);
+        assertNotSame(explicit, merged1);
+        // Merge multiple maps
+        Map<Object, Object> merged2 = (Map<Object, Object>) list.get(6);
+        assertEquals(explicit, merged2);
+        assertNotSame(explicit, merged2);
+        // Override
+        Map<Object, Object> merged3 = (Map<Object, Object>) list.get(7);
+        assertEquals(explicit, merged3);
+        assertNotSame(explicit, merged3);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/NullTagTest.java b/src/test/java/org/yaml/snakeyaml/types/NullTagTest.java
new file mode 100644
index 0000000..b3cd966
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/NullTagTest.java
@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.FlowStyle;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+/**
+ * @see <a href="http://yaml.org/type/null.html"></a>
+ */
+public class NullTagTest extends AbstractTest {
+
+    public void testNull() {
+        assertNull("Got: '" + load("---\n") + "'", load("---\n"));
+        assertNull(load("---\n..."));
+        assertNull(load("---\n...\n"));
+        assertNull(load("\n"));
+        assertNull(load(""));
+        assertNull(load(" "));
+        assertNull(load("~"));
+        assertNull(load("---\n~"));
+        assertNull(load("null"));
+        assertNull(load("Null"));
+        assertNull(load("NULL"));
+        assertNull(getMapValue("empty:\n", "empty"));
+        assertNull(getMapValue("canonical: ~", "canonical"));
+        assertNull(getMapValue("english: null", "english"));
+        assertNull(getMapValue("english: Null", "english"));
+        assertNull(getMapValue("english: NULL", "english"));
+        assertEquals("null key", getMapValue("~: null key\n", null));
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testSequenceNull() {
+        String input = "---\n# This sequence has five\n# entries, two have values.\nsparse:\n  - ~\n  - 2nd entry\n  -\n  - 4th entry\n  - Null\n";
+        List<String> parsed = (List<String>) getMapValue(input, "sparse");
+        assertEquals(5, parsed.size());
+        assertNull(parsed.get(0));
+        assertEquals("2nd entry", parsed.get(1));
+        assertNull("Got: '" + parsed.get(2) + "'", parsed.get(2));
+        assertEquals("4th entry", parsed.get(3));
+        assertNull(parsed.get(4));
+    }
+
+    public void testNullInMap() {
+        String input = "key1: null\n~: value1";
+        Map<String, Object> parsed = getMap(input);
+        assertEquals(2, parsed.size());
+        assertTrue(parsed.containsKey(null));
+        Object value1 = parsed.get(null);
+        assertEquals("value1", value1);
+        //
+        assertNull(parsed.get("key1"));
+        //
+        assertFalse(getMap("key2: value2").containsKey(null));
+    }
+
+    public void testNullShorthand() {
+        assertNull(getMapValue("nothing: !!null null", "nothing"));
+    }
+
+    public void testNullTag() {
+        assertNull(getMapValue("nothing: !<tag:yaml.org,2002:null> null", "nothing"));
+    }
+
+    public void testNullOut() {
+        String output = dump(null);
+        assertEquals("null\n", output);
+    }
+
+    public void testNullOutAsEmpty() {
+        Yaml yaml = new Yaml(new NullRepresenter());
+        String output = yaml.dump(null);
+        assertEquals("", output);
+    }
+
+    /**
+     * test flow style
+     */
+    public void testNullOutAsEmpty2() {
+        Yaml yaml = new Yaml(new NullRepresenter());
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("aaa", "foo");
+        map.put("bbb", null);
+        String output = yaml.dump(map);
+        assertEquals("{aaa: foo, bbb: !!null ''}\n", output);
+    }
+
+    /**
+     * test block style
+     */
+    public void testBoolOutAsEmpty3() {
+        DumperOptions options = new DumperOptions();
+        options.setDefaultFlowStyle(FlowStyle.BLOCK);
+        Yaml yaml = new Yaml(new NullRepresenter(), options);
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("aaa", "foo");
+        map.put("bbb", null);
+        String output = yaml.dump(map);
+        assertEquals("aaa: foo\nbbb:\n", output);
+    }
+
+    private class NullRepresenter extends Representer {
+        public NullRepresenter() {
+            super();
+            // null representer is exceptional and it is stored as an instance
+            // variable.
+            this.nullRepresenter = new RepresentNull();
+        }
+
+        private class RepresentNull implements Represent {
+            public Node representData(Object data) {
+                // possible values are here http://yaml.org/type/null.html
+                return representScalar(Tag.NULL, "");
+            }
+        }
+    }
+
+    public void testNoAnchors() {
+        List<String> list = new ArrayList<String>(3);
+        list.add(null);
+        list.add("value");
+        list.add(null);
+        Yaml yaml = new Yaml();
+        String output = yaml.dump(list);
+        assertEquals("Null values must not get anchors and aliases.", "[null, value, null]\n",
+                output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/OmapTagTest.java b/src/test/java/org/yaml/snakeyaml/types/OmapTagTest.java
new file mode 100644
index 0000000..2811dea
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/OmapTagTest.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.Map;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/omap.html"></a>
+ */
+public class OmapTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testOmap() {
+        YamlDocument document = new YamlDocument("types/omap.yaml");
+        Map<String, Map<String, String>> map = (Map<String, Map<String, String>>) document
+                .getNativeData();
+        assertEquals(2, map.size());
+        Map<String, String> map1 = (Map<String, String>) map.get("Bestiary");
+        assertEquals(3, map1.size());
+        assertEquals("African pig-like ant eater. Ugly.", map1.get("aardvark"));
+        assertEquals("South-American ant eater. Two species.", map1.get("anteater"));
+        assertEquals("South-American constrictor snake. Scaly.", map1.get("anaconda"));
+        //
+        Map<String, String> map2 = (Map<String, String>) map.get("Numbers");
+        assertEquals(3, map2.size());
+        assertEquals(new Integer(1), map2.get("one"));
+        assertEquals(new Integer(2), map2.get("two"));
+        assertEquals(new Integer(3), map2.get("three"));
+    }
+
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/PairsTagTest.java b/src/test/java/org/yaml/snakeyaml/types/PairsTagTest.java
new file mode 100644
index 0000000..2f3c73e
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/PairsTagTest.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/pairs.html"></a>
+ */
+public class PairsTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testPairs() {
+        YamlDocument document = new YamlDocument("types/pairs.yaml", false);
+        Map<String, List<String[]>> map = (Map<String, List<String[]>>) document.getNativeData();
+        assertEquals(2, map.size());
+        List<String[]> list1 = (List<String[]>) map.get("Block tasks");
+        assertEquals(4, list1.size());
+        Object[] tuple1 = list1.get(0);
+        assertEquals(2, tuple1.length);
+        assertEquals("meeting", tuple1[0]);
+        assertEquals("with team.", tuple1[1]);
+        //
+
+        Object[] tuple2 = list1.get(1);
+        assertEquals(2, tuple2.length);
+        assertEquals("meeting", tuple2[0]);
+        assertEquals("with boss.", tuple2[1]);
+        //
+
+        Object[] tuple3 = list1.get(2);
+        assertEquals(2, tuple3.length);
+        assertEquals("break", tuple3[0]);
+        assertEquals("lunch.", tuple3[1]);
+        //
+
+        Object[] tuple4 = list1.get(3);
+        assertEquals(2, tuple4.length);
+        assertEquals("meeting", tuple4[0]);
+        assertEquals("with client.", tuple4[1]);
+        //
+        List<String[]> list2 = (List<String[]>) map.get("Flow tasks");
+        assertEquals(2, list2.size());
+        Object[] tuple2_1 = list2.get(0);
+        assertEquals(2, tuple2_1.length);
+        assertEquals("meeting", tuple2_1[0]);
+        assertEquals("with team", tuple2_1[1]);
+        //
+        Object[] tuple2_2 = list2.get(1);
+        assertEquals(2, tuple2_2.length);
+        assertEquals("meeting", tuple2_2[0]);
+        assertEquals("with boss", tuple2_2[1]);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/SeqTagTest.java b/src/test/java/org/yaml/snakeyaml/types/SeqTagTest.java
new file mode 100644
index 0000000..0f4123b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/SeqTagTest.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/seq.html"></a>
+ */
+public class SeqTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testSeq() {
+        YamlDocument document = new YamlDocument("types/seq.yaml");
+        Map<String, List<String>> map = (Map<String, List<String>>) document.getNativeData();
+        assertEquals(2, map.size());
+        List<String> list1 = (List<String>) map.get("Block style");
+        assertEquals(9, list1.size());
+        assertEquals("Mercury", list1.get(0));
+        assertEquals("Venus", list1.get(1));
+        assertEquals("Earth", list1.get(2));
+        assertEquals("Mars", list1.get(3));
+        assertEquals("Jupiter", list1.get(4));
+        assertEquals("Saturn", list1.get(5));
+        assertEquals("Uranus", list1.get(6));
+        assertEquals("Neptune", list1.get(7));
+        assertEquals("Pluto", list1.get(8));
+        //
+        List<String> list2 = (List<String>) map.get("Flow style");
+        assertEquals(9, list2.size());
+        assertEquals("Mercury", list2.get(0));
+        assertEquals("Venus", list2.get(1));
+        assertEquals("Earth", list2.get(2));
+        assertEquals("Mars", list2.get(3));
+        assertEquals("Jupiter", list2.get(4));
+        assertEquals("Saturn", list2.get(5));
+        assertEquals("Uranus", list2.get(6));
+        assertEquals("Neptune", list2.get(7));
+        assertEquals("Pluto", list2.get(8));
+        //
+        assertEquals(list1, list2);
+        assertNotSame(list1, list2);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testCollection() {
+        Collection<Integer> collection = new LinkedList<Integer>();
+        collection.add(1);
+        collection.add(2);
+        collection.add(3);
+        String output = dump(collection);
+        assertEquals("[1, 2, 3]\n", output);
+        Collection<Integer> obj = (Collection<Integer>) load(output);
+        // System.out.println(obj);
+        assertEquals(3, obj.size());
+        assertTrue("Create ArrayList by default: " + obj.getClass().toString(),
+                obj instanceof ArrayList);
+    }
+
+    public void testArray() {
+        Integer[] array = new Integer[3];
+        array[0] = new Integer(1);
+        array[1] = new Integer(1);
+        array[2] = new Integer(2);
+        String output = dump(array);
+        assertEquals("[1, 1, 2]\n", output);
+    }
+
+    public void testStringArray() {
+        String[] array = new String[] { "aaa", "bbb", "ccc" };
+        String output = dump(array);
+        assertEquals("[aaa, bbb, ccc]\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/SetTagTest.java b/src/test/java/org/yaml/snakeyaml/types/SetTagTest.java
new file mode 100644
index 0000000..add9b59
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/SetTagTest.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/set.html"></a>
+ */
+public class SetTagTest extends AbstractTest {
+
+    @SuppressWarnings("unchecked")
+    public void testSet() {
+        YamlDocument document = new YamlDocument("types/set.yaml");
+        Map<String, Set<String>> map = (Map<String, Set<String>>) document.getNativeData();
+        assertEquals(2, map.size());
+        Set<String> set1 = (Set<String>) map.get("baseball players");
+        assertEquals(3, set1.size());
+        assertTrue(set1.contains("Mark McGwire"));
+        assertTrue(set1.contains("Sammy Sosa"));
+        assertTrue(set1.contains("Ken Griffey"));
+        //
+        Set<String> set2 = (Set<String>) map.get("baseball teams");
+        assertEquals(3, set2.size());
+        assertTrue(set2.contains("Boston Red Sox"));
+        assertTrue(set2.contains("Detroit Tigers"));
+        assertTrue(set2.contains("New York Yankees"));
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java b/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java
new file mode 100644
index 0000000..600bd5b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java
@@ -0,0 +1,173 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * @see <a href="http://yaml.org/type/str.html"></a>
+ */
+public class StrTagTest extends AbstractTest {
+    private String getData(String data, String key) {
+        return (String) getMapValue(data, key);
+    }
+
+    public void testString() {
+        assertEquals("abcd", getData("string: abcd", "string"));
+    }
+
+    public void testStringShorthand() {
+        assertEquals("abcd", getData("string: !!str abcd", "string"));
+    }
+
+    public void testStringTag() {
+        assertEquals("abcd", getData("string: !<tag:yaml.org,2002:str> abcd", "string"));
+    }
+
+    public void testUnicode() {
+        // escaped 8-bit unicode character (u-umlaut):
+        assertEquals("\u00fc", load("\"\\xfc\""));
+        assertEquals("\\xfc", load("\\xfc"));
+
+        // 2 escaped 8-bit unicode characters (u-umlaut following by e-grave):
+        assertEquals("\u00fc\u00e8", load("\"\\xfc\\xe8\""));
+
+        // escaped 16-bit unicode character (em dash):
+        assertEquals("\u2014", load("\"\\u2014\""));
+
+        // UTF-32 encoding is explicitly not supported
+        assertEquals("\\U2014AAAA", load("'\\U2014AAAA'"));
+
+        // (and I don't have a surrogate pair handy at the moment)
+        // raw unicode characters in the stream (em dash)
+        assertEquals("\u2014", load("\u2014"));
+    }
+
+    /**
+     * @see <a href="http://code.google.com/p/jvyamlb/issues/detail?id=6"></a>
+     */
+    @SuppressWarnings("unchecked")
+    public void testIssueId6() {
+        Map<String, String> map = (Map<String, String>) load("---\ntest: |-\n \"Test\r\r (* error here)\"");
+        assertEquals("\"Test\n\n(* error here)\"", map.get("test"));
+    }
+
+    public void testStrDump() {
+        assertEquals("abc\n", dump("abc"));
+    }
+
+    public void testUnicodeDump() {
+        assertEquals("\\u263a\n", dump("\\u263a"));
+        assertEquals("The leading zero must be preserved.", "\\u063a\n", dump("\\u063a"));
+    }
+
+    public void testStringIntOut() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("number", "1");
+        String output = dump(map);
+        assertTrue(output, output.contains("number: '1'"));
+    }
+
+    public void testStringFloatOut() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("number", "1.1");
+        String output = dump(map);
+        assertTrue(output, output.contains("number: '1.1'"));
+    }
+
+    public void testStringBoolOut() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("number", "True");
+        String output = dump(map);
+        assertTrue(output, output.contains("number: 'True'"));
+    }
+
+    public void testEmitLongString() {
+        String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+        String output = dump(str);
+        assertEquals(str + "\n", output);
+    }
+
+    public void testEmitLongStringWithCR() {
+        String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\n";
+        String output = dump(str);
+        assertEquals("|+\n  " + str, output);
+    }
+
+    public void testEmitDoubleQuoted() {
+        String str = "\"xx\"";
+        String output = dump(str);
+        assertEquals("'" + str + "'\n", output);
+    }
+
+    /**
+     * http://pyyaml.org/ticket/196
+     */
+    public void testEmitQuoted() {
+        List<String> list = new ArrayList<String>(3);
+        list.add("This is an 'example'.");
+        list.add("This is an \"example\".");
+        list.add("123");
+        String output = dump(list);
+        assertEquals("[This is an 'example'., This is an \"example\"., '123']\n", output);
+        // single quoted
+        DumperOptions options = new DumperOptions();
+        options.setDefaultScalarStyle(ScalarStyle.SINGLE_QUOTED);
+        Yaml yaml = new Yaml(options);
+        String output2 = yaml.dump(list);
+        // System.out.println(output2);
+        assertEquals("- 'This is an ''example''.'\n- 'This is an \"example\".'\n- '123'\n", output2);
+        // double quoted
+        DumperOptions options2 = new DumperOptions();
+        options2.setDefaultScalarStyle(ScalarStyle.DOUBLE_QUOTED);
+        yaml = new Yaml(options2);
+        String output3 = yaml.dump(list);
+        // System.out.println(output2);
+        assertEquals("- \"This is an 'example'.\"\n- \"This is an \\\"example\\\".\"\n- \"123\"\n",
+                output3);
+    }
+
+    public void testEmitEndOfLine() {
+        String str = "xxxxxxx\n";
+        String output = dump(str);
+        assertEquals("|\n  " + str, output);
+    }
+
+    public void testDumpUtf16() throws UnsupportedEncodingException {
+        String str = "xxx";
+        assertEquals(3, str.toString().length());
+        Yaml yaml = new Yaml();
+        Charset charset = Charset.forName("UTF-16");
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        Writer writer = new OutputStreamWriter(stream, charset);
+        yaml.dump(str, writer);
+        assertEquals(str + "\n", stream.toString("UTF-16"));
+        assertEquals("Must include BOM: " + stream.toString(), (1 + 3 + 1) * 2, stream.toString()
+                .length());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/TimestampTagTest.java b/src/test/java/org/yaml/snakeyaml/types/TimestampTagTest.java
new file mode 100644
index 0000000..5e889d1
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/TimestampTagTest.java
@@ -0,0 +1,123 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * @see <a href="http://yaml.org/type/timestamp.html"></a>
+ */
+public class TimestampTagTest extends AbstractTest {
+
+    public void testTimestamp() {
+        assertEquals("2001-12-15 at 2:59:43 (100)",
+                getText("canonical: 2001-12-15T02:59:43.1Z", "canonical"));
+        // zero miliseconds
+        assertEquals("2001-12-15 at 2:59:43 (0)",
+                getText("canonical: 2001-12-15T02:59:43.000Z", "canonical"));
+        assertEquals("2001-12-15 at 2:59:43 (100)",
+                getText("valid iso8601:    2001-12-14t21:59:43.10-05:00", "valid iso8601"));
+        // half hour time zone
+        assertEquals("2001-12-14 at 22:29:43 (100)",
+                getText("valid iso8601:    2001-12-14t21:59:43.10-0:30", "valid iso8601"));
+        // + time zone
+        assertEquals("2001-12-14 at 19:59:43 (100)",
+                getText("valid iso8601:    2001-12-14t21:59:43.10+2:00", "valid iso8601"));
+        assertEquals("2001-12-15 at 2:59:43 (100)",
+                getText("space separated:  2001-12-14 21:59:43.10 -5", "space separated"));
+        assertEquals("2001-12-15 at 2:59:43 (100)",
+                getText("no time zone (Z): 2001-12-15 2:59:43.10", "no time zone (Z)"));
+        assertEquals("2002-12-14 at 0:0:0 (0)",
+                getText("date (00:00:00Z): 2002-12-14", "date (00:00:00Z)"));
+        assertEquals("2010-5-16 at 3:6:11 (3)",
+                getText("milliseconds: 2010-05-16 03:06:11.003", "milliseconds"));
+        assertEquals("2010-5-16 at 3:6:11 (7)",
+                getText("milliseconds: 2010-05-16 03:06:11.0068", "milliseconds"));
+        assertEquals("2010-5-16 at 3:6:11 (0)",
+                getText("milliseconds: 2010-05-16 03:06:11.0000", "milliseconds"));
+        assertEquals("2010-5-16 at 3:6:11 (0)",
+                getText("milliseconds: 2010-05-16 03:06:11.0004", "milliseconds"));
+        assertEquals("2010-5-16 at 3:6:11 (25)",
+                getText("milliseconds: 2010-05-16 03:06:11.0250", "milliseconds"));
+    }
+
+    public void testTimestampShorthand() {
+        assertTrue(getMapValue("canonical: !!timestamp 2001-12-15T02:59:43.1Z", "canonical") instanceof Date);
+    }
+
+    public void testTimestampTag() {
+        assertTrue(getMapValue("canonical: !<tag:yaml.org,2002:timestamp> 2001-12-15T02:59:43.1Z",
+                "canonical") instanceof Date);
+    }
+
+    public void testTimestampOut() {
+        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Moscow"));
+        cal.clear();
+        cal.set(2008, 8, 23, 14, 35, 4);
+        Date date = cal.getTime();
+        String output = dump(date);
+        assertEquals("2008-09-23T10:35:04Z\n", output);
+    }
+
+    public void testTimestampOutMap() {
+        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Moscow"));
+        cal.clear();
+        cal.set(2008, 8, 23, 14, 35, 4);
+        Date date = cal.getTime();
+        Map<String, Date> map = new HashMap<String, Date>();
+        map.put("canonical", date);
+        String output = dump(map);
+        assertEquals("{canonical: !!timestamp '2008-09-23T10:35:04Z'}\n", output);
+    }
+
+    private String getText(String yaml, String key) {
+        Date date = (Date) getMapValue(yaml, key);
+        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+        cal.setTime(date);
+        int years = cal.get(Calendar.YEAR);
+        int months = cal.get(Calendar.MONTH) + 1; // 0..12
+        int days = cal.get(Calendar.DAY_OF_MONTH); // 1..31
+        int hour24 = cal.get(Calendar.HOUR_OF_DAY); // 0..24
+        int minutes = cal.get(Calendar.MINUTE); // 0..59
+        int seconds = cal.get(Calendar.SECOND); // 0..59
+        int millis = cal.get(Calendar.MILLISECOND);
+        String result = String.valueOf(years) + "-" + String.valueOf(months) + "-"
+                + String.valueOf(days) + " at " + String.valueOf(hour24) + ":"
+                + String.valueOf(minutes) + ":" + String.valueOf(seconds) + " ("
+                + String.valueOf(millis) + ")";
+        return result;
+    }
+
+    public void testTimestampReadWrite() {
+        Date date = (Date) getMapValue("Time: 2001-11-23 15:01:42 -5", "Time");
+        Map<String, Date> map = new HashMap<String, Date>();
+        map.put("canonical", date);
+        String output = dump(map);
+        assertEquals("{canonical: !!timestamp '2001-11-23T20:01:42Z'}\n", output);
+    }
+
+    public void testSqlDate() {
+        java.sql.Date date = new java.sql.Date(1000000000000L);
+        Map<String, java.sql.Date> map = new HashMap<String, java.sql.Date>();
+        map.put("canonical", date);
+        String output = dump(map);
+        assertEquals("{canonical: !!timestamp '2001-09-09T01:46:40Z'}\n", output);
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/types/ValueTagTest.java b/src/test/java/org/yaml/snakeyaml/types/ValueTagTest.java
new file mode 100644
index 0000000..7490b1d
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/types/ValueTagTest.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.types;
+
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.YamlDocument;
+
+/**
+ * @see <a href="http://yaml.org/type/value.html"></a>
+ */
+public class ValueTagTest extends AbstractTest {
+
+    /**
+     * The 'value' tag does not work as defined in the specification but exactly
+     * as in PyYAML
+     */
+    @SuppressWarnings("unchecked")
+    public void testValue() {
+        InputStream input = YamlDocument.class.getClassLoader().getResourceAsStream(
+                YamlDocument.ROOT + "types/value.yaml");
+        Yaml yaml = new Yaml();
+        Iterator<Object> iter = (Iterator<Object>) yaml.loadAll(input).iterator();
+        Map<String, List<String>> oldSchema = (Map<String, List<String>>) iter.next();
+        assertEquals(1, oldSchema.size());
+        List<String> list = oldSchema.get("link with");
+        assertEquals(2, list.size());
+        assertEquals("library1.dll", list.get(0));
+        assertEquals("library2.dll", list.get(1));
+        //
+        Map<String, List<Map<String, String>>> newSchema = (Map<String, List<Map<String, String>>>) iter
+                .next();
+        assertEquals(1, newSchema.size());
+        //
+        List<Map<String, String>> list2 = newSchema.get("link with");
+        assertEquals(2, list2.size());
+        Map<String, String> map1 = list2.get(0);
+        assertEquals(2, map1.size());
+        assertEquals("library1.dll", map1.get("="));
+        assertEquals(new Double(1.2), map1.get("version"));
+        assertFalse(iter.hasNext());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/util/ArrayStackTest.java b/src/test/java/org/yaml/snakeyaml/util/ArrayStackTest.java
new file mode 100644
index 0000000..dd1cebe
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/util/ArrayStackTest.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.util;
+
+import junit.framework.TestCase;
+
+public class ArrayStackTest extends TestCase {
+
+    public void testClear() {
+        ArrayStack<Integer> stack = new ArrayStack<Integer>(25);
+        assertTrue(stack.isEmpty());
+        stack.push(new Integer(1));
+        stack.push(new Integer(2));
+        assertFalse(stack.isEmpty());
+        stack.clear();
+        assertTrue(stack.isEmpty());
+    }
+}
diff --git a/src/test/java/org/yaml/snakeyaml/util/UriEncoderTest.java b/src/test/java/org/yaml/snakeyaml/util/UriEncoderTest.java
new file mode 100644
index 0000000..babf61b
--- /dev/null
+++ b/src/test/java/org/yaml/snakeyaml/util/UriEncoderTest.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.util;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+
+import junit.framework.TestCase;
+
+public class UriEncoderTest extends TestCase {
+
+    public void testEncode() {
+        assertEquals("Acad%C3%A9mico", UriEncoder.encode("Académico"));
+        assertEquals("Check http://yaml.org/spec/1.1/#escaping%20in%20URI/", "[]",
+                UriEncoder.encode("[]"));
+    }
+
+    public void testDecode() throws CharacterCodingException {
+        ByteBuffer buff = ByteBuffer.allocate(10);
+        buff.put((byte) 0x34);
+        buff.put((byte) 0x35);
+        buff.flip();
+        assertEquals("45", UriEncoder.decode(buff));
+    }
+
+    public void testFailDecode() throws CharacterCodingException {
+        ByteBuffer buff = ByteBuffer.allocate(10);
+        buff.put((byte) 0x34);
+        buff.put((byte) 0xC1);
+        buff.flip();
+        try {
+            UriEncoder.decode(buff);
+            fail("Invalid UTF-8 must not be accepted.");
+        } catch (Exception e) {
+            assertEquals("Input length = 1", e.getMessage());
+        }
+    }
+}
diff --git a/src/test/java8/org/yaml/snakeyaml/issues/issue310/OptionalTest.java b/src/test/java8/org/yaml/snakeyaml/issues/issue310/OptionalTest.java
new file mode 100644
index 0000000..59322a3
--- /dev/null
+++ b/src/test/java8/org/yaml/snakeyaml/issues/issue310/OptionalTest.java
@@ -0,0 +1,158 @@
+/**
+ * Copyright (c) 2008, http://www.snakeyaml.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.yaml.snakeyaml.issues.issue310;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.junit.Test;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Represent;
+import org.yaml.snakeyaml.representer.Representer;
+
+public class OptionalTest {
+
+    public static class Salary {
+
+        private Optional<Double> income = Optional.empty();
+
+        public Optional<Double> getIncome() {
+            return income;
+        }
+
+        public void setIncome(Double income) {
+            this.income = Optional.of(income);
+        }
+
+        public void setIncome(Optional<Double> income) {
+            this.income = income;
+        }
+
+        @Override
+        public String toString() {
+            return "Salary{" + "income=" + income + '}';
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result
+                    + ((income == null) ? 0 : income.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            Salary other = (Salary) obj;
+            if (income == null) {
+                if (other.income != null)
+                    return false;
+            } else if (!income.equals(other.income))
+                return false;
+            return true;
+        }
+    }
+
+    public static class Person {
+
+        private String name;
+
+        private Optional<Salary> salary = Optional.empty();
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public Optional<Salary> getSalary() {
+            return salary;
+        }
+
+        public void setSalary(Optional<Salary> salary) {
+            this.salary = salary;
+        }
+
+        @Override
+        public String toString() {
+            return "Person{" + "name='" + name + '\'' + ", salary=" + salary
+                    + '}';
+        }
+    }
+
+    public static class OptionalRepresenter extends Representer {
+        public OptionalRepresenter() {
+            this.representers.put(Optional.class, new RepresentOptional());
+        }
+
+        private class RepresentOptional implements Represent {
+
+            public Node representData(Object data) {
+                Optional<?> opt = (Optional<?>) data;
+                List<Object> seq = new ArrayList<>(1);
+                seq.add(opt.get());
+                return representSequence(Tag.SEQ, seq, true);
+            }
+        }
+    }
+
+    @Test
+    public void testOptionaStringLoad() {
+        final String yamlStr = "name: Neo Anderson\nsalary: [{income: [123456.78]}]\n";
+        final Yaml yamlParser = new Yaml(new OptionalRepresenter());
+        Person expectedPerson = new Person();
+        Salary s = new Salary();
+        s.setIncome(123456.78);
+        expectedPerson.setName("Neo Anderson");
+        expectedPerson.setSalary(Optional.of(s));
+
+        final Person pFromStr = yamlParser.loadAs(yamlStr, Person.class);
+
+        assertEquals(expectedPerson.getName(), pFromStr.getName());
+        assertEquals(expectedPerson.getSalary(), pFromStr.getSalary());
+    }
+
+    @Test
+    public void testOptionalDumpLoad() {
+        final Yaml yamlParser = new Yaml(new OptionalRepresenter());
+        Person expectedPerson = new Person();
+        Salary s = new Salary();
+        s.setIncome(123456.78);
+        expectedPerson.setName("Neo Anderson");
+        expectedPerson.setSalary(Optional.of(s));
+
+        String pDump = yamlParser.dump(expectedPerson);
+        // System.out.println(pDump);
+        final Person pFromDump = yamlParser.loadAs(pDump, Person.class);
+
+        assertEquals(expectedPerson.getName(), pFromDump.getName());
+        assertEquals(expectedPerson.getSalary(), pFromDump.getSalary());
+    }
+}
diff --git a/src/test/resources/compactnotation/error1.yaml b/src/test/resources/compactnotation/error1.yaml
new file mode 100644
index 0000000..1107bc9
--- /dev/null
+++ b/src/test/resources/compactnotation/error1.yaml
@@ -0,0 +1,2 @@
+Table(id12, A table)
+
diff --git a/src/test/resources/compactnotation/error2.yaml b/src/test/resources/compactnotation/error2.yaml
new file mode 100644
index 0000000..2f3fa3e
--- /dev/null
+++ b/src/test/resources/compactnotation/error2.yaml
@@ -0,0 +1,2 @@
+Table(id12)
+
diff --git a/src/test/resources/compactnotation/error3.yaml b/src/test/resources/compactnotation/error3.yaml
new file mode 100644
index 0000000..c97fa44
--- /dev/null
+++ b/src/test/resources/compactnotation/error3.yaml
@@ -0,0 +1,2 @@
+Table(id12, orders): 
+  - Row(id111, size=17, description=text)
diff --git a/src/test/resources/compactnotation/error4.yaml b/src/test/resources/compactnotation/error4.yaml
new file mode 100644
index 0000000..0cd4995
--- /dev/null
+++ b/src/test/resources/compactnotation/error4.yaml
@@ -0,0 +1,4 @@
+Table(id12, table): 
+  - Row(id111, description = text) {size: 15}
+
+
diff --git a/src/test/resources/compactnotation/error5.yaml b/src/test/resources/compactnotation/error5.yaml
new file mode 100644
index 0000000..68c081d
--- /dev/null
+++ b/src/test/resources/compactnotation/error5.yaml
@@ -0,0 +1,11 @@
+Table(id12, table): 
+  - Row(id222):
+    size: 17
+    ratio: 0.333
+    description: >
+        We do not need new lines
+        here, just replace them
+        all with spaces
+
+
+
diff --git a/src/test/resources/compactnotation/error6.yaml b/src/test/resources/compactnotation/error6.yaml
new file mode 100644
index 0000000..f17ecfb
--- /dev/null
+++ b/src/test/resources/compactnotation/error6.yaml
@@ -0,0 +1,4 @@
+Table(id12, table, foo=bar)
+
+
+
diff --git a/src/test/resources/compactnotation/error7.yaml b/src/test/resources/compactnotation/error7.yaml
new file mode 100644
index 0000000..bf0f8c0
--- /dev/null
+++ b/src/test/resources/compactnotation/error7.yaml
@@ -0,0 +1,5 @@
+Table(id12, table):
+  foo: bar
+
+
+
diff --git a/src/test/resources/compactnotation/error8.yaml b/src/test/resources/compactnotation/error8.yaml
new file mode 100644
index 0000000..382d5e4
--- /dev/null
+++ b/src/test/resources/compactnotation/error8.yaml
@@ -0,0 +1,11 @@
+Row(id12): 
+  - Row(id222):
+      size: 17
+      ratio: 0.333
+      description: >
+        We do not need new lines
+        here, just replace them
+        all with spaces
+
+
+
diff --git a/src/test/resources/compactnotation/error9.yaml b/src/test/resources/compactnotation/error9.yaml
new file mode 100644
index 0000000..d8a3ae9
--- /dev/null
+++ b/src/test/resources/compactnotation/error9.yaml
@@ -0,0 +1,11 @@
+ManyListsTable(id12): 
+  - Row(id111, description = text)
+  - Row(id222):
+      size: 17
+      ratio: 0.333
+      description: >
+        We do not need new lines
+        here, just replace them
+        all with spaces
+
+
diff --git a/src/test/resources/compactnotation/example1.yaml b/src/test/resources/compactnotation/example1.yaml
new file mode 100644
index 0000000..c6a6ae5
--- /dev/null
+++ b/src/test/resources/compactnotation/example1.yaml
@@ -0,0 +1 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(name=parent, id=123)
diff --git a/src/test/resources/compactnotation/example10.yaml b/src/test/resources/compactnotation/example10.yaml
new file mode 100644
index 0000000..03b94c2
--- /dev/null
+++ b/src/test/resources/compactnotation/example10.yaml
@@ -0,0 +1,5 @@
+something:
+ - org.yaml.snakeyaml.extensions.compactnotation.Container(t7, id=id7): { name: child7 }
+ - org.yaml.snakeyaml.extensions.compactnotation.Container(t9, id=id9): { name: child9 }
+ - org.yaml.snakeyaml.extensions.compactnotation.Container(t10, id=id10): { name: child10 }
+
diff --git a/src/test/resources/compactnotation/example11.yaml b/src/test/resources/compactnotation/example11.yaml
new file mode 100644
index 0000000..6b09d2d
--- /dev/null
+++ b/src/test/resources/compactnotation/example11.yaml
@@ -0,0 +1,4 @@
+Box(id11, Main box): 
+  top: Item(id003, price=25.0, name=parrot)
+  bottom: Item(id004, price=3.5, name=sweet)
+
diff --git a/src/test/resources/compactnotation/example12.yaml b/src/test/resources/compactnotation/example12.yaml
new file mode 100644
index 0000000..15a14f2
--- /dev/null
+++ b/src/test/resources/compactnotation/example12.yaml
@@ -0,0 +1,19 @@
+Table(id12, A table): 
+  - Row(id111, description = I think; therefore I am.): {size: 15, ratio: 0.125}
+  - Row(id222):
+      size: 17
+      ratio: 0.333
+      floatRatio: 0.333
+      description: >
+        We do not need new lines
+        here, just replace them
+        all with spaces
+  - Row(id333):
+      size: 52
+      ratio: 0.88
+      floatRatio: 0.88
+      description: |-
+        Please preserve all
+        the lines because they may be
+        important, but do not include the last one !!!
+
diff --git a/src/test/resources/compactnotation/example2.yaml b/src/test/resources/compactnotation/example2.yaml
new file mode 100644
index 0000000..6105cd0
--- /dev/null
+++ b/src/test/resources/compactnotation/example2.yaml
@@ -0,0 +1 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(title)
diff --git a/src/test/resources/compactnotation/example3.yaml b/src/test/resources/compactnotation/example3.yaml
new file mode 100644
index 0000000..e240c76
--- /dev/null
+++ b/src/test/resources/compactnotation/example3.yaml
@@ -0,0 +1 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(title3, name=parent, id=123)
\ No newline at end of file
diff --git a/src/test/resources/compactnotation/example4.yaml b/src/test/resources/compactnotation/example4.yaml
new file mode 100644
index 0000000..3c3f25f
--- /dev/null
+++ b/src/test/resources/compactnotation/example4.yaml
@@ -0,0 +1,2 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(title4, name=parent4, id=444):
+  name: child4
diff --git a/src/test/resources/compactnotation/example5.yaml b/src/test/resources/compactnotation/example5.yaml
new file mode 100644
index 0000000..21dd668
--- /dev/null
+++ b/src/test/resources/compactnotation/example5.yaml
@@ -0,0 +1,4 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(title4):
+  name: child5
+  id: ID555
+
diff --git a/src/test/resources/compactnotation/example6.yaml b/src/test/resources/compactnotation/example6.yaml
new file mode 100644
index 0000000..e29f097
--- /dev/null
+++ b/src/test/resources/compactnotation/example6.yaml
@@ -0,0 +1,2 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(title4): { name: child6, id: ID6}
+
diff --git a/src/test/resources/compactnotation/example7.yaml b/src/test/resources/compactnotation/example7.yaml
new file mode 100644
index 0000000..86dd082
--- /dev/null
+++ b/src/test/resources/compactnotation/example7.yaml
@@ -0,0 +1,2 @@
+org.yaml.snakeyaml.extensions.compactnotation.Container(The title, id=id7): { name: child7 }
+
diff --git a/src/test/resources/compactnotation/example9.yaml b/src/test/resources/compactnotation/example9.yaml
new file mode 100644
index 0000000..3dd6e6f
--- /dev/null
+++ b/src/test/resources/compactnotation/example9.yaml
@@ -0,0 +1,4 @@
+something:
+ org.yaml.snakeyaml.extensions.compactnotation.Container(t7, id=id7): { name: child7 }
+ org.yaml.snakeyaml.extensions.compactnotation.Container(t9, id=id9): { name: child9 }
+
diff --git a/src/test/resources/constructor/car-no-root-class-map.yaml b/src/test/resources/constructor/car-no-root-class-map.yaml
new file mode 100644
index 0000000..7677d1b
--- /dev/null
+++ b/src/test/resources/constructor/car-no-root-class-map.yaml
@@ -0,0 +1,15 @@
+plate: 00-FF-Q2
+wheels:
+  ? {brand: Pirelli, id: 1}
+  : 2008-01-16
+  ? {brand: Dunkel, id: 2}
+  : 2002-12-24
+  ? {brand: Pirelli, id: 3}
+  : 2008-01-16
+  ? {brand: Pirelli, id: 4}
+  : 2008-01-16
+  ? {brand: Pirelli, id: 5}
+  : 2008-01-16
+windows:
+  front: 0
+  back: 1
\ No newline at end of file
diff --git a/src/test/resources/constructor/car-no-root-class.yaml b/src/test/resources/constructor/car-no-root-class.yaml
new file mode 100644
index 0000000..9fcc390
--- /dev/null
+++ b/src/test/resources/constructor/car-no-root-class.yaml
@@ -0,0 +1,8 @@
+# No root class defined
+plate: 12-XP-F4
+wheels:
+- {id: 1}
+- {id: 2}
+- {id: 3}
+- {id: 4}
+- {id: 5}
\ No newline at end of file
diff --git a/src/test/resources/constructor/car-with-tags.yaml b/src/test/resources/constructor/car-with-tags.yaml
new file mode 100644
index 0000000..886c55b
--- /dev/null
+++ b/src/test/resources/constructor/car-with-tags.yaml
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.constructor.Car
+plate: 12-XP-F4
+wheels:
+- {id: 1}
+- {id: 2}
+- {id: 3}
+- {id: 4}
+- {id: 5}
\ No newline at end of file
diff --git a/src/test/resources/constructor/car-without-root-tag.yaml b/src/test/resources/constructor/car-without-root-tag.yaml
new file mode 100644
index 0000000..852a888
--- /dev/null
+++ b/src/test/resources/constructor/car-without-root-tag.yaml
@@ -0,0 +1,5 @@
+map: {id: 3}
+part: null
+plate: 12-XP-F4
+wheel: {id: 2}
+year: '2008'
\ No newline at end of file
diff --git a/src/test/resources/constructor/car-without-tags.yaml b/src/test/resources/constructor/car-without-tags.yaml
new file mode 100644
index 0000000..6f515c5
--- /dev/null
+++ b/src/test/resources/constructor/car-without-tags.yaml
@@ -0,0 +1,8 @@
+!car
+plate: 12-XP-F4
+wheels:
+- {id: 1}
+- {id: 2}
+- {id: 3}
+- {id: 4}
+- {id: 5}
\ No newline at end of file
diff --git a/src/test/resources/constructor/cararray-with-tags-flow-auto.yaml b/src/test/resources/constructor/cararray-with-tags-flow-auto.yaml
new file mode 100644
index 0000000..b658266
--- /dev/null
+++ b/src/test/resources/constructor/cararray-with-tags-flow-auto.yaml
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.constructor.ArrayTagsTest$CarWithArray
+plate: 12-XP-F4
+wheels:
+- {id: 1}
+- {id: 2}
+- {id: 3}
+- {id: 4}
+- {id: 5}
\ No newline at end of file
diff --git a/src/test/resources/constructor/cararray-with-tags.yaml b/src/test/resources/constructor/cararray-with-tags.yaml
new file mode 100644
index 0000000..0ee6472
--- /dev/null
+++ b/src/test/resources/constructor/cararray-with-tags.yaml
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.constructor.ArrayTagsTest$CarWithArray
+plate: 12-XP-F4
+wheels:
+- id: 1
+- id: 2
+- id: 3
+- id: 4
+- id: 5
\ No newline at end of file
diff --git a/src/test/resources/constructor/carwheel-root-map.yaml b/src/test/resources/constructor/carwheel-root-map.yaml
new file mode 100644
index 0000000..f5eb144
--- /dev/null
+++ b/src/test/resources/constructor/carwheel-root-map.yaml
@@ -0,0 +1,3 @@
+wheel: !!org.yaml.snakeyaml.constructor.Wheel {id: 2}
+map: {id: 3}
+plate: 12-XP-F4
diff --git a/src/test/resources/constructor/carwheel-without-tags.yaml b/src/test/resources/constructor/carwheel-without-tags.yaml
new file mode 100644
index 0000000..59fdc76
--- /dev/null
+++ b/src/test/resources/constructor/carwheel-without-tags.yaml
@@ -0,0 +1,6 @@
+!!org.yaml.snakeyaml.constructor.ImplicitTagsTest$CarWithWheel
+map: {id: 3}
+part: !!org.yaml.snakeyaml.constructor.Wheel {id: 4}
+plate: 12-XP-F4
+wheel: {id: 2}
+year: '2008'
diff --git a/src/test/resources/constructor/test-primitives1.yaml b/src/test/resources/constructor/test-primitives1.yaml
new file mode 100644
index 0000000..5e2c948
--- /dev/null
+++ b/src/test/resources/constructor/test-primitives1.yaml
@@ -0,0 +1,23 @@
+# TestBean1
+byteClass: 1
+bytePrimitive: -3
+shortClass: +0
+shortPrimitive: -015 # octal
+integer: 5
+intPrimitive: 17
+text: the text
+id: 13
+longClass: 11111111111
+longPrimitive: 9999999999
+booleanClass: True
+booleanPrimitive: on
+charClass: 2
+charPrimitive: '#'
+bigInteger: 1234567890123456789012345678901234567890
+floatClass: 2
+floatPrimitive: 3.1416
+doubleClass: 4.0
+doublePrimitive: +1.12e4
+date: 2008-01-09
+publicField: public
+
diff --git a/src/test/resources/examples/any-object-example.yaml b/src/test/resources/examples/any-object-example.yaml
new file mode 100644
index 0000000..b5092ee
--- /dev/null
+++ b/src/test/resources/examples/any-object-example.yaml
@@ -0,0 +1,6 @@
+none: [~, null]
+bool: [true, false, on, off]
+int: 42
+float: 3.14159
+list: [LITE, RES_ACID, SUS_DEXT]
+dict: {hp: 13, sp: 5}
diff --git a/src/test/resources/examples/list-bean-1.yaml b/src/test/resources/examples/list-bean-1.yaml
new file mode 100644
index 0000000..124a4af
--- /dev/null
+++ b/src/test/resources/examples/list-bean-1.yaml
@@ -0,0 +1,9 @@
+children:
+- aaa
+- bbb
+developers:
+- name: Fred
+  role: creator
+- name: John
+  role: committer
+name: Bean123
diff --git a/src/test/resources/examples/list-bean-2.yaml b/src/test/resources/examples/list-bean-2.yaml
new file mode 100644
index 0000000..82cd9f6
--- /dev/null
+++ b/src/test/resources/examples/list-bean-2.yaml
@@ -0,0 +1,12 @@
+children:
+- aaa
+- bbb
+developers:
+- !!examples.collections.TypeSafeListWithInterfaceTest$Developer
+  name: Fred
+  role: creator
+- !!examples.collections.TypeSafeListWithInterfaceTest$Committer
+  key: 34
+  name: John
+  role: committer
+name: Bean123
\ No newline at end of file
diff --git a/src/test/resources/examples/list-bean-3.yaml b/src/test/resources/examples/list-bean-3.yaml
new file mode 100644
index 0000000..d2ffdac
--- /dev/null
+++ b/src/test/resources/examples/list-bean-3.yaml
@@ -0,0 +1,6 @@
+developers:
+- name: Fred
+  role: creator
+- name: John
+  role: committer
+name: Bean123
diff --git a/src/test/resources/examples/list-bean-4.yaml b/src/test/resources/examples/list-bean-4.yaml
new file mode 100644
index 0000000..518e2d3
--- /dev/null
+++ b/src/test/resources/examples/list-bean-4.yaml
@@ -0,0 +1,12 @@
+children:
+- aaa
+- bbb
+developers:
+- !!examples.collections.TypeSafeListNoGerericsTest$Developer
+  name: Fred
+  role: creator
+- !!examples.collections.TypeSafeListNoGerericsTest$Developer
+  name: John
+  role: committer
+name: Bean123
+
diff --git a/src/test/resources/examples/map-bean-1.yaml b/src/test/resources/examples/map-bean-1.yaml
new file mode 100644
index 0000000..a8e9eb1
--- /dev/null
+++ b/src/test/resources/examples/map-bean-1.yaml
@@ -0,0 +1,7 @@
+name: Bean123
+properties:
+  key2: value2
+  key1: value1
+sorted:
+  '1': one
+  '2': two
\ No newline at end of file
diff --git a/src/test/resources/examples/map-bean-10.yaml b/src/test/resources/examples/map-bean-10.yaml
new file mode 100644
index 0000000..c26c79e
--- /dev/null
+++ b/src/test/resources/examples/map-bean-10.yaml
@@ -0,0 +1,12 @@
+data:
+  aaa: 1
+  bbb: 2
+  zzz: 3
+developers:
+  team1:
+    name: Fred
+    role: creator
+  team2:
+    name: John
+    role: committer
+name: Bean123
diff --git a/src/test/resources/examples/map-bean-11.yaml b/src/test/resources/examples/map-bean-11.yaml
new file mode 100644
index 0000000..24fe91d
--- /dev/null
+++ b/src/test/resources/examples/map-bean-11.yaml
@@ -0,0 +1,15 @@
+data:
+  aaa: 1
+  bbb: 2
+developers:
+  team1:
+    name: Fred
+    role: creator
+  team2:
+    name: John
+    role: committer
+  team3: !!examples.collections.TypeSafeMapTest$Developer222
+    name: Bill
+    role: head
+name: Bean123
+
diff --git a/src/test/resources/examples/map-bean-12.yaml b/src/test/resources/examples/map-bean-12.yaml
new file mode 100644
index 0000000..feb5a85
--- /dev/null
+++ b/src/test/resources/examples/map-bean-12.yaml
@@ -0,0 +1,16 @@
+data:
+  ? name: Andy
+    role: tester
+  : BLACK
+  ? name: Lisa
+    role: owner
+  : RED
+developers:
+  WHITE:
+    name: Fred
+    role: creator
+  BLACK:
+    name: John
+    role: committer
+name: Bean123
+
diff --git a/src/test/resources/examples/map-bean-13.yaml b/src/test/resources/examples/map-bean-13.yaml
new file mode 100644
index 0000000..17974ce
--- /dev/null
+++ b/src/test/resources/examples/map-bean-13.yaml
@@ -0,0 +1,25 @@
+data:
+  ? name: Andy
+    role: tester
+  : BLACK
+  ? !!examples.collections.TypeSafeMap2Test$SuperMan
+    name: Bill
+    role: cleaner
+    smart: false
+  : BLACK
+  ? name: Lisa
+    role: owner
+  : RED
+developers:
+  WHITE:
+    name: Fred
+    role: creator
+  RED: !!examples.collections.TypeSafeMap2Test$SuperMan
+    name: Jason
+    role: contributor
+    smart: true
+  BLACK:
+    name: John
+    role: committer
+name: Bean123
+
diff --git a/src/test/resources/examples/map-bean-2.yaml b/src/test/resources/examples/map-bean-2.yaml
new file mode 100644
index 0000000..742792d
--- /dev/null
+++ b/src/test/resources/examples/map-bean-2.yaml
@@ -0,0 +1,3 @@
+- {'1': one, '2': two}
+- {key2: value2, key1: value1}
+- aaa
diff --git a/src/test/resources/examples/map-recursive-1.yaml b/src/test/resources/examples/map-recursive-1.yaml
new file mode 100644
index 0000000..6352266
--- /dev/null
+++ b/src/test/resources/examples/map-recursive-1.yaml
@@ -0,0 +1,4 @@
+&id001
+'1': one
+'2': two
+'3': *id001
diff --git a/src/test/resources/examples/map-recursive-1_1.yaml b/src/test/resources/examples/map-recursive-1_1.yaml
new file mode 100644
index 0000000..8830184
--- /dev/null
+++ b/src/test/resources/examples/map-recursive-1_1.yaml
@@ -0,0 +1,4 @@
+&id001
+'2': two
+'1': one
+'3': *id001
diff --git a/src/test/resources/examples/map-recursive-2.yaml b/src/test/resources/examples/map-recursive-2.yaml
new file mode 100644
index 0000000..3b582d4
--- /dev/null
+++ b/src/test/resources/examples/map-recursive-2.yaml
@@ -0,0 +1,4 @@
+&id001
+key3: *id001
+key2: value2
+key1: value1
diff --git a/src/test/resources/examples/map-recursive-3.yaml b/src/test/resources/examples/map-recursive-3.yaml
new file mode 100644
index 0000000..0d58727
--- /dev/null
+++ b/src/test/resources/examples/map-recursive-3.yaml
@@ -0,0 +1,4 @@
+&id001 !!java.util.SortedMap
+'2': two
+'1': one
+'3': *id001
diff --git a/src/test/resources/examples/map-recursive-4.yaml b/src/test/resources/examples/map-recursive-4.yaml
new file mode 100644
index 0000000..217ab88
--- /dev/null
+++ b/src/test/resources/examples/map-recursive-4.yaml
@@ -0,0 +1,4 @@
+&id001 !!java.util.Properties
+key3: *id001
+key2: value2
+key1: value1
diff --git a/src/test/resources/examples/set-bean-1.yaml b/src/test/resources/examples/set-bean-1.yaml
new file mode 100644
index 0000000..82c174e
--- /dev/null
+++ b/src/test/resources/examples/set-bean-1.yaml
@@ -0,0 +1,12 @@
+developers: !!set
+  ? name: John
+    role: founder
+  : null
+  ? name: Karl
+    role: user
+  : null
+name: Bean123
+sorted: !!set
+  one: null
+  three: null
+  two: null
\ No newline at end of file
diff --git a/src/test/resources/examples/set-bean-2.yaml b/src/test/resources/examples/set-bean-2.yaml
new file mode 100644
index 0000000..3d219e2
--- /dev/null
+++ b/src/test/resources/examples/set-bean-2.yaml
@@ -0,0 +1,14 @@
+developers: !!set
+  ? name: Karl
+    role: user
+  : null
+  ? name: John
+    role: founder
+  : null
+  
+name: Bean123
+
+sorted: !!set
+  two: null
+  one: null
+  three: null
\ No newline at end of file
diff --git a/src/test/resources/examples/set-bean-3.yaml b/src/test/resources/examples/set-bean-3.yaml
new file mode 100644
index 0000000..9d27d23
--- /dev/null
+++ b/src/test/resources/examples/set-bean-3.yaml
@@ -0,0 +1,7 @@
+&id001 !!set
+? !!examples.collections.TypeSafeSetImplementationsTest$Box
+  id: id123
+  set: *id001
+: null
+111: null
+aaa: null
diff --git a/src/test/resources/examples/set-bean-4.yaml b/src/test/resources/examples/set-bean-4.yaml
new file mode 100644
index 0000000..1e45b8b
--- /dev/null
+++ b/src/test/resources/examples/set-bean-4.yaml
@@ -0,0 +1 @@
+!!set {bbb: null, aaa: null, zzz: null}
\ No newline at end of file
diff --git a/src/test/resources/examples/set-bean-5.yaml b/src/test/resources/examples/set-bean-5.yaml
new file mode 100644
index 0000000..9cdba4f
--- /dev/null
+++ b/src/test/resources/examples/set-bean-5.yaml
@@ -0,0 +1 @@
+!!java.util.SortedSet {zzz: null, aaa: null, bbb: null}
\ No newline at end of file
diff --git a/src/test/resources/examples/set-bean-6.yaml b/src/test/resources/examples/set-bean-6.yaml
new file mode 100644
index 0000000..2a5243c
--- /dev/null
+++ b/src/test/resources/examples/set-bean-6.yaml
@@ -0,0 +1,16 @@
+developers: !!set
+  ? !!examples.collections.TypeSafeSetImplementationsTest$SuperDeveloper
+    name: Bill
+    role: super
+  : null
+  ? name: John
+    role: founder
+  : null
+  ? name: Karl
+    role: user
+  : null
+name: Bean123
+sorted: !!set
+  one: null
+  three: null
+  two: null
\ No newline at end of file
diff --git a/src/test/resources/examples/spring.xml b/src/test/resources/examples/spring.xml
new file mode 100644
index 0000000..66a3ad6
--- /dev/null
+++ b/src/test/resources/examples/spring.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+	xsi:schemaLocation="
+http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
+
+	<!-- the most powerful way -->
+	<bean id="yamlConstructor" class="examples.CustomConstructor" scope="prototype" />
+	<bean id="yamlRepresenter" class="org.yaml.snakeyaml.representer.Representer" scope="prototype" />
+	<bean id="yamlOptions" class="org.yaml.snakeyaml.DumperOptions" scope="prototype">
+		<property name="indent" value="2" />
+	</bean>
+	<bean id="snakeYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
+		<constructor-arg ref="yamlConstructor" />
+		<constructor-arg ref="yamlRepresenter" />
+        <constructor-arg ref="yamlOptions" />
+	</bean>
+
+	<!-- for a single JavaBean -->
+    <bean id="beanConstructor" class="org.yaml.snakeyaml.constructor.Constructor" scope="prototype">
+        <constructor-arg value="org.yaml.snakeyaml.Invoice" />
+    </bean>
+    <bean id="javabeanYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
+        <constructor-arg ref="beanConstructor" />
+    </bean>
+
+	<!-- the simplest way -->
+	<bean id="standardYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype" />
+</beans>
\ No newline at end of file
diff --git a/src/test/resources/examples/unknown-tags-example.yaml b/src/test/resources/examples/unknown-tags-example.yaml
new file mode 100644
index 0000000..7177a58
--- /dev/null
+++ b/src/test/resources/examples/unknown-tags-example.yaml
@@ -0,0 +1,8 @@
+--- !Foo
+aaa: !Bar1 123
+bbb: !Bar2
+- 111
+- ddd
+ccc: !Bar3
+ x: !!float 1
+ y: 3.1416
diff --git a/src/test/resources/immutable/shape1.yaml b/src/test/resources/immutable/shape1.yaml
new file mode 100644
index 0000000..1ab581f
--- /dev/null
+++ b/src/test/resources/immutable/shape1.yaml
@@ -0,0 +1,9 @@
+!!org.yaml.snakeyaml.immutable.Shape
+color: BLACK
+point:
+- 1.17
+- 3.14
+point3d: !!org.yaml.snakeyaml.immutable.Point3d
+ - !!org.yaml.snakeyaml.immutable.Point [1.17, 3.14]
+ - 345.1
+id: 123
\ No newline at end of file
diff --git a/src/test/resources/immutable/shapeNoTags.yaml b/src/test/resources/immutable/shapeNoTags.yaml
new file mode 100644
index 0000000..1917739
--- /dev/null
+++ b/src/test/resources/immutable/shapeNoTags.yaml
@@ -0,0 +1,8 @@
+color: BLACK
+point:
+- 1.17
+- 3.14
+point3d:
+ - [1.96, 1.78]
+ - 345.1
+id: 123
\ No newline at end of file
diff --git a/src/test/resources/issues/ios_emoji_surrogate.yaml b/src/test/resources/issues/ios_emoji_surrogate.yaml
new file mode 100644
index 0000000..902ca7d
--- /dev/null
+++ b/src/test/resources/issues/ios_emoji_surrogate.yaml
@@ -0,0 +1 @@
+text: "😷😊"
\ No newline at end of file
diff --git a/src/test/resources/issues/issue100-1.yaml b/src/test/resources/issues/issue100-1.yaml
new file mode 100644
index 0000000..78080e0
--- /dev/null
+++ b/src/test/resources/issues/issue100-1.yaml
@@ -0,0 +1,20 @@
+- !!org.yaml.snakeyaml.issues.issue100.Data 
+ &id001 
+ id: id123 
+ age: 11
+- !!org.yaml.snakeyaml.issues.issue100.Data
+ <<: *id001
+ id: id456
+ age: 13 
+- !!org.yaml.snakeyaml.issues.issue100.Data
+ &id003
+ <<: *id001 
+ id: id789
+- !!org.yaml.snakeyaml.issues.issue100.DataMore
+ <<: *id001 
+ age: 30
+ complete: true
+- &id004
+ age: 100
+- !!org.yaml.snakeyaml.issues.issue100.DataMore
+ <<: [*id004, *id003]
diff --git a/src/test/resources/issues/issue100-2.yaml b/src/test/resources/issues/issue100-2.yaml
new file mode 100644
index 0000000..d6183a3
--- /dev/null
+++ b/src/test/resources/issues/issue100-2.yaml
@@ -0,0 +1,29 @@
+---
+input:
+    - &CENTER { x: 1, y: 2 }
+    - &LEFT { x: 0, y: 2 }
+    - &BIG { r: 10 }
+    - &SMALL { r: 1 }
+
+# All the following maps are equal:
+result:
+    - # Explicit keys
+      x: 1
+      y: 2
+      r: 10
+      label: center/big
+    
+    - # Merge one map
+      << : *CENTER
+      r: 10
+      label: center/big
+    
+    - !!map # Merge multiple maps
+      !!merge ignore: [ *CENTER, *BIG ]
+      label: center/big
+    
+    - # Override
+      << : [ *BIG, *LEFT, *SMALL ]
+      x: 1
+      label: center/big
+
diff --git a/src/test/resources/issues/issue100-3.yaml b/src/test/resources/issues/issue100-3.yaml
new file mode 100644
index 0000000..9799c03
--- /dev/null
+++ b/src/test/resources/issues/issue100-3.yaml
@@ -0,0 +1,4 @@
+data: &001 {age: 17, id: id002}
+id: id001
+more: {<<: *001, id: more003, complete: true}
+
diff --git a/src/test/resources/issues/issue103.yaml b/src/test/resources/issues/issue103.yaml
new file mode 100644
index 0000000..9ff412c
--- /dev/null
+++ b/src/test/resources/issues/issue103.yaml
@@ -0,0 +1,35 @@
+---
+input:
+    - &CENTER { x: 1, y: 2 }
+    - &LEFT { x: 0, y: 2 }
+    - &BIG { r: 10 }
+    - &SMALL { r: 1 }
+
+# All the following maps are equal:
+result:
+    - # Explicit keys
+      x: 1
+      y: 2
+      r: 10
+      label: center/big
+    
+    - # Merge one map
+      << : *CENTER
+      r: 10
+      label: center/big
+
+    - # Merge one map and override
+      << : *LEFT
+      y: 5
+      r: 10
+      label: center/big
+    
+    - !!map # Merge multiple maps
+      !!merge ignore: [ *CENTER, *BIG ]
+      label: center/big
+    
+    - # Override
+      << : [ *BIG, *LEFT, *SMALL ]
+      x: 1
+      label: center/big
+
diff --git a/src/test/resources/issues/issue112-1.yaml b/src/test/resources/issues/issue112-1.yaml
new file mode 100644
index 0000000..1866763
--- /dev/null
+++ b/src/test/resources/issues/issue112-1.yaml
@@ -0,0 +1,6 @@
+things:
+    - name: 1
+    - name: two
+    - name: 3
+    - name: four
+    - name: "!!!"
\ No newline at end of file
diff --git a/src/test/resources/issues/issue112-2.yaml b/src/test/resources/issues/issue112-2.yaml
new file mode 100644
index 0000000..4aef51b
--- /dev/null
+++ b/src/test/resources/issues/issue112-2.yaml
@@ -0,0 +1,6 @@
+things:
+- name: 1
+- name: two
+- name: 3
+- name: four
+- name: '!!!'
\ No newline at end of file
diff --git a/src/test/resources/issues/issue139-1.yaml b/src/test/resources/issues/issue139-1.yaml
new file mode 100644
index 0000000..098e9fc
--- /dev/null
+++ b/src/test/resources/issues/issue139-1.yaml
@@ -0,0 +1,6 @@
+common: &id_common
+   key1: 1
+   key2: 2
+
+production:
+   <<: *id_common
\ No newline at end of file
diff --git a/src/test/resources/issues/issue139-2.yaml b/src/test/resources/issues/issue139-2.yaml
new file mode 100644
index 0000000..ca2967c
--- /dev/null
+++ b/src/test/resources/issues/issue139-2.yaml
@@ -0,0 +1,6 @@
+common: &id_common
+   key: 1
+   key: 2 # this value must overwrite the previous. Is it specified ?
+
+production:
+   <<: *id_common
\ No newline at end of file
diff --git a/src/test/resources/issues/issue139-3.yaml b/src/test/resources/issues/issue139-3.yaml
new file mode 100644
index 0000000..3b5d0d9
--- /dev/null
+++ b/src/test/resources/issues/issue139-3.yaml
@@ -0,0 +1,7 @@
+common: &id_common
+   key: 1
+   key: 2
+
+production:
+   <<: *id_common
+   key: 3 # this value must stay
\ No newline at end of file
diff --git a/src/test/resources/issues/issue149-losing-directives-2.yaml b/src/test/resources/issues/issue149-losing-directives-2.yaml
new file mode 100644
index 0000000..3c61376
--- /dev/null
+++ b/src/test/resources/issues/issue149-losing-directives-2.yaml
@@ -0,0 +1,10 @@
+%YAML 1.1
+%TAG !u! tag:ualberta.ca,2012:
+--- !u!29 &1
+property1: 0
+property2: aaa
+...
+%TAG !second! tag:ualberta.ca,2012:
+--- !second!29 &2
+property1: 3
+property2: bbb
diff --git a/src/test/resources/issues/issue149-losing-directives.yaml b/src/test/resources/issues/issue149-losing-directives.yaml
new file mode 100644
index 0000000..db0885c
--- /dev/null
+++ b/src/test/resources/issues/issue149-losing-directives.yaml
@@ -0,0 +1,9 @@
+%YAML 1.1
+%TAG !u! tag:ualberta.ca,2012:
+--- !u!29 &1
+property1: 0
+property2: aaa
+
+--- !u!29 &2
+property1: 3
+property2: bbb
diff --git a/src/test/resources/issues/issue149-one-document.yaml b/src/test/resources/issues/issue149-one-document.yaml
new file mode 100644
index 0000000..cd45999
--- /dev/null
+++ b/src/test/resources/issues/issue149-one-document.yaml
@@ -0,0 +1,7 @@
+%YAML 1.1
+%TAG !u! tag:ualberta.ca,2012:
+--- !u!29 &1
+property1: 0
+property2: aaa
+
+
diff --git a/src/test/resources/issues/issue177-1.yaml b/src/test/resources/issues/issue177-1.yaml
new file mode 100644
index 0000000..e5975aa
--- /dev/null
+++ b/src/test/resources/issues/issue177-1.yaml
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.issues.issue177.Points
+points:
+  pt1:
+    x: 1
+    y: 2
+  pt2:
+    x: a
+    y: 4
diff --git a/src/test/resources/issues/issue24-1.yaml b/src/test/resources/issues/issue24-1.yaml
new file mode 100644
index 0000000..e9b4a6f
--- /dev/null
+++ b/src/test/resources/issues/issue24-1.yaml
@@ -0,0 +1,4 @@
+pop :  {
+    banana: coconut
+    peppery: salty
+    }
diff --git a/src/test/resources/issues/issue306-1.yaml b/src/test/resources/issues/issue306-1.yaml
new file mode 100644
index 0000000..eb237d6
--- /dev/null
+++ b/src/test/resources/issues/issue306-1.yaml
@@ -0,0 +1,2 @@
+value: 14
+id: 7f511847-781a-45df-9c8d-1e32e028b9b3
diff --git a/src/test/resources/issues/issue306-2.yaml b/src/test/resources/issues/issue306-2.yaml
new file mode 100644
index 0000000..6bd4916
--- /dev/null
+++ b/src/test/resources/issues/issue306-2.yaml
@@ -0,0 +1,2 @@
+id: !!java.util.UUID 'ac4877be-0c31-4458-a86e-0272efe1aaa8'
+value: 3
diff --git a/src/test/resources/issues/issue55_1.txt b/src/test/resources/issues/issue55_1.txt
new file mode 100644
index 0000000..58fbf98
--- /dev/null
+++ b/src/test/resources/issues/issue55_1.txt
@@ -0,0 +1,5 @@
+posts:
+- text: Dummy
+  title: Test
+- text: Creative
+  title: Highly
\ No newline at end of file
diff --git a/src/test/resources/issues/issue55_1_rootTag.txt b/src/test/resources/issues/issue55_1_rootTag.txt
new file mode 100644
index 0000000..f073468
--- /dev/null
+++ b/src/test/resources/issues/issue55_1_rootTag.txt
@@ -0,0 +1,4 @@
+!!org.yaml.snakeyaml.issues.issue55.Blog
+posts:
+- {text: Dummy, title: Test}
+- {text: Creative, title: Highly}
\ No newline at end of file
diff --git a/src/test/resources/issues/issue55_2.txt b/src/test/resources/issues/issue55_2.txt
new file mode 100644
index 0000000..d5e6e85
--- /dev/null
+++ b/src/test/resources/issues/issue55_2.txt
@@ -0,0 +1,5 @@
+posts:
+- 1
+- 2
+- 3
+- 4
\ No newline at end of file
diff --git a/src/test/resources/issues/issue56-1.yaml b/src/test/resources/issues/issue56-1.yaml
new file mode 100644
index 0000000..e23c153
--- /dev/null
+++ b/src/test/resources/issues/issue56-1.yaml
@@ -0,0 +1,830 @@
+--- !de.oddb.org,2007/ODDB::Drugs::Product 
+oid: 1724265
+name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+  canonical: 
+    :de: ARGATRA  MITSUBISHI
+  synonyms: []
+
+company: !de.oddb.org,2007/ODDB::Business::Company 
+  oid: 1724276
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: 
+      :de: Mitsubishi Ph Dt. GmbH
+    synonyms: []
+
+sequences: 
+- !de.oddb.org,2007/ODDB::Drugs::Sequence 
+  oid: 1724267
+  codes: []
+
+  data_origins: 
+    :substance: zdavatz@ywesee.com
+    :atc: zdavatz@ywesee.com
+    :registration: zdavatz@ywesee.com
+  fachinfo_url: http://gripsdb.dimdi.de/amispb/doc/2162085-20050615/OBFM086D979C01C570B0.rtf
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: {}
+
+    synonyms: []
+
+  patinfo_url: http://gripsdb.dimdi.de/amispb/doc/2162085-20050615/OBFM6D3DDEAF01C570D8.rtf
+  atc: !de.oddb.org,2007/ODDB::Drugs::Atc 
+    oid: 2250
+    code: B01AE03
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :en: Argatroban
+        :de: Argatroban
+      synonyms: []
+
+  compositions: 
+  - !de.oddb.org,2007/ODDB::Drugs::Composition 
+    oid: 1724261
+    active_agents: 
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 1724263
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 250
+        unit: mg
+      substance: !de.oddb.org,2007/ODDB::Drugs::Substance 
+        oid: 1724260
+        codes: []
+
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Argatroban
+          synonyms: []
+
+    parts: 
+    - &id002 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 1724270
+      multi: 
+      quantity: 
+      size: 1
+      unit: &id001 !de.oddb.org,2007/ODDB::Drugs::Unit 
+        oid: 19094
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Durchstechflasche
+          synonyms: []
+
+    - &id003 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 1727992
+      multi: 
+      quantity: 
+      size: 1
+      unit: *id001
+  packages: 
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 1724271
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "3081565"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: true
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: zuzahlungsbefreit
+      value: false
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 
+    data_origins: 
+      :price_exfactory: hwyss@ywesee.com
+      :code_festbetragsgruppe: zdavatz@ywesee.com
+      :code_zuzahlungsbefreit: zdavatz@ywesee.com
+      :code_festbetragsstufe: zdavatz@ywesee.com
+      :size: hwyss@ywesee.com
+      :name: zdavatz@ywesee.com
+      :price_public: zdavatz@ywesee.com
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Argatra(R) 100 mg/ml Argatroban (N1)
+      synonyms: []
+
+    parts: 
+    - *id002
+    prices: 
+      public: 238.11
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 1727993
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: true
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: zuzahlungsbefreit
+      value: false
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "1022245"
+    data_origins: 
+      :price_exfactory: hwyss@ywesee.com
+      :code_festbetragsgruppe: zdavatz@ywesee.com
+      :code_zuzahlungsbefreit: zdavatz@ywesee.com
+      :code_festbetragsstufe: zdavatz@ywesee.com
+      :size: hwyss@ywesee.com
+      :code_cid: zdavatz@ywesee.com
+      :name: zdavatz@ywesee.com
+      :price_public: hwyss@ywesee.com
+      :code_prescription: zdavatz@ywesee.com
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Argatra(R) 100 mg/ml Argatroban (Klinikpackung)
+      synonyms: []
+
+    parts: 
+    - *id003
+    prices: 
+      exfactory: 158.0
+--- !de.oddb.org,2007/ODDB::Drugs::Product 
+oid: 1841680
+name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+  canonical: 
+    :de: ASTHMA BRONCHIALE  STAUFEN
+  synonyms: []
+
+company: !de.oddb.org,2007/ODDB::Business::Company 
+  oid: 1841686
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: 
+      :de: Staufen Pharma
+    synonyms: []
+
+sequences: 
+- !de.oddb.org,2007/ODDB::Drugs::Sequence 
+  oid: 1841675
+  codes: 
+  - !de.oddb.org,2007/ODDB::Util::Code 
+    country: EU
+    type: registration
+    value: 2510055.00.00
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: {}
+
+    synonyms: []
+
+  patinfo_url: http://gripsdb.dimdi.de/amispb/doc/2009/03/07/2510055/OBFME745B5B701C954E7.rtf
+  compositions: 
+  - !de.oddb.org,2007/ODDB::Drugs::Composition 
+    oid: 1841672
+    galenic_form: !de.oddb.org,2007/ODDB::Drugs::GalenicForm 
+      oid: 18210
+      codes: 
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: AMP
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: AMP1
+      description: !de.oddb.org,2007/ODDB::Util::Multilingual 
+        canonical: 
+          :de: Ampullen
+        synonyms: []
+
+    active_agents: 
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 1841674
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 1
+        unit: ml
+      substance: !de.oddb.org,2007/ODDB::Drugs::Substance 
+        oid: 1841670
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Asthma-bronchiale-Nosode (Pot.-Angaben)
+          synonyms: []
+
+    parts: 
+    - &id001 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 1841683
+      multi: 
+      size: 10
+      unit: !de.oddb.org,2007/ODDB::Drugs::Unit 
+        oid: 19684
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Ampullen
+          synonyms: []
+
+  packages: 
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 1841684
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "4661796"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :csv_product_infos
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Asthma Bronchiale Nosoden Ser. Ampullen
+      synonyms: []
+
+    parts: 
+    - *id001
+    prices: 
+      public: 15.3866
+--- !de.oddb.org,2007/ODDB::Drugs::Product 
+oid: 27823
+name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+  canonical: 
+    :de: Aarane
+  synonyms: []
+
+company: !de.oddb.org,2007/ODDB::Business::Company 
+  oid: 213595
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: 
+      :de: Sanofi-Aventis Dt. GmbH
+    synonyms: []
+
+sequences: 
+- !de.oddb.org,2007/ODDB::Drugs::Sequence 
+  oid: 27830
+  codes: 
+  - !de.oddb.org,2007/ODDB::Util::Code 
+    country: EU
+    type: registration
+    value: 3159.00.00
+  fachinfo_url: http://gripsdb.dimdi.de/amispb/doc/2009/02/27/2103159/OBFMDE81481A01C93A3F.rtf
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: {}
+
+    synonyms: []
+
+  patinfo_url: http://gripsdb.dimdi.de/amispb/doc/2009/02/27/2103159/OBFMDEDBE17601C93A3F.rtf
+  atc: !de.oddb.org,2007/ODDB::Drugs::Atc 
+    oid: 13239
+    code: R03AK05
+    ddd_guidelines: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: {}
+
+      synonyms: []
+
+    guidelines: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: {}
+
+      synonyms: []
+
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :en: Reproterol and Other Drugs for Obstructive Airway Diseases
+        :de: "Reproterol und andere Mittel bei obstruktiven Atemwegserkrankungen "
+      synonyms: []
+
+    ddds: []
+
+  compositions: 
+  - !de.oddb.org,2007/ODDB::Drugs::Composition 
+    oid: 27825
+    equivalence_factor: 0.25
+    galenic_form: !de.oddb.org,2007/ODDB::Drugs::GalenicForm 
+      oid: 18225
+      codes: 
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: DA
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: DA1
+      description: !de.oddb.org,2007/ODDB::Util::Multilingual 
+        canonical: 
+          :de: Dosieraerosol
+        synonyms: []
+
+    active_agents: 
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 27827
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 0.5
+        unit: mg
+      substance: !de.oddb.org,2007/ODDB::Drugs::Substance 
+        oid: 258913
+        codes: []
+
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Reproterolhydrochlorid
+          synonyms: []
+
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 27828
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 1
+        unit: mg
+      substance: !de.oddb.org,2007/ODDB::Drugs::Substance 
+        oid: 258916
+        codes: []
+
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Natriumcromoglicat (Ph.Eur.)
+          synonyms: []
+
+    parts: 
+    - &id001 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 27834
+      multi: 
+      quantity: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 10
+        unit: ml
+      size: 1
+      unit: &id002 !de.oddb.org,2007/ODDB::Drugs::Unit 
+        oid: 27833
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Dosieraerosol
+          synonyms: []
+
+  packages: 
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 27835
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "225437"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: true
+    data_origins: 
+      :price_exfactory: :dimdi
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Aarane N Dos.-Aerosol
+      synonyms: []
+
+    parts: 
+    - *id001
+    prices: 
+      festbetrag: 41.63
+      exfactory: 27.483606557377
+      public: 41.63
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 27846
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "225443"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: true
+    data_origins: 
+      :price_exfactory: :dimdi
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Aarane N Dos.-Aerosol
+      synonyms: []
+
+    parts: 
+    - !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 27845
+      multi: 
+      quantity: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 10
+        unit: ml
+      size: 2
+      unit: *id002
+    prices: 
+      festbetrag: 70.44
+      exfactory: 51.0983606557377
+      public: 70.44
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 27857
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "225466"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: true
+    data_origins: 
+      :price_exfactory: :dimdi
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Aarane N Dos.-Aerosol
+      synonyms: []
+
+    parts: 
+    - !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 27856
+      multi: 
+      quantity: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 10
+        unit: ml
+      size: 3
+      unit: *id002
+    prices: 
+      festbetrag: 98.14
+      exfactory: 73.8032786885246
+      public: 98.14
+--- !de.oddb.org,2007/ODDB::Drugs::Product 
+oid: 123058
+name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+  canonical: 
+    :de: Ass Stada
+  synonyms: []
+
+company: !de.oddb.org,2007/ODDB::Business::Company 
+  oid: 213544
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: 
+      :de: Stadapharm GmbH
+    synonyms: []
+
+sequences: 
+- !de.oddb.org,2007/ODDB::Drugs::Sequence 
+  oid: 123064
+  atc: !de.oddb.org,2007/ODDB::Drugs::Atc 
+    oid: 10794
+    code: N02BA01
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :en: Acetylsalicylic Acid
+        :de: "Acetylsalicyls\xC3\xA4ure"
+      synonyms: []
+
+    ddds: 
+    - !de.oddb.org,2007/ODDB::Drugs::Ddd 
+      oid: 17503
+      administration: O
+      comment: 
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 3
+        unit: g
+    - !de.oddb.org,2007/ODDB::Drugs::Ddd 
+      oid: 17504
+      administration: P
+      comment: bezogen auf Lysinacetylsalicylat
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 1
+        unit: g
+    - !de.oddb.org,2007/ODDB::Drugs::Ddd 
+      oid: 17505
+      administration: R
+      comment: 
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 3
+        unit: g
+  compositions: 
+  - !de.oddb.org,2007/ODDB::Drugs::Composition 
+    oid: 123060
+    equivalence_factor: 500.0
+    galenic_form: &id004 !de.oddb.org,2007/ODDB::Drugs::GalenicForm 
+      oid: 18322
+      codes: 
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: TAB1
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: TABL
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: TABL1
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: TABL2
+      - !de.oddb.org,2007/ODDB::Util::Code 
+        country: DE
+        type: galenic_form
+        value: TABL3
+      description: !de.oddb.org,2007/ODDB::Util::Multilingual 
+        canonical: 
+          :de: Tabletten
+        synonyms: 
+        - Tablette
+      group: !de.oddb.org,2007/ODDB::Drugs::GalenicGroup 
+        oid: 18348
+        administration: O
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Tabletten
+          synonyms: []
+
+    active_agents: 
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 123062
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 500
+        unit: mg
+      substance: &id005 !de.oddb.org,2007/ODDB::Drugs::Substance 
+        oid: 18991
+        codes: []
+
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: "Acetylsalicyls\xC3\xA4ure"
+          synonyms: 
+          - ASS
+        group: !de.oddb.org,2007/ODDB::Drugs::SubstanceGroup 
+          oid: 18993
+          name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+            canonical: 
+              :de: ASS / ASS-Puffer
+            synonyms: []
+
+    parts: 
+    - &id002 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 123067
+      multi: 
+      size: 10
+      unit: &id001 !de.oddb.org,2007/ODDB::Drugs::Unit 
+        oid: 18979
+        name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+          canonical: 
+            :de: Tabletten
+          synonyms: []
+
+    - &id003 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 166317
+      multi: 
+      size: 30
+      unit: *id001
+  packages: 
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 123068
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "3366167"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Ass Stada 500 Tabletten
+      synonyms: []
+
+    parts: 
+    - *id002
+    prices: 
+      festbetrag: 1.62
+      public: 1.34
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 166318
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "4860432"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Ass Stada 500 Tabletten
+      synonyms: []
+
+    parts: 
+    - *id003
+    prices: 
+      festbetrag: 3.45
+      public: 1.59
+- !de.oddb.org,2007/ODDB::Drugs::Sequence 
+  oid: 187618
+  codes: []
+
+  fachinfo_url: http://gripsdb.dimdi.de/amispb/doc/2007/10/20/2149897/OBFME354082201C79E74.rtf
+  name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+    canonical: {}
+
+    synonyms: []
+
+  patinfo_url: http://gripsdb.dimdi.de/amispb/doc/2007/10/20/2149897/OBFME3351D5E01C79E74.rtf
+  atc: !de.oddb.org,2007/ODDB::Drugs::Atc 
+    oid: 2160
+    code: B01AC06
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :en: Acetylsalicylic Acid
+        :de: "Acetylsalicyls\xC3\xA4ure"
+      synonyms: []
+
+    ddds: 
+    - !de.oddb.org,2007/ODDB::Drugs::Ddd 
+      oid: 16383
+      administration: O
+      comment: "unabh\xC3\xA4ngig von der Wirkst\xC3\xA4rke"
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 1
+        unit: ""
+  compositions: 
+  - !de.oddb.org,2007/ODDB::Drugs::Composition 
+    oid: 187614
+    equivalence_factor: 100.0
+    galenic_form: *id004
+    active_agents: 
+    - !de.oddb.org,2007/ODDB::Drugs::ActiveAgent 
+      oid: 187616
+      dose: !de.oddb.org,2007/ODDB::Drugs::Dose 
+        val: 100
+        unit: mg
+      substance: *id005
+    parts: 
+    - &id006 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 187621
+      multi: 
+      size: 50
+      unit: *id001
+    - &id007 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 187624
+      multi: 
+      size: 100
+      unit: *id001
+    - &id008 !de.oddb.org,2007/ODDB::Drugs::Part 
+      oid: 188222
+      multi: 
+      size: 20
+      unit: *id001
+  packages: 
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 187622
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "7382275"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Ass Stada 100 Tabletten
+      synonyms: []
+
+    parts: 
+    - *id006
+    prices: 
+      festbetrag: 2.64
+      public: 1.64
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 187625
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "7382281"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Ass Stada 100 Tabletten
+      synonyms: []
+
+    parts: 
+    - *id007
+    prices: 
+      festbetrag: 4.26
+      public: 2.87
+  - !de.oddb.org,2007/ODDB::Drugs::Package 
+    oid: 188223
+    codes: 
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: cid
+      value: "7394433"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsgruppe
+      value: "1"
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: festbetragsstufe
+      value: 3
+    - !de.oddb.org,2007/ODDB::Util::Code 
+      country: DE
+      type: prescription
+      value: false
+    data_origins: 
+      :price_public: :dimdi
+      :price_festbetrag: :dimdi
+    name: !de.oddb.org,2007/ODDB::Util::Multilingual 
+      canonical: 
+        :de: Ass Stada 100 Tabletten
+      synonyms: []
+
+    parts: 
+    - *id008
+    prices: 
+      festbetrag: 1.42
+      public: 1.14
diff --git a/src/test/resources/issues/issue59-1.yaml b/src/test/resources/issues/issue59-1.yaml
new file mode 100644
index 0000000..dac8f8b
--- /dev/null
+++ b/src/test/resources/issues/issue59-1.yaml
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.issues.issue60.SkipBean
+text: foo
+number: null
+map: {}
+listStr: [bar, null, foo, null]
+listInt: [null, 1, 2, 3]
+listDate: null
+empty: []
\ No newline at end of file
diff --git a/src/test/resources/issues/issue59-2.yaml b/src/test/resources/issues/issue59-2.yaml
new file mode 100644
index 0000000..801d910
--- /dev/null
+++ b/src/test/resources/issues/issue59-2.yaml
@@ -0,0 +1,7 @@
+!!org.yaml.snakeyaml.issues.issue60.SkipBean
+listStr: [bar, null, foo, null]
+listDate: null
+empty: []
+map: {}
+text: foo
+number: null
\ No newline at end of file
diff --git a/src/test/resources/issues/issue60-1.yaml b/src/test/resources/issues/issue60-1.yaml
new file mode 100644
index 0000000..0d148e8
--- /dev/null
+++ b/src/test/resources/issues/issue60-1.yaml
@@ -0,0 +1,6 @@
+!!org.yaml.snakeyaml.issues.issue60.SkipBean
+empty: []
+listInt: [null, 1, 2, 3]
+listStr: [bar, null, foo, null]
+map: {}
+text: foo
diff --git a/src/test/resources/issues/issue60-2.yaml b/src/test/resources/issues/issue60-2.yaml
new file mode 100644
index 0000000..d5100c3
--- /dev/null
+++ b/src/test/resources/issues/issue60-2.yaml
@@ -0,0 +1,4 @@
+!!org.yaml.snakeyaml.issues.issue60.SkipBean
+listInt: [null, 1, 2, 3]
+listStr: [bar, null, foo, null]
+text: foo
diff --git a/src/test/resources/issues/issue61-1.yaml b/src/test/resources/issues/issue61-1.yaml
new file mode 100644
index 0000000..80361f2
--- /dev/null
+++ b/src/test/resources/issues/issue61-1.yaml
@@ -0,0 +1,7 @@
+list:
+- !!org.yaml.snakeyaml.issues.issue61.GenericListBeanTest$Bean
+  name: foo
+  number: 0
+- !!org.yaml.snakeyaml.issues.issue61.GenericListBeanTest$Bean
+  name: bar
+  number: 3
diff --git a/src/test/resources/issues/issue61-2.yaml b/src/test/resources/issues/issue61-2.yaml
new file mode 100644
index 0000000..2aea65c
--- /dev/null
+++ b/src/test/resources/issues/issue61-2.yaml
@@ -0,0 +1,7 @@
+map:
+  foo: !!org.yaml.snakeyaml.issues.issue61.GenericMapBeanTest$Bean
+    name: foo
+    number: 0
+  bar: !!org.yaml.snakeyaml.issues.issue61.GenericMapBeanTest$Bean
+    name: bar
+    number: 3
diff --git a/src/test/resources/issues/issue67-error1.txt b/src/test/resources/issues/issue67-error1.txt
new file mode 100644
index 0000000..a171ab1
--- /dev/null
+++ b/src/test/resources/issues/issue67-error1.txt
@@ -0,0 +1,8 @@
+while scanning a tag
+ in 'string', line 1, column 1:
+    !!org.yaml.snakeyaml.issues.issu ... 
+    ^
+expected URI escape sequence of 2 hexadecimal numbers, but found 9(57) and %(37)
+ in 'string', line 1, column 71:
+     ... nAsciiCharsInClassNameTest$Acad%9%A9mico {id: 3, name: Foo bar}
+                                         ^
diff --git a/src/test/resources/issues/issue67-error2.txt b/src/test/resources/issues/issue67-error2.txt
new file mode 100644
index 0000000..8673ae0
--- /dev/null
+++ b/src/test/resources/issues/issue67-error2.txt
@@ -0,0 +1,8 @@
+while scanning a tag
+ in 'string', line 1, column 1:
+    !!org.yaml.snakeyaml.issues.issu ... 
+    ^
+expected URI in UTF-8: Input length = 1
+ in 'string', line 1, column 70:
+     ... onAsciiCharsInClassNameTest$Acad%C0mico {id: 3, name: Foo bar}
+                                         ^
diff --git a/src/test/resources/issues/issue68.txt b/src/test/resources/issues/issue68.txt
new file mode 100644
index 0000000..cd4230b
--- /dev/null
+++ b/src/test/resources/issues/issue68.txt
@@ -0,0 +1,2 @@
+И жить торопится и чувствовать спешит...
+
diff --git a/src/test/resources/issues/issue73-1.txt b/src/test/resources/issues/issue73-1.txt
new file mode 100644
index 0000000..78f262a
--- /dev/null
+++ b/src/test/resources/issues/issue73-1.txt
@@ -0,0 +1,10 @@
+!!org.yaml.snakeyaml.issues.issue73.Blog
+labels: !!set {Java: null, SnakeYAML: null, YAML: null}
+name: Test Me!
+numbers: !!set {19: null, 17: null}
+posts: !!set
+  ? {text: text 1, title: Title1}
+  : null
+  ? {text: text text 2, title: Title2}
+  : null
+
diff --git a/src/test/resources/issues/issue73-2.txt b/src/test/resources/issues/issue73-2.txt
new file mode 100644
index 0000000..ade8322
--- /dev/null
+++ b/src/test/resources/issues/issue73-2.txt
@@ -0,0 +1,7 @@
+name: No tags!
+posts:
+  - text: Dummy
+    title: Test
+  - text: Creative
+    title: Highly
+
diff --git a/src/test/resources/issues/issue73-3.txt b/src/test/resources/issues/issue73-3.txt
new file mode 100644
index 0000000..c188fe0
--- /dev/null
+++ b/src/test/resources/issues/issue73-3.txt
@@ -0,0 +1,5 @@
+!!java.util.HashSet
+- aaa
+- bbb
+- ccc
+
diff --git a/src/test/resources/issues/issue73-6.txt b/src/test/resources/issues/issue73-6.txt
new file mode 100644
index 0000000..57d49dc
--- /dev/null
+++ b/src/test/resources/issues/issue73-6.txt
@@ -0,0 +1,5 @@
+!!java.util.TreeSet
+- aaa
+- bbb
+- ccc
+
diff --git a/src/test/resources/issues/issue73-dump7.txt b/src/test/resources/issues/issue73-dump7.txt
new file mode 100644
index 0000000..0d36c28
--- /dev/null
+++ b/src/test/resources/issues/issue73-dump7.txt
@@ -0,0 +1,8 @@
+!!org.yaml.snakeyaml.issues.issue73.Blog
+labels: [Java, SnakeYAML, YAML]
+name: Test Me!
+numbers: [19, 17]
+posts:
+- {text: text 1, title: Title1}
+- {text: text text 2, title: Title2}
+
diff --git a/src/test/resources/issues/issue73-dump8.txt b/src/test/resources/issues/issue73-dump8.txt
new file mode 100644
index 0000000..2b61b71
--- /dev/null
+++ b/src/test/resources/issues/issue73-dump8.txt
@@ -0,0 +1,15 @@
+!!org.yaml.snakeyaml.issues.issue73.Blog
+labels:
+- Java
+- SnakeYAML
+- YAML
+name: Test Me!
+numbers:
+- 19
+- 17
+posts:
+- text: text 1
+  title: Title1
+- text: text text 2
+  title: Title2
+
diff --git a/src/test/resources/issues/issue73-recursive10.txt b/src/test/resources/issues/issue73-recursive10.txt
new file mode 100644
index 0000000..772fe92
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive10.txt
@@ -0,0 +1,5 @@
+&id001 !!java.util.TreeSet
+- aaa
+- !!set { 1, *id001 }
+- ccc
+
diff --git a/src/test/resources/issues/issue73-recursive4.txt b/src/test/resources/issues/issue73-recursive4.txt
new file mode 100644
index 0000000..5141208
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive4.txt
@@ -0,0 +1,5 @@
+&id001 !!java.util.HashSet
+- aaa
+- !!set { 1, *id001 }
+- ccc
+
diff --git a/src/test/resources/issues/issue73-recursive5.txt b/src/test/resources/issues/issue73-recursive5.txt
new file mode 100644
index 0000000..ddb7c42
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive5.txt
@@ -0,0 +1,4 @@
+!!org.yaml.snakeyaml.issues.issue73.RecursiveSetTest$Bean1
+id: ID123
+set: &id001 !!set {ccc: null, *id001: null, zzz: null}
+
diff --git a/src/test/resources/issues/issue73-recursive9.txt b/src/test/resources/issues/issue73-recursive9.txt
new file mode 100644
index 0000000..35620c7
--- /dev/null
+++ b/src/test/resources/issues/issue73-recursive9.txt
@@ -0,0 +1,7 @@
+&id001 !!org.yaml.snakeyaml.issues.issue73.RecursiveSortedSetTest$Bean11
+id: ID555
+set: !!set
+  *id001: null
+  ggg: null
+  hhh: null
+
diff --git a/src/test/resources/issues/issue74-array1.txt b/src/test/resources/issues/issue74-array1.txt
new file mode 100644
index 0000000..45c05d8
--- /dev/null
+++ b/src/test/resources/issues/issue74-array1.txt
@@ -0,0 +1,20 @@
+id: ID123
+list:
+- age: 111
+  name: John
+- age: 222
+  name: Tony
+members:
+- age: 21
+  name: Foo
+- age: 23
+  name: Bar
+- age: 25
+  name: Hue Long Hair
+number: 7
+openMembers:
+- age: 1000
+  name: OpenFoo
+- age: 2000
+  name: OpenBar
+
diff --git a/src/test/resources/issues/issue99-base64_double_quoted.yaml b/src/test/resources/issues/issue99-base64_double_quoted.yaml
new file mode 100644
index 0000000..8c1fccc
--- /dev/null
+++ b/src/test/resources/issues/issue99-base64_double_quoted.yaml
@@ -0,0 +1,82 @@
+jpegPhoto: !!binary
+    "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8L\
+    CwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF\
+    BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e\
+    Hh4eHh4eHh4eHh4eHh7/wAARCAB4AFoDASIAAhEBAxEB/8QAHAAAAgMBAQEBAAAA\
+    AAAAAAAABgcEBQgAAgMB/8QAORAAAQMDAwEGAwYEBwEAAAAAAQIDBAAFEQYSITET\
+    IkFRYXEHMpEUFYGhwfAII7HRM0JSYoKywuH/xAAaAQACAwEBAAAAAAAAAAAAAAAE\
+    BQEDBgAC/8QAIhEAAgICAgIDAQEAAAAAAAAAAQIAAwQREiETMQUiQVEy/9oADAMB\
+    AAIRAxEAPwDOejbio3MgNkoBzx403LRcba9taeHzdQpGaUuhbbJBLqmyCTwFGtAa\
+    VssPR+nEauu7bUi6vNlVuhKGQhPQOrHrzgeuaUlx5CB+Q6xAToSzl6G0sbSX7kwL\
+    eopSrOdiiD44496A9Tat0fopxVr0xp9i5P7QXJUokkkjkDk4HT69KqNYXW+6mbU8\
+    7NdSEkFSArr1Ph654obVY1d3ajKyOQR1J65rnyFHqGY3x4H+zL25a8WbQVW6JDjP\
+    ye6pDaMBOTnA5JxQpMuFyW+p1bq2Gkj+YtTg7VZxnAPhk+H9jV/H0bOdHaMMrCuq\
+    ldMDpxnx9arJNlVFugQ5hQSNzfJwXMDk+mfChxcpMZeAKvQg6JNzRh37xeZQQT3X\
+    VZx7Cor2oXA2WnFrlbvmLv79vwqxv6mXZS27eypcfHZoUB3lkY6DwAHHl60Jzmih\
+    agpO1aSQpOOhzRtaBvcFtOl3qXcGTAccy1hBI7zav05HSruySnWpaXgXMAju8cUu\
+    iopOc0XaUk/a2XEqGX2xnOfmT5+4/tU20lRyEUOQzR32S5R3ogSp7flPAzU8qJOQ\
+    gkH/AGml7pBa2UDKio5yAPD3o1DM5Q3BSMHn5agN1KCmjPvZbBD7dDLSSgpO4YTk\
+    evtV/qeDKuMtx95bi0tNgNZHAx0FRPhszMvsh+DGBb+0OIaLikghIGSceXFNu82O\
+    ElhiDDbCGY7ewqV1V6nHiTk/jSkbVN/2NMZCXiSa0+slwJTkqGMefBP61e6f0ehJ\
+    S9JRvIGcEdPWmPBs8VkjDaVFI4PlUlcZs90J6jp4Cg2tYjUd1167MEJFvbDHYttb\
+    ePLGfegjUejWJOOFpWAccc+PNN+ZGQ03whO7zBoWvZUkJSAULzjjy8qo2yncLBB6\
+    Ez9PtAgF+M0hxK14ClJQTsTjqD5nz/pQPfrQzDkFKHVBBBJC8bv3mnjqWKFdqAUg\
+    gZ56e9JrV6GG5C1POpMjdykf1JH9Kd4drNqK7l+x3A+Q1jywK/LZNXBmIebJ7p5G\
+    cbh4ivcxRWsgDBPgKgrSUmnSDY0YiydK+1EdXw5kxJRD8lYCAchGfA9KbKLlaQgA\
+    IGAOM1nbQLjaoY3Faih3apKVYwPP14zTJaS4W0kB7GBj+ef7UEx8balDKD3HL/D3\
+    GS3bn5rqBvUVBvHJwTg/9aYU11CXVjvKz1pZfAi8PTtBwpLiW2nj3XCgYTkFQyB+\
+    +tMGSHStKgQkEdSOT60lym4nj/JpcKromcHh2OEkJHOec1+sqZQ0FFaNxHOSMV8U\
+    Mt56Eq8Sal9kSlISkEEZJ/YoVezuHtoCRnlMvIwFNrUPLAPvmhTUTZ+0jI7oPBJ5\
+    omngJwFKaRn/AFdKF7q4ytpSkuIVuB2hKs8+H1ry7danVqd7EXOqloZD6lJWcHOS\
+    OlIvV+H7o4GknJV08Qa0VLtbd0SsrAwrPgMUr9caNQzMBbUEhwAjwz9KMwchVbRl\
+    V1QOyYo3I6kK5UCRzwc4qysdhTOQp+XJ+zM52IIb3rWrwATkZNXuorGm2x9iSlKX\
+    FY2j51gfpk5/D0o6+FFmjSbM/LeLSZCDhhChy2Bz18zjk9ab3ZfCvkIFRhpbZ9h1\
+    ATQdrl2/V0i2yUqSSxvA5AXyNpH4E05G4b/Zp/lNdB1UaiTYraHbVdOxSXWn3Iq+\
+    OQhbZUOfRTYx71cC4pAA7PpQ75At00V5uMcezgIM/wAKNznLVcrWgl1hlxD2wnhI\
+    VkEj6CtGOPI2FeRvA5yoHB/SsY/Ci4XCFeXodskiM+8nBcz/AKT+fU00b5JesUJt\
+    67a67F50ghlKdzh/4p5x6kUHn18rjr2ZocRNVgx4SLuw2ELUDtVwQmgHXPxTmRZS\
+    bTp2KX5KuFLKeB6elSPhNLF/t8pDr7jwawQ6pop3pUOD3qB73pq9RLtOfgSUpS+8\
+    U9qcbmkjB/DrQCNxPcOCI5hLAsWp75GTPv8AeVoUeUx2lAbfc14RBnWx4NtrW+0V\
+    JThQ564HNRYOioc2JHkXPUV0CGUntUInrSHT1BOensKurTp23x3g7EmSRDz3W3Hl\
+    upV5Eknj61FjLr3uQFPoCWCGUNs5ABThXeHy+2fqKDtdsibEU9HCSWu6lR88UY3t\
+    SlNIyph1lPADeUhIHljIxQhqF5pFvdhKSEoIIUQfDNVI33Ek17WKCY46/duycaDr\
+    +3B7QlIPoCDnzoq0tpftUCYiUuLNTne2FHa4Bz+BxQxGbdYuDqHR2oB7iuufbyNO\
+    TSEq3tbH5bjPauISlEdPJ3HxIHkP60zvtIUASjHXgSRPEtovpag7FhbbTXaE9Svv\
+    YPvtNQlWe4BRwhRGevNGGm2I8y+zIuMuspD7pzk5cPGfLhJou+7GPFsfSrcfHLJu\
+    IPlsjnf6mIbBMkRtQx5UUBLq1bBnnG4bc/nWiNKWHTtu7EKtjF2uyk5dkPntClRH\
+    PB6e1ZrYc7C4NSWwQhKwoAnJGK2LpK5xpFihutoawGxjAx4dan5Z2TiVjr45OYYG\
+    XOm2Psbco9m0krKVKCEbUjjpXiNHSt5TymEOJUTlJOOK+1snQ126S4Xkghas56k+\
+    FUDuorPa2Frn3JllalHAWsAkenNJu3PQjBV4jUuHbDbhJEhERtSTzjAOPbFXEWJH\
+    jRApDaW0qHQcflS+td3cvSpMqFIcZZZIDLqU8OnxGPEeteXdVSGEGLcApp0HCVf5\
+    T/8Aa4gjsz0F5dblreWWC8p6LhtfRSAeF/h4H1oG1SwsnKkqS2fLwHlxX0vV5kMP\
+    NywT2e7vc8YzUi6b5EMrUFbSnckmq1GjuS68RqDDVigqIkckgYwOAfKpdx1rZ9Os\
+    ogMW9Kp5QNywkZBPr7VGbkFpC21KIGeKhXC222fIEqRHCnU4wsnr70ZXon7+oON+\
+    hCX+G569T9aatulxKthaa3Ag7clSijH/ABBptOXRsOKG4Dk0F/CR8N/ekZlKd7yE\
+    qzgA93j9T9RUx+NOLyzv6qP9adh+agrMdmqRcwaY5akKQShaMjyNOr4c60UqwtxH\
+    FbHW8NoOeFHGBSgblw2U4lRO0X5hWKsLFfGhPaioSGIy1gHnkHPzZ9KtzMfz1616\
+    jvCyVx7NM+9x4O3G5R0rcgK3E/4iF8gnzFVGl9PKuN/cn3mEHVuHckEFSUfXgVeJ\
+    t70a3mZGc+1d0KKFHOR5g1Y2y7T5K0x0tKjjgKW2wSduPA4xWfqYpvUeOC43CR2X\
+    CtUMJPZspSngEgYHtQcdQ2y8Xb7qQESFLJHAyPrRZD0TAl4k3NbriykqDb6uSkjg\
+    kDgH0zVvZ9P2S12tcaDFaQgnKlBI3LI8SfHwrn0R9pUNJ6gTfbPHYtLUVre4vn5j\
+    nAFTdUkwrQ2RwktJPPtX5qCUlpDzqe8oJLaeeCScAY86HfiXdFJhtRSvKkoSnr5A\
+    D9KHC7InssSIMuzAtXBGSrJBqwilSmQfDJJHp0H79aHGipw4HPPvRRAYUpKW0pyT\
+    gY9AKIPQkIO9wm+GclMbVcZJJBfy0fYjkfUCm+YbBJPZdaWvwmsTk3UL9zcwI9vQ\
+    VZI6uLylA/7H8KcyILxQk7QOOmOlOcNSU7mR+WI8/Uwx8ZdLMWK+BUNKxHfTuAPO\
+    00AllbRCgPDNPH4l3W1XwIhY3Ps7hhIyc4pbW62pVe2oziQ4gOALB6bfGr8TKPi0\
+    35GOR8d5bOQ9xn/CDUwutl+7pCsvso7I5PJHgf0o/gz3o7PYNR0qUk4wpWAPwpMa\
+    E09dbbeZM1lvsmQBsUrorkHH0NO2KuPJQysNDK0JJOKSZ6KlpZPRjb469mXxuexL\
+    SI9c5ZKFrHHzJSan3GW5AgBIRlWD+NfkOZFhRsBaUYzuAFCWvNWsx4J747ZY2tpB\
+    5PhQgBb1L2JJlVcZ6VT2kLWClLpeXgcd3JA+uKX+rbsJ0tZbO1O7ABr1e7+hlvCD\
+    l9acY9+tCfaOvvbACTnJI8KNx8c/6Mgn8hRaFg9nydw8qYek7e8/hTbKluvENspA\
+    yVE9cevhQjoqyuyloG0kqIzWpfhpo0aehNXScwPvBxOIjBH+EnHznyUR9M1y0m2z\
+    QlGXlpiVEn3LDSGmm7DZotrISp7f9omLA4U4fDPknoKvl3iE2so2JO04znrVVrK7\
+    M6esrst9fexwCeVLI4T+vsDWepeo5bsp51c1e5a1KOFEck042EAAmR4tcxdoDT9N\
+    uQNU9u6G3ITru7vcFeTkoPqelMvUGh7XcRFuNpiR7c+SOwgowoDJ/wAxGennXV1L\
+    FYvoGae4lBzHufKJFbs6X4F1R2Uhg94Hz/UetC07Vka2y1IYUFN5yAMHbXV1e7aV\
+    PUS03utnMHuUd8104+sGOgFWOQRwaDrk5dZkv7XJUvcr5OMAetdXV1aLUPqI/S5n\
+    1uQnyWhgAuPKOM0UaJ07Ouk5mHEjOSZTygAhCdxz7V1dVlnagf2erWKIWE1v8K/h\
+    zE0lEbmXJKJV0PKW08tsn/0r8hTHQ0UhyVIVlRHU/vpXV1FV1qi9TI3XPdYS5meP\
+    jBqNy+3ZxiOrdFjlSW8cbz4q/L8vWl0GMjJb6/7q6uqsnZhVY0Op/9k="
+
+
+    
\ No newline at end of file
diff --git a/src/test/resources/issues/issue99-base64_literal.yaml b/src/test/resources/issues/issue99-base64_literal.yaml
new file mode 100644
index 0000000..d6f1c6a
--- /dev/null
+++ b/src/test/resources/issues/issue99-base64_literal.yaml
@@ -0,0 +1,82 @@
+jpegPhoto: !!binary |
+    /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8L
+    CwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF
+    BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e
+    Hh4eHh4eHh4eHh4eHh7/wAARCAB4AFoDASIAAhEBAxEB/8QAHAAAAgMBAQEBAAAA
+    AAAAAAAABgcEBQgAAgMB/8QAORAAAQMDAwEGAwYEBwEAAAAAAQIDBAAFEQYSITET
+    IkFRYXEHMpEUFYGhwfAII7HRM0JSYoKywuH/xAAaAQACAwEBAAAAAAAAAAAAAAAE
+    BQEDBgAC/8QAIhEAAgICAgIDAQEAAAAAAAAAAQIAAwQREiETMQUiQVEy/9oADAMB
+    AAIRAxEAPwDOejbio3MgNkoBzx403LRcba9taeHzdQpGaUuhbbJBLqmyCTwFGtAa
+    VssPR+nEauu7bUi6vNlVuhKGQhPQOrHrzgeuaUlx5CB+Q6xAToSzl6G0sbSX7kwL
+    eopSrOdiiD44496A9Tat0fopxVr0xp9i5P7QXJUokkkjkDk4HT69KqNYXW+6mbU8
+    7NdSEkFSArr1Ph654obVY1d3ajKyOQR1J65rnyFHqGY3x4H+zL25a8WbQVW6JDjP
+    ye6pDaMBOTnA5JxQpMuFyW+p1bq2Gkj+YtTg7VZxnAPhk+H9jV/H0bOdHaMMrCuq
+    ldMDpxnx9arJNlVFugQ5hQSNzfJwXMDk+mfChxcpMZeAKvQg6JNzRh37xeZQQT3X
+    VZx7Cor2oXA2WnFrlbvmLv79vwqxv6mXZS27eypcfHZoUB3lkY6DwAHHl60Jzmih
+    agpO1aSQpOOhzRtaBvcFtOl3qXcGTAccy1hBI7zav05HSruySnWpaXgXMAju8cUu
+    iopOc0XaUk/a2XEqGX2xnOfmT5+4/tU20lRyEUOQzR32S5R3ogSp7flPAzU8qJOQ
+    gkH/AGml7pBa2UDKio5yAPD3o1DM5Q3BSMHn5agN1KCmjPvZbBD7dDLSSgpO4YTk
+    evtV/qeDKuMtx95bi0tNgNZHAx0FRPhszMvsh+DGBb+0OIaLikghIGSceXFNu82O
+    ElhiDDbCGY7ewqV1V6nHiTk/jSkbVN/2NMZCXiSa0+slwJTkqGMefBP61e6f0ehJ
+    S9JRvIGcEdPWmPBs8VkjDaVFI4PlUlcZs90J6jp4Cg2tYjUd1167MEJFvbDHYttb
+    ePLGfegjUejWJOOFpWAccc+PNN+ZGQ03whO7zBoWvZUkJSAULzjjy8qo2yncLBB6
+    Ez9PtAgF+M0hxK14ClJQTsTjqD5nz/pQPfrQzDkFKHVBBBJC8bv3mnjqWKFdqAUg
+    gZ56e9JrV6GG5C1POpMjdykf1JH9Kd4drNqK7l+x3A+Q1jywK/LZNXBmIebJ7p5G
+    cbh4ivcxRWsgDBPgKgrSUmnSDY0YiydK+1EdXw5kxJRD8lYCAchGfA9KbKLlaQgA
+    IGAOM1nbQLjaoY3Faih3apKVYwPP14zTJaS4W0kB7GBj+ef7UEx8balDKD3HL/D3
+    GS3bn5rqBvUVBvHJwTg/9aYU11CXVjvKz1pZfAi8PTtBwpLiW2nj3XCgYTkFQyB+
+    +tMGSHStKgQkEdSOT60lym4nj/JpcKromcHh2OEkJHOec1+sqZQ0FFaNxHOSMV8U
+    Mt56Eq8Sal9kSlISkEEZJ/YoVezuHtoCRnlMvIwFNrUPLAPvmhTUTZ+0jI7oPBJ5
+    omngJwFKaRn/AFdKF7q4ytpSkuIVuB2hKs8+H1ry7danVqd7EXOqloZD6lJWcHOS
+    OlIvV+H7o4GknJV08Qa0VLtbd0SsrAwrPgMUr9caNQzMBbUEhwAjwz9KMwchVbRl
+    V1QOyYo3I6kK5UCRzwc4qysdhTOQp+XJ+zM52IIb3rWrwATkZNXuorGm2x9iSlKX
+    FY2j51gfpk5/D0o6+FFmjSbM/LeLSZCDhhChy2Bz18zjk9ab3ZfCvkIFRhpbZ9h1
+    ATQdrl2/V0i2yUqSSxvA5AXyNpH4E05G4b/Zp/lNdB1UaiTYraHbVdOxSXWn3Iq+
+    OQhbZUOfRTYx71cC4pAA7PpQ75At00V5uMcezgIM/wAKNznLVcrWgl1hlxD2wnhI
+    VkEj6CtGOPI2FeRvA5yoHB/SsY/Ci4XCFeXodskiM+8nBcz/AKT+fU00b5JesUJt
+    67a67F50ghlKdzh/4p5x6kUHn18rjr2ZocRNVgx4SLuw2ELUDtVwQmgHXPxTmRZS
+    bTp2KX5KuFLKeB6elSPhNLF/t8pDr7jwawQ6pop3pUOD3qB73pq9RLtOfgSUpS+8
+    U9qcbmkjB/DrQCNxPcOCI5hLAsWp75GTPv8AeVoUeUx2lAbfc14RBnWx4NtrW+0V
+    JThQ564HNRYOioc2JHkXPUV0CGUntUInrSHT1BOensKurTp23x3g7EmSRDz3W3Hl
+    upV5Eknj61FjLr3uQFPoCWCGUNs5ABThXeHy+2fqKDtdsibEU9HCSWu6lR88UY3t
+    SlNIyph1lPADeUhIHljIxQhqF5pFvdhKSEoIIUQfDNVI33Ek17WKCY46/duycaDr
+    +3B7QlIPoCDnzoq0tpftUCYiUuLNTne2FHa4Bz+BxQxGbdYuDqHR2oB7iuufbyNO
+    TSEq3tbH5bjPauISlEdPJ3HxIHkP60zvtIUASjHXgSRPEtovpag7FhbbTXaE9Svv
+    YPvtNQlWe4BRwhRGevNGGm2I8y+zIuMuspD7pzk5cPGfLhJou+7GPFsfSrcfHLJu
+    IPlsjnf6mIbBMkRtQx5UUBLq1bBnnG4bc/nWiNKWHTtu7EKtjF2uyk5dkPntClRH
+    PB6e1ZrYc7C4NSWwQhKwoAnJGK2LpK5xpFihutoawGxjAx4dan5Z2TiVjr45OYYG
+    XOm2Psbco9m0krKVKCEbUjjpXiNHSt5TymEOJUTlJOOK+1snQ126S4Xkghas56k+
+    FUDuorPa2Frn3JllalHAWsAkenNJu3PQjBV4jUuHbDbhJEhERtSTzjAOPbFXEWJH
+    jRApDaW0qHQcflS+td3cvSpMqFIcZZZIDLqU8OnxGPEeteXdVSGEGLcApp0HCVf5
+    T/8Aa4gjsz0F5dblreWWC8p6LhtfRSAeF/h4H1oG1SwsnKkqS2fLwHlxX0vV5kMP
+    NywT2e7vc8YzUi6b5EMrUFbSnckmq1GjuS68RqDDVigqIkckgYwOAfKpdx1rZ9Os
+    ogMW9Kp5QNywkZBPr7VGbkFpC21KIGeKhXC222fIEqRHCnU4wsnr70ZXon7+oON+
+    hCX+G569T9aatulxKthaa3Ag7clSijH/ABBptOXRsOKG4Dk0F/CR8N/ekZlKd7yE
+    qzgA93j9T9RUx+NOLyzv6qP9adh+agrMdmqRcwaY5akKQShaMjyNOr4c60UqwtxH
+    FbHW8NoOeFHGBSgblw2U4lRO0X5hWKsLFfGhPaioSGIy1gHnkHPzZ9KtzMfz1616
+    jvCyVx7NM+9x4O3G5R0rcgK3E/4iF8gnzFVGl9PKuN/cn3mEHVuHckEFSUfXgVeJ
+    t70a3mZGc+1d0KKFHOR5g1Y2y7T5K0x0tKjjgKW2wSduPA4xWfqYpvUeOC43CR2X
+    CtUMJPZspSngEgYHtQcdQ2y8Xb7qQESFLJHAyPrRZD0TAl4k3NbriykqDb6uSkjg
+    kDgH0zVvZ9P2S12tcaDFaQgnKlBI3LI8SfHwrn0R9pUNJ6gTfbPHYtLUVre4vn5j
+    nAFTdUkwrQ2RwktJPPtX5qCUlpDzqe8oJLaeeCScAY86HfiXdFJhtRSvKkoSnr5A
+    D9KHC7InssSIMuzAtXBGSrJBqwilSmQfDJJHp0H79aHGipw4HPPvRRAYUpKW0pyT
+    gY9AKIPQkIO9wm+GclMbVcZJJBfy0fYjkfUCm+YbBJPZdaWvwmsTk3UL9zcwI9vQ
+    VZI6uLylA/7H8KcyILxQk7QOOmOlOcNSU7mR+WI8/Uwx8ZdLMWK+BUNKxHfTuAPO
+    00AllbRCgPDNPH4l3W1XwIhY3Ps7hhIyc4pbW62pVe2oziQ4gOALB6bfGr8TKPi0
+    35GOR8d5bOQ9xn/CDUwutl+7pCsvso7I5PJHgf0o/gz3o7PYNR0qUk4wpWAPwpMa
+    E09dbbeZM1lvsmQBsUrorkHH0NO2KuPJQysNDK0JJOKSZ6KlpZPRjb469mXxuexL
+    SI9c5ZKFrHHzJSan3GW5AgBIRlWD+NfkOZFhRsBaUYzuAFCWvNWsx4J747ZY2tpB
+    5PhQgBb1L2JJlVcZ6VT2kLWClLpeXgcd3JA+uKX+rbsJ0tZbO1O7ABr1e7+hlvCD
+    l9acY9+tCfaOvvbACTnJI8KNx8c/6Mgn8hRaFg9nydw8qYek7e8/hTbKluvENspA
+    yVE9cevhQjoqyuyloG0kqIzWpfhpo0aehNXScwPvBxOIjBH+EnHznyUR9M1y0m2z
+    QlGXlpiVEn3LDSGmm7DZotrISp7f9omLA4U4fDPknoKvl3iE2so2JO04znrVVrK7
+    M6esrst9fexwCeVLI4T+vsDWepeo5bsp51c1e5a1KOFEck042EAAmR4tcxdoDT9N
+    uQNU9u6G3ITru7vcFeTkoPqelMvUGh7XcRFuNpiR7c+SOwgowoDJ/wAxGennXV1L
+    FYvoGae4lBzHufKJFbs6X4F1R2Uhg94Hz/UetC07Vka2y1IYUFN5yAMHbXV1e7aV
+    PUS03utnMHuUd8104+sGOgFWOQRwaDrk5dZkv7XJUvcr5OMAetdXV1aLUPqI/S5n
+    1uQnyWhgAuPKOM0UaJ07Ouk5mHEjOSZTygAhCdxz7V1dVlnagf2erWKIWE1v8K/h
+    zE0lEbmXJKJV0PKW08tsn/0r8hTHQ0UhyVIVlRHU/vpXV1FV1qi9TI3XPdYS5meP
+    jBqNy+3ZxiOrdFjlSW8cbz4q/L8vWl0GMjJb6/7q6uqsnZhVY0Op/9k=
+
+
+    
\ No newline at end of file
diff --git a/src/test/resources/issues/issue99-base64_literal_custom_tag.yaml b/src/test/resources/issues/issue99-base64_literal_custom_tag.yaml
new file mode 100644
index 0000000..35910ab
--- /dev/null
+++ b/src/test/resources/issues/issue99-base64_literal_custom_tag.yaml
@@ -0,0 +1,82 @@
+jpegPhoto: !beautiful |
+    /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8L
+    CwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUF
+    BQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e
+    Hh4eHh4eHh4eHh4eHh7/wAARCAB4AFoDASIAAhEBAxEB/8QAHAAAAgMBAQEBAAAA
+    AAAAAAAABgcEBQgAAgMB/8QAORAAAQMDAwEGAwYEBwEAAAAAAQIDBAAFEQYSITET
+    IkFRYXEHMpEUFYGhwfAII7HRM0JSYoKywuH/xAAaAQACAwEBAAAAAAAAAAAAAAAE
+    BQEDBgAC/8QAIhEAAgICAgIDAQEAAAAAAAAAAQIAAwQREiETMQUiQVEy/9oADAMB
+    AAIRAxEAPwDOejbio3MgNkoBzx403LRcba9taeHzdQpGaUuhbbJBLqmyCTwFGtAa
+    VssPR+nEauu7bUi6vNlVuhKGQhPQOrHrzgeuaUlx5CB+Q6xAToSzl6G0sbSX7kwL
+    eopSrOdiiD44496A9Tat0fopxVr0xp9i5P7QXJUokkkjkDk4HT69KqNYXW+6mbU8
+    7NdSEkFSArr1Ph654obVY1d3ajKyOQR1J65rnyFHqGY3x4H+zL25a8WbQVW6JDjP
+    ye6pDaMBOTnA5JxQpMuFyW+p1bq2Gkj+YtTg7VZxnAPhk+H9jV/H0bOdHaMMrCuq
+    ldMDpxnx9arJNlVFugQ5hQSNzfJwXMDk+mfChxcpMZeAKvQg6JNzRh37xeZQQT3X
+    VZx7Cor2oXA2WnFrlbvmLv79vwqxv6mXZS27eypcfHZoUB3lkY6DwAHHl60Jzmih
+    agpO1aSQpOOhzRtaBvcFtOl3qXcGTAccy1hBI7zav05HSruySnWpaXgXMAju8cUu
+    iopOc0XaUk/a2XEqGX2xnOfmT5+4/tU20lRyEUOQzR32S5R3ogSp7flPAzU8qJOQ
+    gkH/AGml7pBa2UDKio5yAPD3o1DM5Q3BSMHn5agN1KCmjPvZbBD7dDLSSgpO4YTk
+    evtV/qeDKuMtx95bi0tNgNZHAx0FRPhszMvsh+DGBb+0OIaLikghIGSceXFNu82O
+    ElhiDDbCGY7ewqV1V6nHiTk/jSkbVN/2NMZCXiSa0+slwJTkqGMefBP61e6f0ehJ
+    S9JRvIGcEdPWmPBs8VkjDaVFI4PlUlcZs90J6jp4Cg2tYjUd1167MEJFvbDHYttb
+    ePLGfegjUejWJOOFpWAccc+PNN+ZGQ03whO7zBoWvZUkJSAULzjjy8qo2yncLBB6
+    Ez9PtAgF+M0hxK14ClJQTsTjqD5nz/pQPfrQzDkFKHVBBBJC8bv3mnjqWKFdqAUg
+    gZ56e9JrV6GG5C1POpMjdykf1JH9Kd4drNqK7l+x3A+Q1jywK/LZNXBmIebJ7p5G
+    cbh4ivcxRWsgDBPgKgrSUmnSDY0YiydK+1EdXw5kxJRD8lYCAchGfA9KbKLlaQgA
+    IGAOM1nbQLjaoY3Faih3apKVYwPP14zTJaS4W0kB7GBj+ef7UEx8balDKD3HL/D3
+    GS3bn5rqBvUVBvHJwTg/9aYU11CXVjvKz1pZfAi8PTtBwpLiW2nj3XCgYTkFQyB+
+    +tMGSHStKgQkEdSOT60lym4nj/JpcKromcHh2OEkJHOec1+sqZQ0FFaNxHOSMV8U
+    Mt56Eq8Sal9kSlISkEEZJ/YoVezuHtoCRnlMvIwFNrUPLAPvmhTUTZ+0jI7oPBJ5
+    omngJwFKaRn/AFdKF7q4ytpSkuIVuB2hKs8+H1ry7danVqd7EXOqloZD6lJWcHOS
+    OlIvV+H7o4GknJV08Qa0VLtbd0SsrAwrPgMUr9caNQzMBbUEhwAjwz9KMwchVbRl
+    V1QOyYo3I6kK5UCRzwc4qysdhTOQp+XJ+zM52IIb3rWrwATkZNXuorGm2x9iSlKX
+    FY2j51gfpk5/D0o6+FFmjSbM/LeLSZCDhhChy2Bz18zjk9ab3ZfCvkIFRhpbZ9h1
+    ATQdrl2/V0i2yUqSSxvA5AXyNpH4E05G4b/Zp/lNdB1UaiTYraHbVdOxSXWn3Iq+
+    OQhbZUOfRTYx71cC4pAA7PpQ75At00V5uMcezgIM/wAKNznLVcrWgl1hlxD2wnhI
+    VkEj6CtGOPI2FeRvA5yoHB/SsY/Ci4XCFeXodskiM+8nBcz/AKT+fU00b5JesUJt
+    67a67F50ghlKdzh/4p5x6kUHn18rjr2ZocRNVgx4SLuw2ELUDtVwQmgHXPxTmRZS
+    bTp2KX5KuFLKeB6elSPhNLF/t8pDr7jwawQ6pop3pUOD3qB73pq9RLtOfgSUpS+8
+    U9qcbmkjB/DrQCNxPcOCI5hLAsWp75GTPv8AeVoUeUx2lAbfc14RBnWx4NtrW+0V
+    JThQ564HNRYOioc2JHkXPUV0CGUntUInrSHT1BOensKurTp23x3g7EmSRDz3W3Hl
+    upV5Eknj61FjLr3uQFPoCWCGUNs5ABThXeHy+2fqKDtdsibEU9HCSWu6lR88UY3t
+    SlNIyph1lPADeUhIHljIxQhqF5pFvdhKSEoIIUQfDNVI33Ek17WKCY46/duycaDr
+    +3B7QlIPoCDnzoq0tpftUCYiUuLNTne2FHa4Bz+BxQxGbdYuDqHR2oB7iuufbyNO
+    TSEq3tbH5bjPauISlEdPJ3HxIHkP60zvtIUASjHXgSRPEtovpag7FhbbTXaE9Svv
+    YPvtNQlWe4BRwhRGevNGGm2I8y+zIuMuspD7pzk5cPGfLhJou+7GPFsfSrcfHLJu
+    IPlsjnf6mIbBMkRtQx5UUBLq1bBnnG4bc/nWiNKWHTtu7EKtjF2uyk5dkPntClRH
+    PB6e1ZrYc7C4NSWwQhKwoAnJGK2LpK5xpFihutoawGxjAx4dan5Z2TiVjr45OYYG
+    XOm2Psbco9m0krKVKCEbUjjpXiNHSt5TymEOJUTlJOOK+1snQ126S4Xkghas56k+
+    FUDuorPa2Frn3JllalHAWsAkenNJu3PQjBV4jUuHbDbhJEhERtSTzjAOPbFXEWJH
+    jRApDaW0qHQcflS+td3cvSpMqFIcZZZIDLqU8OnxGPEeteXdVSGEGLcApp0HCVf5
+    T/8Aa4gjsz0F5dblreWWC8p6LhtfRSAeF/h4H1oG1SwsnKkqS2fLwHlxX0vV5kMP
+    NywT2e7vc8YzUi6b5EMrUFbSnckmq1GjuS68RqDDVigqIkckgYwOAfKpdx1rZ9Os
+    ogMW9Kp5QNywkZBPr7VGbkFpC21KIGeKhXC222fIEqRHCnU4wsnr70ZXon7+oON+
+    hCX+G569T9aatulxKthaa3Ag7clSijH/ABBptOXRsOKG4Dk0F/CR8N/ekZlKd7yE
+    qzgA93j9T9RUx+NOLyzv6qP9adh+agrMdmqRcwaY5akKQShaMjyNOr4c60UqwtxH
+    FbHW8NoOeFHGBSgblw2U4lRO0X5hWKsLFfGhPaioSGIy1gHnkHPzZ9KtzMfz1616
+    jvCyVx7NM+9x4O3G5R0rcgK3E/4iF8gnzFVGl9PKuN/cn3mEHVuHckEFSUfXgVeJ
+    t70a3mZGc+1d0KKFHOR5g1Y2y7T5K0x0tKjjgKW2wSduPA4xWfqYpvUeOC43CR2X
+    CtUMJPZspSngEgYHtQcdQ2y8Xb7qQESFLJHAyPrRZD0TAl4k3NbriykqDb6uSkjg
+    kDgH0zVvZ9P2S12tcaDFaQgnKlBI3LI8SfHwrn0R9pUNJ6gTfbPHYtLUVre4vn5j
+    nAFTdUkwrQ2RwktJPPtX5qCUlpDzqe8oJLaeeCScAY86HfiXdFJhtRSvKkoSnr5A
+    D9KHC7InssSIMuzAtXBGSrJBqwilSmQfDJJHp0H79aHGipw4HPPvRRAYUpKW0pyT
+    gY9AKIPQkIO9wm+GclMbVcZJJBfy0fYjkfUCm+YbBJPZdaWvwmsTk3UL9zcwI9vQ
+    VZI6uLylA/7H8KcyILxQk7QOOmOlOcNSU7mR+WI8/Uwx8ZdLMWK+BUNKxHfTuAPO
+    00AllbRCgPDNPH4l3W1XwIhY3Ps7hhIyc4pbW62pVe2oziQ4gOALB6bfGr8TKPi0
+    35GOR8d5bOQ9xn/CDUwutl+7pCsvso7I5PJHgf0o/gz3o7PYNR0qUk4wpWAPwpMa
+    E09dbbeZM1lvsmQBsUrorkHH0NO2KuPJQysNDK0JJOKSZ6KlpZPRjb469mXxuexL
+    SI9c5ZKFrHHzJSan3GW5AgBIRlWD+NfkOZFhRsBaUYzuAFCWvNWsx4J747ZY2tpB
+    5PhQgBb1L2JJlVcZ6VT2kLWClLpeXgcd3JA+uKX+rbsJ0tZbO1O7ABr1e7+hlvCD
+    l9acY9+tCfaOvvbACTnJI8KNx8c/6Mgn8hRaFg9nydw8qYek7e8/hTbKluvENspA
+    yVE9cevhQjoqyuyloG0kqIzWpfhpo0aehNXScwPvBxOIjBH+EnHznyUR9M1y0m2z
+    QlGXlpiVEn3LDSGmm7DZotrISp7f9omLA4U4fDPknoKvl3iE2so2JO04znrVVrK7
+    M6esrst9fexwCeVLI4T+vsDWepeo5bsp51c1e5a1KOFEck042EAAmR4tcxdoDT9N
+    uQNU9u6G3ITru7vcFeTkoPqelMvUGh7XcRFuNpiR7c+SOwgowoDJ/wAxGennXV1L
+    FYvoGae4lBzHufKJFbs6X4F1R2Uhg94Hz/UetC07Vka2y1IYUFN5yAMHbXV1e7aV
+    PUS03utnMHuUd8104+sGOgFWOQRwaDrk5dZkv7XJUvcr5OMAetdXV1aLUPqI/S5n
+    1uQnyWhgAuPKOM0UaJ07Ouk5mHEjOSZTygAhCdxz7V1dVlnagf2erWKIWE1v8K/h
+    zE0lEbmXJKJV0PKW08tsn/0r8hTHQ0UhyVIVlRHU/vpXV1FV1qi9TI3XPdYS5meP
+    jBqNy+3ZxiOrdFjlSW8cbz4q/L8vWl0GMjJb6/7q6uqsnZhVY0Op/9k=
+
+
+    
\ No newline at end of file
diff --git a/src/test/resources/issues/issue99.jpeg b/src/test/resources/issues/issue99.jpeg
new file mode 100644
index 0000000..3258ec5
--- /dev/null
+++ b/src/test/resources/issues/issue99.jpeg
Binary files differ
diff --git a/src/test/resources/javabeans/genericArray-1.yaml b/src/test/resources/javabeans/genericArray-1.yaml
new file mode 100644
index 0000000..bca5836
--- /dev/null
+++ b/src/test/resources/javabeans/genericArray-1.yaml
@@ -0,0 +1,7 @@
+ga:
+  home:
+  - 1
+  - 2
+  - 3
+  name: Array3
+id: ID556677
\ No newline at end of file
diff --git a/src/test/resources/javabeans/house-dump1.yaml b/src/test/resources/javabeans/house-dump1.yaml
new file mode 100644
index 0000000..783d968
--- /dev/null
+++ b/src/test/resources/javabeans/house-dump1.yaml
@@ -0,0 +1,12 @@
+frontDoor:
+  height: 5
+  id: qaz1
+  keytype: qwerty123
+number: 1
+reminders:
+  today: do nothig
+  tomorrow: go shoping
+rooms:
+- name: Hall
+- name: Kitchen
+street: Wall Street
\ No newline at end of file
diff --git a/src/test/resources/javabeans/house-dump2.yaml b/src/test/resources/javabeans/house-dump2.yaml
new file mode 100644
index 0000000..4a3850b
--- /dev/null
+++ b/src/test/resources/javabeans/house-dump2.yaml
@@ -0,0 +1,13 @@
+!!org.yaml.snakeyaml.javabeans.House
+frontDoor:
+  height: 5
+  id: qaz1
+  keytype: qwerty123
+number: 1
+reminders:
+  today: do nothig
+  tomorrow: go shoping
+rooms:
+- name: Hall
+- name: Kitchen
+street: Wall Street
\ No newline at end of file
diff --git a/src/test/resources/javabeans/house-dump3.yaml b/src/test/resources/javabeans/house-dump3.yaml
new file mode 100644
index 0000000..514d921
--- /dev/null
+++ b/src/test/resources/javabeans/house-dump3.yaml
@@ -0,0 +1,12 @@
+frontDoor:
+  height: 5
+  id: qaz1
+  keytype: qwerty123
+number: 1
+reminders:
+  today: do nothig
+  tomorrow: go shoping
+rooms:
+- name: Hall
+- name: Kitchen
+street: Wall Street
diff --git a/src/test/resources/javabeans/issue10-1.yaml b/src/test/resources/javabeans/issue10-1.yaml
new file mode 100644
index 0000000..83ddc87
--- /dev/null
+++ b/src/test/resources/javabeans/issue10-1.yaml
@@ -0,0 +1,9 @@
+!!org.yaml.snakeyaml.issues.issue10.DataSources
+dataSources:
+- &id001 {name: null}
+- !!org.yaml.snakeyaml.issues.issue10.JDBCDataSource
+  name: null
+  parent: *id001
+  password: null
+  url: null
+  username: null
\ No newline at end of file
diff --git a/src/test/resources/javabeans/issue10-2.yaml b/src/test/resources/javabeans/issue10-2.yaml
new file mode 100644
index 0000000..0a83745
--- /dev/null
+++ b/src/test/resources/javabeans/issue10-2.yaml
@@ -0,0 +1,9 @@
+dataSources:
+- &id001
+  name: null
+- !!org.yaml.snakeyaml.issues.issue10.JDBCDataSource
+  name: null
+  parent: *id001
+  password: null
+  url: null
+  username: null
\ No newline at end of file
diff --git a/src/test/resources/javabeans/issue10-3.yaml b/src/test/resources/javabeans/issue10-3.yaml
new file mode 100644
index 0000000..390a9c7
--- /dev/null
+++ b/src/test/resources/javabeans/issue10-3.yaml
@@ -0,0 +1,9 @@
+dataSources:
+- !!org.yaml.snakeyaml.issues.issue10.JDBCDataSource
+  name: null
+  parent: &id001
+    name: null
+  password: null
+  url: null
+  username: null
+- *id001
\ No newline at end of file
diff --git a/src/test/resources/javabeans/mycar-with-global-tag1.yaml b/src/test/resources/javabeans/mycar-with-global-tag1.yaml
new file mode 100644
index 0000000..724d20e
--- /dev/null
+++ b/src/test/resources/javabeans/mycar-with-global-tag1.yaml
@@ -0,0 +1,14 @@
+!!org.yaml.snakeyaml.constructor.MyCar
+plate: 00-FF-Q2
+wheels:
+  ? {brand: Pirelli1, id: 1}
+  : 2009-07-21T21:36:08.085Z
+  ? {brand: Pirelli2, id: 2}
+  : 2009-07-21T21:36:08.086Z
+  ? {brand: Pirelli3, id: 3}
+  : 2009-07-21T21:36:08.087Z
+  ? {brand: Pirelli4, id: 4}
+  : 2009-07-21T21:36:08.088Z
+  ? {brand: Pirelli5, id: 5}
+  : 2009-07-21T21:36:08.089Z
+windows: null
\ No newline at end of file
diff --git a/src/test/resources/org/yaml/snakeyaml/issues/issue318/classpath.properties b/src/test/resources/org/yaml/snakeyaml/issues/issue318/classpath.properties
new file mode 100644
index 0000000..a0ee78c
--- /dev/null
+++ b/src/test/resources/org/yaml/snakeyaml/issues/issue318/classpath.properties
@@ -0,0 +1,2 @@
+runtime_classes_dir=${project.build.outputDirectory}
+test_classes_dir=${project.build.testOutputDirectory}
diff --git a/src/test/resources/pyyaml/a-nasty-libyaml-bug.loader-error b/src/test/resources/pyyaml/a-nasty-libyaml-bug.loader-error
new file mode 100644
index 0000000..f97d49f
--- /dev/null
+++ b/src/test/resources/pyyaml/a-nasty-libyaml-bug.loader-error
@@ -0,0 +1 @@
+[ [
\ No newline at end of file
diff --git a/src/test/resources/pyyaml/aliases.events b/src/test/resources/pyyaml/aliases.events
new file mode 100644
index 0000000..9139b51
--- /dev/null
+++ b/src/test/resources/pyyaml/aliases.events
@@ -0,0 +1,8 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { anchor: 'myanchor', tag: '!mytag', value: 'data' }
+- !Alias { anchor: 'myanchor' }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/bool.data b/src/test/resources/pyyaml/bool.data
new file mode 100644
index 0000000..0988b63
--- /dev/null
+++ b/src/test/resources/pyyaml/bool.data
@@ -0,0 +1,4 @@
+- yes
+- NO
+- True
+- on
diff --git a/src/test/resources/pyyaml/colon-in-flow-context.loader-error b/src/test/resources/pyyaml/colon-in-flow-context.loader-error
new file mode 100644
index 0000000..13d5087
--- /dev/null
+++ b/src/test/resources/pyyaml/colon-in-flow-context.loader-error
@@ -0,0 +1 @@
+{ foo:bar }
diff --git a/src/test/resources/pyyaml/construct-binary.data b/src/test/resources/pyyaml/construct-binary.data
new file mode 100644
index 0000000..dcdb16f
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-binary.data
@@ -0,0 +1,12 @@
+canonical: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
+generic: !!binary |
+ R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
+ OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+ +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
+ AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
+description:
+ The binary value above is a tiny arrow encoded as a gif image.
diff --git a/src/test/resources/pyyaml/construct-bool.data b/src/test/resources/pyyaml/construct-bool.data
new file mode 100644
index 0000000..36d6519
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-bool.data
@@ -0,0 +1,9 @@
+canonical: yes
+answer: NO
+logical: True
+option: on
+
+
+but:
+    y: is a string
+    n: is a string
diff --git a/src/test/resources/pyyaml/construct-custom.data b/src/test/resources/pyyaml/construct-custom.data
new file mode 100644
index 0000000..9db0f64
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-custom.data
@@ -0,0 +1,26 @@
+---
+- !tag1
+  x: 1
+- !tag1
+  x: 1
+  'y': 2
+  z: 3
+- !tag2
+  10
+- !tag2
+  =: 10
+  'y': 20
+  z: 30
+- !tag3
+  x: 1
+- !tag3
+  x: 1
+  'y': 2
+  z: 3
+- !tag3
+  =: 1
+  'y': 2
+  z: 3
+- !foo
+  my-parameter: foo
+  my-another-parameter: [1,2,3]
diff --git a/src/test/resources/pyyaml/construct-float.data b/src/test/resources/pyyaml/construct-float.data
new file mode 100644
index 0000000..b662c62
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-float.data
@@ -0,0 +1,6 @@
+canonical: 6.8523015e+5
+exponential: 685.230_15e+03
+fixed: 685_230.15
+sexagesimal: 190:20:30.15
+negative infinity: -.inf
+not a number: .NaN
diff --git a/src/test/resources/pyyaml/construct-int.data b/src/test/resources/pyyaml/construct-int.data
new file mode 100644
index 0000000..852c314
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-int.data
@@ -0,0 +1,6 @@
+canonical: 685230
+decimal: +685_230
+octal: 02472256
+hexadecimal: 0x_0A_74_AE
+binary: 0b1010_0111_0100_1010_1110
+sexagesimal: 190:20:30
diff --git a/src/test/resources/pyyaml/construct-map.data b/src/test/resources/pyyaml/construct-map.data
new file mode 100644
index 0000000..022446d
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-map.data
@@ -0,0 +1,6 @@
+# Unordered set of key: value pairs.
+Block style: !!map
+  Clark : Evans
+  Brian : Ingerson
+  Oren  : Ben-Kiki
+Flow style: !!map { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki }
diff --git a/src/test/resources/pyyaml/construct-merge.data b/src/test/resources/pyyaml/construct-merge.data
new file mode 100644
index 0000000..3fdb2e2
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-merge.data
@@ -0,0 +1,27 @@
+---
+- &CENTER { x: 1, 'y': 2 }
+- &LEFT { x: 0, 'y': 2 }
+- &BIG { r: 10 }
+- &SMALL { r: 1 }
+
+# All the following maps are equal:
+
+- # Explicit keys
+  x: 1
+  'y': 2
+  r: 10
+  label: center/big
+
+- # Merge one map
+  << : *CENTER
+  r: 10
+  label: center/big
+
+- # Merge multiple maps
+  << : [ *CENTER, *BIG ]
+  label: center/big
+
+- # Override
+  << : [ *BIG, *LEFT, *SMALL ]
+  x: 1
+  label: center/big
diff --git a/src/test/resources/pyyaml/construct-null.data b/src/test/resources/pyyaml/construct-null.data
new file mode 100644
index 0000000..9ad0344
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-null.data
@@ -0,0 +1,18 @@
+# A document may be null.
+---
+---
+# This mapping has four keys,
+# one has a value.
+empty:
+canonical: ~
+english: null
+~: null key
+---
+# This sequence has five
+# entries, two have values.
+sparse:
+  - ~
+  - 2nd entry
+  -
+  - 4th entry
+  - Null
diff --git a/src/test/resources/pyyaml/construct-omap.data b/src/test/resources/pyyaml/construct-omap.data
new file mode 100644
index 0000000..4fa0f45
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-omap.data
@@ -0,0 +1,8 @@
+# Explicitly typed ordered map (dictionary).
+Bestiary: !!omap
+  - aardvark: African pig-like ant eater. Ugly.
+  - anteater: South-American ant eater. Two species.
+  - anaconda: South-American constrictor snake. Scaly.
+  # Etc.
+# Flow style
+Numbers: !!omap [ one: 1, two: 2, three : 3 ]
diff --git a/src/test/resources/pyyaml/construct-pairs.data b/src/test/resources/pyyaml/construct-pairs.data
new file mode 100644
index 0000000..05f55b9
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-pairs.data
@@ -0,0 +1,7 @@
+# Explicitly typed pairs.
+Block tasks: !!pairs
+  - meeting: with team.
+  - meeting: with boss.
+  - break: lunch.
+  - meeting: with client.
+Flow tasks: !!pairs [ meeting: with team, meeting: with boss ]
diff --git a/src/test/resources/pyyaml/construct-seq.data b/src/test/resources/pyyaml/construct-seq.data
new file mode 100644
index 0000000..bb92fd1
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-seq.data
@@ -0,0 +1,15 @@
+# Ordered sequence of nodes
+Block style: !!seq
+- Mercury   # Rotates - no light/dark sides.
+- Venus     # Deadliest. Aptly named.
+- Earth     # Mostly dirt.
+- Mars      # Seems empty.
+- Jupiter   # The king.
+- Saturn    # Pretty.
+- Uranus    # Where the sun hardly shines.
+- Neptune   # Boring. No rings.
+- Pluto     # You call this a planet?
+Flow style: !!seq [ Mercury, Venus, Earth, Mars,      # Rocks
+                    Jupiter, Saturn, Uranus, Neptune, # Gas
+                    Pluto ]                           # Overrated
+
diff --git a/src/test/resources/pyyaml/construct-set.data b/src/test/resources/pyyaml/construct-set.data
new file mode 100644
index 0000000..e05dc88
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-set.data
@@ -0,0 +1,7 @@
+# Explicitly typed set.
+baseball players: !!set
+  ? Mark McGwire
+  ? Sammy Sosa
+  ? Ken Griffey
+# Flow style
+baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees }
diff --git a/src/test/resources/pyyaml/construct-str-ascii.data b/src/test/resources/pyyaml/construct-str-ascii.data
new file mode 100644
index 0000000..0d93013
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-str-ascii.data
@@ -0,0 +1 @@
+--- !!str "ascii string"
diff --git a/src/test/resources/pyyaml/construct-str-utf8.data b/src/test/resources/pyyaml/construct-str-utf8.data
new file mode 100644
index 0000000..e355f18
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-str-utf8.data
@@ -0,0 +1 @@
+--- !!str "Это уникодная строка"
diff --git a/src/test/resources/pyyaml/construct-str.data b/src/test/resources/pyyaml/construct-str.data
new file mode 100644
index 0000000..606ac6b
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-str.data
@@ -0,0 +1 @@
+string: abcd
diff --git a/src/test/resources/pyyaml/construct-timestamp.data b/src/test/resources/pyyaml/construct-timestamp.data
new file mode 100644
index 0000000..c5f3840
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-timestamp.data
@@ -0,0 +1,5 @@
+canonical:        2001-12-15T02:59:43.1Z
+valid iso8601:    2001-12-14t21:59:43.10-05:00
+space separated:  2001-12-14 21:59:43.10 -5
+no time zone (Z): 2001-12-15 2:59:43.10
+date (00:00:00Z): 2002-12-14
diff --git a/src/test/resources/pyyaml/construct-value.data b/src/test/resources/pyyaml/construct-value.data
new file mode 100644
index 0000000..3eb7919
--- /dev/null
+++ b/src/test/resources/pyyaml/construct-value.data
@@ -0,0 +1,10 @@
+---     # Old schema
+link with:
+  - library1.dll
+  - library2.dll
+---     # New schema
+link with:
+  - = : library1.dll
+    version: 1.2
+  - = : library2.dll
+    version: 2.3
diff --git a/src/test/resources/pyyaml/document-separator-in-quoted-scalar.loader-error b/src/test/resources/pyyaml/document-separator-in-quoted-scalar.loader-error
new file mode 100644
index 0000000..9eeb0d6
--- /dev/null
+++ b/src/test/resources/pyyaml/document-separator-in-quoted-scalar.loader-error
@@ -0,0 +1,11 @@
+---
+"this --- is correct"
+---
+"this
+...is also
+correct"
+---
+"a quoted scalar
+cannot contain
+---
+document separators"
diff --git a/src/test/resources/pyyaml/documents.events b/src/test/resources/pyyaml/documents.events
new file mode 100644
index 0000000..775a51a
--- /dev/null
+++ b/src/test/resources/pyyaml/documents.events
@@ -0,0 +1,11 @@
+- !StreamStart
+- !DocumentStart { explicit: false }
+- !Scalar { implicit: [true,false], value: 'data' }
+- !DocumentEnd
+- !DocumentStart
+- !Scalar { implicit: [true,false] }
+- !DocumentEnd
+- !DocumentStart { version: [1,1], tags: { '!': '!foo', '!yaml!': 'tag:yaml.org,2002:', '!ugly!': '!!!!!!!' } }
+- !Scalar { implicit: [true,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/duplicate-anchors.canonical b/src/test/resources/pyyaml/duplicate-anchors.canonical
new file mode 100644
index 0000000..257ea26
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-anchors.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "foo", !!str "foo",
+  !!str "bar", !!str "bar"
+]
diff --git a/src/test/resources/pyyaml/duplicate-anchors.data b/src/test/resources/pyyaml/duplicate-anchors.data
new file mode 100644
index 0000000..8dc8685
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-anchors.data
@@ -0,0 +1,4 @@
+- &id foo
+- *id
+- &id bar
+- *id
diff --git a/src/test/resources/pyyaml/duplicate-key.former-loader-error.data b/src/test/resources/pyyaml/duplicate-key.former-loader-error.data
new file mode 100644
index 0000000..84deb8f
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-key.former-loader-error.data
@@ -0,0 +1,3 @@
+---
+foo: bar
+foo: baz
diff --git a/src/test/resources/pyyaml/duplicate-mapping-key.former-loader-error.data b/src/test/resources/pyyaml/duplicate-mapping-key.former-loader-error.data
new file mode 100644
index 0000000..7e7b4d1
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-mapping-key.former-loader-error.data
@@ -0,0 +1,6 @@
+---
+&anchor foo:
+    foo: bar
+    *anchor: duplicate key
+    baz: bat
+    *anchor: duplicate key
diff --git a/src/test/resources/pyyaml/duplicate-merge-key.former-loader-error.data b/src/test/resources/pyyaml/duplicate-merge-key.former-loader-error.data
new file mode 100644
index 0000000..cebc3a1
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-merge-key.former-loader-error.data
@@ -0,0 +1,4 @@
+---
+<<: {x: 1, y: 2}
+foo: bar
+<<: {z: 3, t: 4}
diff --git a/src/test/resources/pyyaml/duplicate-tag-directive.loader-error b/src/test/resources/pyyaml/duplicate-tag-directive.loader-error
new file mode 100644
index 0000000..50c81a0
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-tag-directive.loader-error
@@ -0,0 +1,3 @@
+%TAG    !foo!   bar
+%TAG    !foo!   baz
+--- foo
diff --git a/src/test/resources/pyyaml/duplicate-value-key.former-loader-error.data b/src/test/resources/pyyaml/duplicate-value-key.former-loader-error.data
new file mode 100644
index 0000000..b34a1d6
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-value-key.former-loader-error.data
@@ -0,0 +1,4 @@
+---
+=: 1
+foo: bar
+=: 2
diff --git a/src/test/resources/pyyaml/duplicate-yaml-directive.loader-error b/src/test/resources/pyyaml/duplicate-yaml-directive.loader-error
new file mode 100644
index 0000000..9b72390
--- /dev/null
+++ b/src/test/resources/pyyaml/duplicate-yaml-directive.loader-error
@@ -0,0 +1,3 @@
+%YAML   1.1
+%YAML   1.1
+--- foo
diff --git a/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.canonical b/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.canonical
new file mode 100644
index 0000000..473bed5
--- /dev/null
+++ b/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+--- !!map
+{
+  ? !!str "foo"
+  : !!str "bar"
+}
diff --git a/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.data b/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.data
new file mode 100644
index 0000000..b6b42ba
--- /dev/null
+++ b/src/test/resources/pyyaml/emit-block-scalar-in-simple-key-context-bug.data
@@ -0,0 +1,4 @@
+? |-
+  foo
+: |-
+  bar
diff --git a/src/test/resources/pyyaml/emitting-unacceptable-unicode-character-bug.data b/src/test/resources/pyyaml/emitting-unacceptable-unicode-character-bug.data
new file mode 100644
index 0000000..2a5df00
--- /dev/null
+++ b/src/test/resources/pyyaml/emitting-unacceptable-unicode-character-bug.data
@@ -0,0 +1 @@
+"\udd00"
diff --git a/src/test/resources/pyyaml/empty-anchor.emitter-error b/src/test/resources/pyyaml/empty-anchor.emitter-error
new file mode 100644
index 0000000..ce663b6
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-anchor.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { anchor: '', value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/empty-document-bug.canonical b/src/test/resources/pyyaml/empty-document-bug.canonical
new file mode 100644
index 0000000..28a6cf1
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-document-bug.canonical
@@ -0,0 +1 @@
+# This YAML stream contains no YAML documents.
diff --git a/src/test/resources/pyyaml/empty-document-bug.data b/src/test/resources/pyyaml/empty-document-bug.data
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-document-bug.data
diff --git a/src/test/resources/pyyaml/empty-documents.single-loader-error b/src/test/resources/pyyaml/empty-documents.single-loader-error
new file mode 100644
index 0000000..f8dba8d
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-documents.single-loader-error
@@ -0,0 +1,2 @@
+--- # first document
+--- # second document
diff --git a/src/test/resources/pyyaml/empty-tag-handle.emitter-error b/src/test/resources/pyyaml/empty-tag-handle.emitter-error
new file mode 100644
index 0000000..235c899
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-tag-handle.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/empty-tag-prefix.emitter-error b/src/test/resources/pyyaml/empty-tag-prefix.emitter-error
new file mode 100644
index 0000000..c6c0e95
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-tag-prefix.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!': '' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/empty-tag.emitter-error b/src/test/resources/pyyaml/empty-tag.emitter-error
new file mode 100644
index 0000000..b7ca593
--- /dev/null
+++ b/src/test/resources/pyyaml/empty-tag.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { tag: '', value: 'key', implicit: [false,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-document-end.emitter-error b/src/test/resources/pyyaml/expected-document-end.emitter-error
new file mode 100644
index 0000000..0cbab89
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-document-end.emitter-error
@@ -0,0 +1,6 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { value: 'data 1' }
+- !Scalar { value: 'data 2' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-document-start.emitter-error b/src/test/resources/pyyaml/expected-document-start.emitter-error
new file mode 100644
index 0000000..8ce575e
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-document-start.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !MappingStart
+- !MappingEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-mapping.loader-error b/src/test/resources/pyyaml/expected-mapping.loader-error
new file mode 100644
index 0000000..82aed98
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-mapping.loader-error
@@ -0,0 +1 @@
+--- !!map [not, a, map]
diff --git a/src/test/resources/pyyaml/expected-node-1.emitter-error b/src/test/resources/pyyaml/expected-node-1.emitter-error
new file mode 100644
index 0000000..36ceca3
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-node-1.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !DocumentStart
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-node-2.emitter-error b/src/test/resources/pyyaml/expected-node-2.emitter-error
new file mode 100644
index 0000000..891ee37
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-node-2.emitter-error
@@ -0,0 +1,7 @@
+- !StreamStart
+- !DocumentStart
+- !MappingStart
+- !Scalar { value: 'key' }
+- !MappingEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-nothing.emitter-error b/src/test/resources/pyyaml/expected-nothing.emitter-error
new file mode 100644
index 0000000..62c54d3
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-nothing.emitter-error
@@ -0,0 +1,4 @@
+- !StreamStart
+- !StreamEnd
+- !StreamStart
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/expected-scalar.loader-error b/src/test/resources/pyyaml/expected-scalar.loader-error
new file mode 100644
index 0000000..7b3171e
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-scalar.loader-error
@@ -0,0 +1 @@
+--- !!str [not a scalar]
diff --git a/src/test/resources/pyyaml/expected-sequence.loader-error b/src/test/resources/pyyaml/expected-sequence.loader-error
new file mode 100644
index 0000000..08074ea
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-sequence.loader-error
@@ -0,0 +1 @@
+--- !!seq {foo, bar, baz}
diff --git a/src/test/resources/pyyaml/expected-stream-start.emitter-error b/src/test/resources/pyyaml/expected-stream-start.emitter-error
new file mode 100644
index 0000000..480dc2e
--- /dev/null
+++ b/src/test/resources/pyyaml/expected-stream-start.emitter-error
@@ -0,0 +1,2 @@
+- !DocumentStart
+- !DocumentEnd
diff --git a/src/test/resources/pyyaml/explicit-document.single-loader-error b/src/test/resources/pyyaml/explicit-document.single-loader-error
new file mode 100644
index 0000000..46c6f8b
--- /dev/null
+++ b/src/test/resources/pyyaml/explicit-document.single-loader-error
@@ -0,0 +1,4 @@
+---
+foo: bar
+---
+foo: bar
diff --git a/src/test/resources/pyyaml/fetch-complex-value-bug.loader-error b/src/test/resources/pyyaml/fetch-complex-value-bug.loader-error
new file mode 100644
index 0000000..25fac24
--- /dev/null
+++ b/src/test/resources/pyyaml/fetch-complex-value-bug.loader-error
@@ -0,0 +1,2 @@
+? "foo"
+ : "bar"
diff --git a/src/test/resources/pyyaml/float-representer-2.3-bug.data b/src/test/resources/pyyaml/float-representer-2.3-bug.data
new file mode 100644
index 0000000..efd1716
--- /dev/null
+++ b/src/test/resources/pyyaml/float-representer-2.3-bug.data
@@ -0,0 +1,5 @@
+#0.0:   # hash(0) == hash(nan) and 0 == nan in Python 2.3
+1.0: 1
++.inf: 10
+-.inf: -10
+.nan: 100
diff --git a/src/test/resources/pyyaml/float.data b/src/test/resources/pyyaml/float.data
new file mode 100644
index 0000000..524d5db
--- /dev/null
+++ b/src/test/resources/pyyaml/float.data
@@ -0,0 +1,6 @@
+- 6.8523015e+5
+- 685.230_15e+03
+- 685_230.15
+- 190:20:30.15
+- -.inf
+- .NaN
diff --git a/src/test/resources/pyyaml/forbidden-entry.loader-error b/src/test/resources/pyyaml/forbidden-entry.loader-error
new file mode 100644
index 0000000..f2e3079
--- /dev/null
+++ b/src/test/resources/pyyaml/forbidden-entry.loader-error
@@ -0,0 +1,2 @@
+test: - foo
+      - bar
diff --git a/src/test/resources/pyyaml/forbidden-key.loader-error b/src/test/resources/pyyaml/forbidden-key.loader-error
new file mode 100644
index 0000000..da9b471
--- /dev/null
+++ b/src/test/resources/pyyaml/forbidden-key.loader-error
@@ -0,0 +1,2 @@
+test: ? foo
+      : bar
diff --git a/src/test/resources/pyyaml/forbidden-value.loader-error b/src/test/resources/pyyaml/forbidden-value.loader-error
new file mode 100644
index 0000000..efd7ce5
--- /dev/null
+++ b/src/test/resources/pyyaml/forbidden-value.loader-error
@@ -0,0 +1 @@
+test: key: value
diff --git a/src/test/resources/pyyaml/implicit-document.single-loader-error b/src/test/resources/pyyaml/implicit-document.single-loader-error
new file mode 100644
index 0000000..f8c9a5c
--- /dev/null
+++ b/src/test/resources/pyyaml/implicit-document.single-loader-error
@@ -0,0 +1,3 @@
+foo: bar
+---
+foo: bar
diff --git a/src/test/resources/pyyaml/int.data b/src/test/resources/pyyaml/int.data
new file mode 100644
index 0000000..d44d376
--- /dev/null
+++ b/src/test/resources/pyyaml/int.data
@@ -0,0 +1,6 @@
+- 685230
+- +685_230
+- 02472256
+- 0x_0A_74_AE
+- 0b1010_0111_0100_1010_1110
+- 190:20:30
diff --git a/src/test/resources/pyyaml/invalid-anchor-1.loader-error b/src/test/resources/pyyaml/invalid-anchor-1.loader-error
new file mode 100644
index 0000000..fcf7d0f
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-anchor-1.loader-error
@@ -0,0 +1 @@
+--- &?  foo # we allow only ascii and numeric characters in anchor names.
diff --git a/src/test/resources/pyyaml/invalid-anchor-2.loader-error b/src/test/resources/pyyaml/invalid-anchor-2.loader-error
new file mode 100644
index 0000000..bfc4ff0
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-anchor-2.loader-error
@@ -0,0 +1,8 @@
+---
+- [
+    &correct foo,
+    *correct,
+    *correct]   # still correct
+- *correct: still correct
+- &correct-or-not[foo, bar]
+
diff --git a/src/test/resources/pyyaml/invalid-anchor.emitter-error b/src/test/resources/pyyaml/invalid-anchor.emitter-error
new file mode 100644
index 0000000..3d2a814
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-anchor.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { anchor: '5*5=25', value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/invalid-base64-data.loader-error b/src/test/resources/pyyaml/invalid-base64-data.loader-error
new file mode 100644
index 0000000..798abba
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-base64-data.loader-error
@@ -0,0 +1,2 @@
+--- !!binary
+    binary data encoded in base64 should be here.
diff --git a/src/test/resources/pyyaml/invalid-block-scalar-indicator.loader-error b/src/test/resources/pyyaml/invalid-block-scalar-indicator.loader-error
new file mode 100644
index 0000000..16a6db1
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-block-scalar-indicator.loader-error
@@ -0,0 +1,2 @@
+--- > what is this?  # a comment
+data
diff --git a/src/test/resources/pyyaml/invalid-character.loader-error b/src/test/resources/pyyaml/invalid-character.loader-error
new file mode 100644
index 0000000..03687b0
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-character.loader-error
Binary files differ
diff --git a/src/test/resources/pyyaml/invalid-character.stream-error b/src/test/resources/pyyaml/invalid-character.stream-error
new file mode 100644
index 0000000..03687b0
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-character.stream-error
Binary files differ
diff --git a/src/test/resources/pyyaml/invalid-directive-line.loader-error b/src/test/resources/pyyaml/invalid-directive-line.loader-error
new file mode 100644
index 0000000..0892eb6
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-directive-line.loader-error
@@ -0,0 +1,2 @@
+%YAML   1.1 ?   # extra symbol
+---
diff --git a/src/test/resources/pyyaml/invalid-directive-name-1.loader-error b/src/test/resources/pyyaml/invalid-directive-name-1.loader-error
new file mode 100644
index 0000000..153fd88
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-directive-name-1.loader-error
@@ -0,0 +1,2 @@
+%   # no name at all
+---
diff --git a/src/test/resources/pyyaml/invalid-directive-name-2.loader-error b/src/test/resources/pyyaml/invalid-directive-name-2.loader-error
new file mode 100644
index 0000000..3732a06
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-directive-name-2.loader-error
@@ -0,0 +1,2 @@
+%invalid-characters:in-directive name
+---
diff --git a/src/test/resources/pyyaml/invalid-escape-character.loader-error b/src/test/resources/pyyaml/invalid-escape-character.loader-error
new file mode 100644
index 0000000..a95ab76
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-escape-character.loader-error
@@ -0,0 +1 @@
+"some escape characters are \ncorrect, but this one \?\nis not\n"
diff --git a/src/test/resources/pyyaml/invalid-escape-numbers.loader-error b/src/test/resources/pyyaml/invalid-escape-numbers.loader-error
new file mode 100644
index 0000000..614ec9f
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-escape-numbers.loader-error
@@ -0,0 +1 @@
+"hm.... \u123?"
diff --git a/src/test/resources/pyyaml/invalid-indentation-indicator-1.loader-error b/src/test/resources/pyyaml/invalid-indentation-indicator-1.loader-error
new file mode 100644
index 0000000..a3cd12f
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-indentation-indicator-1.loader-error
@@ -0,0 +1,2 @@
+--- >0  # not valid
+data
diff --git a/src/test/resources/pyyaml/invalid-indentation-indicator-2.loader-error b/src/test/resources/pyyaml/invalid-indentation-indicator-2.loader-error
new file mode 100644
index 0000000..eefb6ec
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-indentation-indicator-2.loader-error
@@ -0,0 +1,2 @@
+--- >-0
+data
diff --git a/src/test/resources/pyyaml/invalid-item-without-trailing-break.loader-error b/src/test/resources/pyyaml/invalid-item-without-trailing-break.loader-error
new file mode 100644
index 0000000..fdcf6c6
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-item-without-trailing-break.loader-error
@@ -0,0 +1,2 @@
+-
+-0
\ No newline at end of file
diff --git a/src/test/resources/pyyaml/invalid-merge-1.loader-error b/src/test/resources/pyyaml/invalid-merge-1.loader-error
new file mode 100644
index 0000000..fc3c284
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-merge-1.loader-error
@@ -0,0 +1,2 @@
+foo: bar
+<<: baz
diff --git a/src/test/resources/pyyaml/invalid-merge-2.loader-error b/src/test/resources/pyyaml/invalid-merge-2.loader-error
new file mode 100644
index 0000000..8e88615
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-merge-2.loader-error
@@ -0,0 +1,2 @@
+foo: bar
+<<: [x: 1, y: 2, z, t: 4]
diff --git a/src/test/resources/pyyaml/invalid-omap-1.loader-error b/src/test/resources/pyyaml/invalid-omap-1.loader-error
new file mode 100644
index 0000000..2863392
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-omap-1.loader-error
@@ -0,0 +1,3 @@
+--- !!omap
+foo: bar
+baz: bat
diff --git a/src/test/resources/pyyaml/invalid-omap-2.loader-error b/src/test/resources/pyyaml/invalid-omap-2.loader-error
new file mode 100644
index 0000000..c377dfb
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-omap-2.loader-error
@@ -0,0 +1,3 @@
+--- !!omap
+- foo: bar
+- baz
diff --git a/src/test/resources/pyyaml/invalid-omap-3.loader-error b/src/test/resources/pyyaml/invalid-omap-3.loader-error
new file mode 100644
index 0000000..2a4f50d
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-omap-3.loader-error
@@ -0,0 +1,4 @@
+--- !!omap
+- foo: bar
+- baz: bar
+  bar: bar
diff --git a/src/test/resources/pyyaml/invalid-pairs-1.loader-error b/src/test/resources/pyyaml/invalid-pairs-1.loader-error
new file mode 100644
index 0000000..42d19ae
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-pairs-1.loader-error
@@ -0,0 +1,3 @@
+--- !!pairs
+foo: bar
+baz: bat
diff --git a/src/test/resources/pyyaml/invalid-pairs-2.loader-error b/src/test/resources/pyyaml/invalid-pairs-2.loader-error
new file mode 100644
index 0000000..31389ea
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-pairs-2.loader-error
@@ -0,0 +1,3 @@
+--- !!pairs
+- foo: bar
+- baz
diff --git a/src/test/resources/pyyaml/invalid-pairs-3.loader-error b/src/test/resources/pyyaml/invalid-pairs-3.loader-error
new file mode 100644
index 0000000..f8d7704
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-pairs-3.loader-error
@@ -0,0 +1,4 @@
+--- !!pairs
+- foo: bar
+- baz: bar
+  bar: bar
diff --git a/src/test/resources/pyyaml/invalid-simple-key.loader-error b/src/test/resources/pyyaml/invalid-simple-key.loader-error
new file mode 100644
index 0000000..a58deec
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-simple-key.loader-error
@@ -0,0 +1,3 @@
+key: value
+invalid simple key
+next key: next value
diff --git a/src/test/resources/pyyaml/invalid-single-quote-bug.data b/src/test/resources/pyyaml/invalid-single-quote-bug.data
new file mode 100644
index 0000000..76ef7ae
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-single-quote-bug.data
@@ -0,0 +1,2 @@
+- "foo 'bar'"
+- "foo\n'bar'"
diff --git a/src/test/resources/pyyaml/invalid-starting-character.loader-error b/src/test/resources/pyyaml/invalid-starting-character.loader-error
new file mode 100644
index 0000000..bb81c60
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-starting-character.loader-error
@@ -0,0 +1 @@
+@@@@@@@@@@@@@@@@@@@
diff --git a/src/test/resources/pyyaml/invalid-tag-1.loader-error b/src/test/resources/pyyaml/invalid-tag-1.loader-error
new file mode 100644
index 0000000..a68cd38
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-1.loader-error
@@ -0,0 +1 @@
+- !<foo#bar> baz
diff --git a/src/test/resources/pyyaml/invalid-tag-2.loader-error b/src/test/resources/pyyaml/invalid-tag-2.loader-error
new file mode 100644
index 0000000..3a36700
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-2.loader-error
@@ -0,0 +1 @@
+- !prefix!foo#bar baz
diff --git a/src/test/resources/pyyaml/invalid-tag-directive-handle.loader-error b/src/test/resources/pyyaml/invalid-tag-directive-handle.loader-error
new file mode 100644
index 0000000..42b5d7e
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-directive-handle.loader-error
@@ -0,0 +1,2 @@
+%TAG !!! !!!
+---
diff --git a/src/test/resources/pyyaml/invalid-tag-directive-prefix.loader-error b/src/test/resources/pyyaml/invalid-tag-directive-prefix.loader-error
new file mode 100644
index 0000000..0cb482c
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-directive-prefix.loader-error
@@ -0,0 +1,2 @@
+%TAG    !   tag:zz.com/foo#bar  # '#' is not allowed in URLs
+---
diff --git a/src/test/resources/pyyaml/invalid-tag-handle-1.emitter-error b/src/test/resources/pyyaml/invalid-tag-handle-1.emitter-error
new file mode 100644
index 0000000..d5df9a2
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-handle-1.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!foo': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/invalid-tag-handle-1.loader-error b/src/test/resources/pyyaml/invalid-tag-handle-1.loader-error
new file mode 100644
index 0000000..ef0d143
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-handle-1.loader-error
@@ -0,0 +1,2 @@
+%TAG    foo bar
+---
diff --git a/src/test/resources/pyyaml/invalid-tag-handle-2.emitter-error b/src/test/resources/pyyaml/invalid-tag-handle-2.emitter-error
new file mode 100644
index 0000000..d1831d5
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-handle-2.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart { tags: { '!!!': 'bar' } }
+- !Scalar { value: 'foo' }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/invalid-tag-handle-2.loader-error b/src/test/resources/pyyaml/invalid-tag-handle-2.loader-error
new file mode 100644
index 0000000..06c7f0e
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-tag-handle-2.loader-error
@@ -0,0 +1,2 @@
+%TAG    !foo    bar
+---
diff --git a/src/test/resources/pyyaml/invalid-uri-escapes-1.loader-error b/src/test/resources/pyyaml/invalid-uri-escapes-1.loader-error
new file mode 100644
index 0000000..a6ecb36
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-uri-escapes-1.loader-error
@@ -0,0 +1 @@
+--- !<tag:%x?y> foo
diff --git a/src/test/resources/pyyaml/invalid-uri-escapes-2.loader-error b/src/test/resources/pyyaml/invalid-uri-escapes-2.loader-error
new file mode 100644
index 0000000..b89e8f6
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-uri-escapes-2.loader-error
@@ -0,0 +1 @@
+--- !<%FF> foo
diff --git a/src/test/resources/pyyaml/invalid-uri-escapes-3.loader-error b/src/test/resources/pyyaml/invalid-uri-escapes-3.loader-error
new file mode 100644
index 0000000..f2e4cb8
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-uri-escapes-3.loader-error
@@ -0,0 +1 @@
+--- !<foo%d0%af%d0%af%d0bar> baz
diff --git a/src/test/resources/pyyaml/invalid-uri.loader-error b/src/test/resources/pyyaml/invalid-uri.loader-error
new file mode 100644
index 0000000..06307e0
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-uri.loader-error
@@ -0,0 +1 @@
+--- !foo!   bar
diff --git a/src/test/resources/pyyaml/invalid-utf8-byte.loader-error b/src/test/resources/pyyaml/invalid-utf8-byte.loader-error
new file mode 100644
index 0000000..15111c3
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-utf8-byte.loader-error
@@ -0,0 +1,18 @@
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+Invalid byte ('\xFF'): ÿ <--
+-------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/test/resources/pyyaml/invalid-utf8-byte.stream-error b/src/test/resources/pyyaml/invalid-utf8-byte.stream-error
new file mode 100644
index 0000000..15111c3
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-utf8-byte.stream-error
@@ -0,0 +1,18 @@
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+-------------------------------------------------------------------------------------------------------------------------------
+Invalid byte ('\xFF'): ÿ <--
+-------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-1.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-1.loader-error
new file mode 100644
index 0000000..e9b4e3a
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-1.loader-error
@@ -0,0 +1,3 @@
+# No version at all.
+%YAML
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-2.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-2.loader-error
new file mode 100644
index 0000000..6aa7740
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-2.loader-error
@@ -0,0 +1,2 @@
+%YAML   1e-5
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-3.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-3.loader-error
new file mode 100644
index 0000000..345e784
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-3.loader-error
@@ -0,0 +1,2 @@
+%YAML 1.
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-4.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-4.loader-error
new file mode 100644
index 0000000..b35ca82
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-4.loader-error
@@ -0,0 +1,2 @@
+%YAML 1.132.435
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-5.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-5.loader-error
new file mode 100644
index 0000000..7c2b49f
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-5.loader-error
@@ -0,0 +1,2 @@
+%YAML A.0
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-directive-version-6.loader-error b/src/test/resources/pyyaml/invalid-yaml-directive-version-6.loader-error
new file mode 100644
index 0000000..bae714f
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-directive-version-6.loader-error
@@ -0,0 +1,2 @@
+%YAML 123.C
+---
diff --git a/src/test/resources/pyyaml/invalid-yaml-version.loader-error b/src/test/resources/pyyaml/invalid-yaml-version.loader-error
new file mode 100644
index 0000000..dd01948
--- /dev/null
+++ b/src/test/resources/pyyaml/invalid-yaml-version.loader-error
@@ -0,0 +1,2 @@
+%YAML   2.0
+--- foo
diff --git a/src/test/resources/pyyaml/mappings.events b/src/test/resources/pyyaml/mappings.events
new file mode 100644
index 0000000..3cb5579
--- /dev/null
+++ b/src/test/resources/pyyaml/mappings.events
@@ -0,0 +1,44 @@
+- !StreamStart
+
+- !DocumentStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
+- !Scalar { implicit: [true,true], value: 'empty mapping' }
+- !MappingStart
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'empty mapping with tag' }
+- !MappingStart { tag: '!mytag', implicit: false }
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'block mapping' }
+- !MappingStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingEnd
+- !Scalar { implicit: [true,true], value: 'flow mapping' }
+- !MappingStart { flow_style: true }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'value' }
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'complex' }
+- !Scalar { implicit: [true,true], value: 'key' }
+- !MappingEnd
+- !MappingEnd
+- !MappingEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/merge.data b/src/test/resources/pyyaml/merge.data
new file mode 100644
index 0000000..e455bbc
--- /dev/null
+++ b/src/test/resources/pyyaml/merge.data
@@ -0,0 +1 @@
+- <<
diff --git a/src/test/resources/pyyaml/more-floats.data b/src/test/resources/pyyaml/more-floats.data
new file mode 100644
index 0000000..399eb17
--- /dev/null
+++ b/src/test/resources/pyyaml/more-floats.data
@@ -0,0 +1 @@
+[0.0, +1.0, -1.0, +.inf, -.inf, .nan, .nan]
diff --git a/src/test/resources/pyyaml/negative-float-bug.data b/src/test/resources/pyyaml/negative-float-bug.data
new file mode 100644
index 0000000..18e16e3
--- /dev/null
+++ b/src/test/resources/pyyaml/negative-float-bug.data
@@ -0,0 +1 @@
+-1.0
diff --git a/src/test/resources/pyyaml/no-alias-anchor.emitter-error b/src/test/resources/pyyaml/no-alias-anchor.emitter-error
new file mode 100644
index 0000000..5ff065c
--- /dev/null
+++ b/src/test/resources/pyyaml/no-alias-anchor.emitter-error
@@ -0,0 +1,8 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { anchor: A, value: data }
+- !Alias { }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/no-block-collection-end.loader-error b/src/test/resources/pyyaml/no-block-collection-end.loader-error
new file mode 100644
index 0000000..02d4d37
--- /dev/null
+++ b/src/test/resources/pyyaml/no-block-collection-end.loader-error
@@ -0,0 +1,3 @@
+- foo
+- bar
+baz: bar
diff --git a/src/test/resources/pyyaml/no-block-mapping-end-2.loader-error b/src/test/resources/pyyaml/no-block-mapping-end-2.loader-error
new file mode 100644
index 0000000..be63571
--- /dev/null
+++ b/src/test/resources/pyyaml/no-block-mapping-end-2.loader-error
@@ -0,0 +1,3 @@
+? foo
+: bar
+: baz
diff --git a/src/test/resources/pyyaml/no-block-mapping-end.loader-error b/src/test/resources/pyyaml/no-block-mapping-end.loader-error
new file mode 100644
index 0000000..1ea921c
--- /dev/null
+++ b/src/test/resources/pyyaml/no-block-mapping-end.loader-error
@@ -0,0 +1 @@
+foo: "bar" "baz"
diff --git a/src/test/resources/pyyaml/no-document-start.loader-error b/src/test/resources/pyyaml/no-document-start.loader-error
new file mode 100644
index 0000000..c725ec8
--- /dev/null
+++ b/src/test/resources/pyyaml/no-document-start.loader-error
@@ -0,0 +1,3 @@
+%YAML   1.1
+# no ---
+foo: bar
diff --git a/src/test/resources/pyyaml/no-flow-mapping-end.loader-error b/src/test/resources/pyyaml/no-flow-mapping-end.loader-error
new file mode 100644
index 0000000..8bd1403
--- /dev/null
+++ b/src/test/resources/pyyaml/no-flow-mapping-end.loader-error
@@ -0,0 +1 @@
+{ foo: bar ]
diff --git a/src/test/resources/pyyaml/no-flow-sequence-end.loader-error b/src/test/resources/pyyaml/no-flow-sequence-end.loader-error
new file mode 100644
index 0000000..750d973
--- /dev/null
+++ b/src/test/resources/pyyaml/no-flow-sequence-end.loader-error
@@ -0,0 +1 @@
+[foo, bar}
diff --git a/src/test/resources/pyyaml/no-node-1.loader-error b/src/test/resources/pyyaml/no-node-1.loader-error
new file mode 100644
index 0000000..07b1500
--- /dev/null
+++ b/src/test/resources/pyyaml/no-node-1.loader-error
@@ -0,0 +1 @@
+- !foo ]
diff --git a/src/test/resources/pyyaml/no-node-2.loader-error b/src/test/resources/pyyaml/no-node-2.loader-error
new file mode 100644
index 0000000..563e3b3
--- /dev/null
+++ b/src/test/resources/pyyaml/no-node-2.loader-error
@@ -0,0 +1 @@
+- [ !foo } ]
diff --git a/src/test/resources/pyyaml/no-tag.emitter-error b/src/test/resources/pyyaml/no-tag.emitter-error
new file mode 100644
index 0000000..384c62f
--- /dev/null
+++ b/src/test/resources/pyyaml/no-tag.emitter-error
@@ -0,0 +1,5 @@
+- !StreamStart
+- !DocumentStart
+- !Scalar { value: 'foo', implicit: [false,false] }
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/null.data b/src/test/resources/pyyaml/null.data
new file mode 100644
index 0000000..ad12528
--- /dev/null
+++ b/src/test/resources/pyyaml/null.data
@@ -0,0 +1,3 @@
+-
+- ~
+- null
diff --git a/src/test/resources/pyyaml/odd-utf16.stream-error b/src/test/resources/pyyaml/odd-utf16.stream-error
new file mode 100644
index 0000000..37da060
--- /dev/null
+++ b/src/test/resources/pyyaml/odd-utf16.stream-error
Binary files differ
diff --git a/src/test/resources/pyyaml/remove-possible-simple-key-bug.loader-error b/src/test/resources/pyyaml/remove-possible-simple-key-bug.loader-error
new file mode 100644
index 0000000..fe1bc6c
--- /dev/null
+++ b/src/test/resources/pyyaml/remove-possible-simple-key-bug.loader-error
@@ -0,0 +1,3 @@
+foo: &A bar
+*A ]    # The ']' indicator triggers remove_possible_simple_key,
+        # which should raise an error.
diff --git a/src/test/resources/pyyaml/resolver.data b/src/test/resources/pyyaml/resolver.data
new file mode 100644
index 0000000..a296404
--- /dev/null
+++ b/src/test/resources/pyyaml/resolver.data
@@ -0,0 +1,30 @@
+---
+"this scalar should be selected"
+---
+key11: !foo
+    key12:
+        is: [selected]
+    key22:
+        key13: [not, selected]
+        key23: [not, selected]
+    key32:
+        key31: [not, selected]
+        key32: [not, selected]
+        key33: {not: selected}
+key21: !bar
+    - not selected
+    - selected
+    - not selected
+key31: !baz
+    key12:
+        key13:
+            key14: {selected}
+        key23:
+            key14: [not, selected]
+        key33:
+            key14: {selected}
+            key24: {not: selected}
+    key22:
+        -   key14: {selected}
+            key24: {not: selected}
+        -   key14: {selected}
diff --git a/src/test/resources/pyyaml/run-parser-crash-bug.data b/src/test/resources/pyyaml/run-parser-crash-bug.data
new file mode 100644
index 0000000..fe01734
--- /dev/null
+++ b/src/test/resources/pyyaml/run-parser-crash-bug.data
@@ -0,0 +1,8 @@
+---
+- Harry Potter and the Prisoner of Azkaban
+- Harry Potter and the Goblet of Fire
+- Harry Potter and the Order of the Phoenix
+---
+- Memoirs Found in a Bathtub
+- Snow Crash
+- Ghost World
diff --git a/src/test/resources/pyyaml/scalars.events b/src/test/resources/pyyaml/scalars.events
new file mode 100644
index 0000000..32c40f4
--- /dev/null
+++ b/src/test/resources/pyyaml/scalars.events
@@ -0,0 +1,28 @@
+- !StreamStart
+
+- !DocumentStart
+- !MappingStart
+- !Scalar { implicit: [true,true], value: 'empty scalar' }
+- !Scalar { implicit: [true,false], value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar' }
+- !Scalar { implicit: [true,true], value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar' }
+- !Scalar { value: 'data', style: '"' }
+- !Scalar { implicit: [true,true], value: 'block scalar' }
+- !Scalar { value: 'data', style: '|' }
+- !Scalar { implicit: [true,true], value: 'empty scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: '' }
+- !Scalar { implicit: [true,true], value: 'implicit scalar with tag' }
+- !Scalar { implicit: [false,false], tag: '!mytag', value: 'data' }
+- !Scalar { implicit: [true,true], value: 'quoted scalar with tag' }
+- !Scalar { value: 'data', style: '"', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'block scalar with tag' }
+- !Scalar { value: 'data', style: '|', tag: '!mytag', implicit: [false,false] }
+- !Scalar { implicit: [true,true], value: 'single character' }
+- !Scalar { value: 'a', implicit: [true,true] }
+- !Scalar { implicit: [true,true], value: 'single digit' }
+- !Scalar { value: '1', implicit: [true,false] }
+- !MappingEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/scan-document-end-bug.canonical b/src/test/resources/pyyaml/scan-document-end-bug.canonical
new file mode 100644
index 0000000..4a0e8a8
--- /dev/null
+++ b/src/test/resources/pyyaml/scan-document-end-bug.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!null ""
diff --git a/src/test/resources/pyyaml/scan-document-end-bug.data b/src/test/resources/pyyaml/scan-document-end-bug.data
new file mode 100644
index 0000000..3c70543
--- /dev/null
+++ b/src/test/resources/pyyaml/scan-document-end-bug.data
@@ -0,0 +1,3 @@
+# Ticket #4
+---
+...
\ No newline at end of file
diff --git a/src/test/resources/pyyaml/scan-line-break-bug.canonical b/src/test/resources/pyyaml/scan-line-break-bug.canonical
new file mode 100644
index 0000000..79f08b7
--- /dev/null
+++ b/src/test/resources/pyyaml/scan-line-break-bug.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!map { ? !!str "foo" : !!str "bar baz" }
diff --git a/src/test/resources/pyyaml/scan-line-break-bug.data b/src/test/resources/pyyaml/scan-line-break-bug.data
new file mode 100644
index 0000000..5856454
--- /dev/null
+++ b/src/test/resources/pyyaml/scan-line-break-bug.data
@@ -0,0 +1,3 @@
+foo:
+    bar
+    baz
diff --git a/src/test/resources/pyyaml/sequences.events b/src/test/resources/pyyaml/sequences.events
new file mode 100644
index 0000000..692a329
--- /dev/null
+++ b/src/test/resources/pyyaml/sequences.events
@@ -0,0 +1,81 @@
+- !StreamStart
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceStart
+- !SequenceEnd
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceEnd
+- !SequenceStart
+- !Scalar
+- !Scalar { value: 'data' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
+- !SequenceEnd
+- !SequenceStart
+- !SequenceStart
+- !SequenceStart
+- !Scalar
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceStart
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !SequenceStart
+- !Scalar { value: 'data' }
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !MappingStart
+- !Scalar { value: 'key1' }
+- !SequenceStart
+- !Scalar { value: 'data1' }
+- !Scalar { value: 'data2' }
+- !SequenceEnd
+- !Scalar { value: 'key2' }
+- !SequenceStart { tag: '!mytag1', implicit: false }
+- !Scalar { value: 'data3' }
+- !SequenceStart
+- !Scalar { value: 'data4' }
+- !Scalar { value: 'data5' }
+- !SequenceEnd
+- !SequenceStart { tag: '!mytag2', implicit: false }
+- !Scalar { value: 'data6' }
+- !Scalar { value: 'data7' }
+- !SequenceEnd
+- !SequenceEnd
+- !MappingEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !DocumentStart
+- !SequenceStart
+- !SequenceStart { flow_style: true }
+- !SequenceStart
+- !SequenceEnd
+- !Scalar
+- !Scalar { value: 'data' }
+- !Scalar { tag: '!mytag', implicit: [false,false], value: 'data' }
+- !SequenceStart { tag: '!mytag', implicit: false }
+- !Scalar { value: 'data' }
+- !Scalar { value: 'data' }
+- !SequenceEnd
+- !SequenceEnd
+- !SequenceEnd
+- !DocumentEnd
+
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/single-dot-is-not-float-bug.data b/src/test/resources/pyyaml/single-dot-is-not-float-bug.data
new file mode 100644
index 0000000..9c558e3
--- /dev/null
+++ b/src/test/resources/pyyaml/single-dot-is-not-float-bug.data
@@ -0,0 +1 @@
+.
diff --git a/src/test/resources/pyyaml/sloppy-indentation.canonical b/src/test/resources/pyyaml/sloppy-indentation.canonical
new file mode 100644
index 0000000..438bc04
--- /dev/null
+++ b/src/test/resources/pyyaml/sloppy-indentation.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!map { 
+    ? !!str "in the block context"
+    : !!map {
+        ? !!str "indentation should be kept"
+        : !!map {
+            ? !!str "but in the flow context"
+            : !!seq [ !!str "it may be violated" ]
+        }
+    }
+}
+--- !!str
+"the parser does not require scalars to be indented with at least one space"
+--- !!str
+"the parser does not require scalars to be indented with at least one space"
+--- !!map
+{ ? !!str "foo": { ? !!str "bar" : !!str "quoted scalars may not adhere indentation" } }
diff --git a/src/test/resources/pyyaml/sloppy-indentation.data b/src/test/resources/pyyaml/sloppy-indentation.data
new file mode 100644
index 0000000..2eb4f5a
--- /dev/null
+++ b/src/test/resources/pyyaml/sloppy-indentation.data
@@ -0,0 +1,17 @@
+---
+in the block context:
+    indentation should be kept: { 
+    but in the flow context: [
+it may be violated]
+}
+---
+the parser does not require scalars
+to be indented with at least one space
+...
+---
+"the parser does not require scalars
+to be indented with at least one space"
+---
+foo:
+    bar: 'quoted scalars
+may not adhere indentation'
diff --git a/src/test/resources/pyyaml/spec-02-01.data b/src/test/resources/pyyaml/spec-02-01.data
new file mode 100644
index 0000000..d12e671
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-01.data
@@ -0,0 +1,3 @@
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
diff --git a/src/test/resources/pyyaml/spec-02-01.tokens b/src/test/resources/pyyaml/spec-02-01.tokens
new file mode 100644
index 0000000..ce44cac
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-01.tokens
@@ -0,0 +1 @@
+[[ , _ , _ , _ ]}
diff --git a/src/test/resources/pyyaml/spec-02-02.data b/src/test/resources/pyyaml/spec-02-02.data
new file mode 100644
index 0000000..7b7ec94
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-02.data
@@ -0,0 +1,3 @@
+hr:  65    # Home runs
+avg: 0.278 # Batting average
+rbi: 147   # Runs Batted In
diff --git a/src/test/resources/pyyaml/spec-02-02.tokens b/src/test/resources/pyyaml/spec-02-02.tokens
new file mode 100644
index 0000000..e4e381b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-02.tokens
@@ -0,0 +1,5 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-03.data b/src/test/resources/pyyaml/spec-02-03.data
new file mode 100644
index 0000000..656d628
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-03.data
@@ -0,0 +1,8 @@
+american:
+  - Boston Red Sox
+  - Detroit Tigers
+  - New York Yankees
+national:
+  - New York Mets
+  - Chicago Cubs
+  - Atlanta Braves
diff --git a/src/test/resources/pyyaml/spec-02-03.tokens b/src/test/resources/pyyaml/spec-02-03.tokens
new file mode 100644
index 0000000..89815f2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-03.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : [[ , _ , _ , _ ]}
+? _ : [[ , _ , _ , _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-04.data b/src/test/resources/pyyaml/spec-02-04.data
new file mode 100644
index 0000000..430f6b3
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-04.data
@@ -0,0 +1,8 @@
+-
+  name: Mark McGwire
+  hr:   65
+  avg:  0.278
+-
+  name: Sammy Sosa
+  hr:   63
+  avg:  0.288
diff --git a/src/test/resources/pyyaml/spec-02-04.tokens b/src/test/resources/pyyaml/spec-02-04.tokens
new file mode 100644
index 0000000..9cb9815
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-04.tokens
@@ -0,0 +1,4 @@
+[[
+, {{ ? _ : _ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ? _ : _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-05.data b/src/test/resources/pyyaml/spec-02-05.data
new file mode 100644
index 0000000..cdd7770
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-05.data
@@ -0,0 +1,3 @@
+- [name        , hr, avg  ]
+- [Mark McGwire, 65, 0.278]
+- [Sammy Sosa  , 63, 0.288]
diff --git a/src/test/resources/pyyaml/spec-02-05.tokens b/src/test/resources/pyyaml/spec-02-05.tokens
new file mode 100644
index 0000000..3f6f1ab
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-05.tokens
@@ -0,0 +1,5 @@
+[[
+, [ _ , _ , _ ]
+, [ _ , _ , _ ]
+, [ _ , _ , _ ]
+]}
diff --git a/src/test/resources/pyyaml/spec-02-06.data b/src/test/resources/pyyaml/spec-02-06.data
new file mode 100644
index 0000000..7a957b2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-06.data
@@ -0,0 +1,5 @@
+Mark McGwire: {hr: 65, avg: 0.278}
+Sammy Sosa: {
+    hr: 63,
+    avg: 0.288
+  }
diff --git a/src/test/resources/pyyaml/spec-02-06.tokens b/src/test/resources/pyyaml/spec-02-06.tokens
new file mode 100644
index 0000000..a1a5eef
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-06.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : { ? _ : _ , ? _ : _ }
+? _ : { ? _ : _ , ? _ : _ }
+]}
diff --git a/src/test/resources/pyyaml/spec-02-07.data b/src/test/resources/pyyaml/spec-02-07.data
new file mode 100644
index 0000000..bc711d5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-07.data
@@ -0,0 +1,10 @@
+# Ranking of 1998 home runs
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+# Team ranking
+---
+- Chicago Cubs
+- St Louis Cardinals
diff --git a/src/test/resources/pyyaml/spec-02-07.tokens b/src/test/resources/pyyaml/spec-02-07.tokens
new file mode 100644
index 0000000..ed48883
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-07.tokens
@@ -0,0 +1,12 @@
+---
+[[
+, _
+, _
+, _
+]}
+
+---
+[[
+, _
+, _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-08.data b/src/test/resources/pyyaml/spec-02-08.data
new file mode 100644
index 0000000..05e102d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-08.data
@@ -0,0 +1,10 @@
+---
+time: 20:03:20
+player: Sammy Sosa
+action: strike (miss)
+...
+---
+time: 20:03:47
+player: Sammy Sosa
+action: grand slam
+...
diff --git a/src/test/resources/pyyaml/spec-02-08.tokens b/src/test/resources/pyyaml/spec-02-08.tokens
new file mode 100644
index 0000000..7d2c03d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-08.tokens
@@ -0,0 +1,15 @@
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+...
+
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+...
diff --git a/src/test/resources/pyyaml/spec-02-09.data b/src/test/resources/pyyaml/spec-02-09.data
new file mode 100644
index 0000000..e264180
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-09.data
@@ -0,0 +1,8 @@
+---
+hr: # 1998 hr ranking
+  - Mark McGwire
+  - Sammy Sosa
+rbi:
+  # 1998 rbi ranking
+  - Sammy Sosa
+  - Ken Griffey
diff --git a/src/test/resources/pyyaml/spec-02-09.tokens b/src/test/resources/pyyaml/spec-02-09.tokens
new file mode 100644
index 0000000..b2ec10e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-09.tokens
@@ -0,0 +1,5 @@
+---
+{{
+? _ : [[ , _ , _ ]}
+? _ : [[ , _ , _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-10.data b/src/test/resources/pyyaml/spec-02-10.data
new file mode 100644
index 0000000..61808f6
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-10.data
@@ -0,0 +1,8 @@
+---
+hr:
+  - Mark McGwire
+  # Following node labeled SS
+  - &SS Sammy Sosa
+rbi:
+  - *SS # Subsequent occurrence
+  - Ken Griffey
diff --git a/src/test/resources/pyyaml/spec-02-10.tokens b/src/test/resources/pyyaml/spec-02-10.tokens
new file mode 100644
index 0000000..26caa2b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-10.tokens
@@ -0,0 +1,5 @@
+---
+{{
+? _ : [[ , _ , & _ ]}
+? _ : [[ , * , _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-11.data b/src/test/resources/pyyaml/spec-02-11.data
new file mode 100644
index 0000000..9123ce2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-11.data
@@ -0,0 +1,9 @@
+? - Detroit Tigers
+  - Chicago cubs
+:
+  - 2001-07-23
+
+? [ New York Yankees,
+    Atlanta Braves ]
+: [ 2001-07-02, 2001-08-12,
+    2001-08-14 ]
diff --git a/src/test/resources/pyyaml/spec-02-11.tokens b/src/test/resources/pyyaml/spec-02-11.tokens
new file mode 100644
index 0000000..fe24203
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-11.tokens
@@ -0,0 +1,6 @@
+{{
+? [[ , _ , _ ]}
+: [[ , _ ]}
+? [ _ , _ ]
+: [ _ , _ , _ ]
+]}
diff --git a/src/test/resources/pyyaml/spec-02-12.data b/src/test/resources/pyyaml/spec-02-12.data
new file mode 100644
index 0000000..1fc33f9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-12.data
@@ -0,0 +1,8 @@
+---
+# products purchased
+- item    : Super Hoop
+  quantity: 1
+- item    : Basketball
+  quantity: 4
+- item    : Big Shoes
+  quantity: 1
diff --git a/src/test/resources/pyyaml/spec-02-12.tokens b/src/test/resources/pyyaml/spec-02-12.tokens
new file mode 100644
index 0000000..ea21e50
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-12.tokens
@@ -0,0 +1,6 @@
+---
+[[
+, {{ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ]}
+, {{ ? _ : _ ? _ : _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-13.data b/src/test/resources/pyyaml/spec-02-13.data
new file mode 100644
index 0000000..13fb656
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-13.data
@@ -0,0 +1,4 @@
+# ASCII Art
+--- |
+  \//||\/||
+  // ||  ||__
diff --git a/src/test/resources/pyyaml/spec-02-13.tokens b/src/test/resources/pyyaml/spec-02-13.tokens
new file mode 100644
index 0000000..7456c05
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-13.tokens
@@ -0,0 +1 @@
+--- _
diff --git a/src/test/resources/pyyaml/spec-02-14.data b/src/test/resources/pyyaml/spec-02-14.data
new file mode 100644
index 0000000..59943de
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-14.data
@@ -0,0 +1,4 @@
+---
+  Mark McGwire's
+  year was crippled
+  by a knee injury.
diff --git a/src/test/resources/pyyaml/spec-02-14.tokens b/src/test/resources/pyyaml/spec-02-14.tokens
new file mode 100644
index 0000000..7456c05
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-14.tokens
@@ -0,0 +1 @@
+--- _
diff --git a/src/test/resources/pyyaml/spec-02-15.data b/src/test/resources/pyyaml/spec-02-15.data
new file mode 100644
index 0000000..80b89a6
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-15.data
@@ -0,0 +1,8 @@
+>
+ Sammy Sosa completed another
+ fine season with great stats.
+
+   63 Home Runs
+   0.288 Batting Average
+
+ What a year!
diff --git a/src/test/resources/pyyaml/spec-02-15.tokens b/src/test/resources/pyyaml/spec-02-15.tokens
new file mode 100644
index 0000000..31354ec
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-15.tokens
@@ -0,0 +1 @@
+_
diff --git a/src/test/resources/pyyaml/spec-02-16.data b/src/test/resources/pyyaml/spec-02-16.data
new file mode 100644
index 0000000..9f66d88
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-16.data
@@ -0,0 +1,7 @@
+name: Mark McGwire
+accomplishment: >
+  Mark set a major league
+  home run record in 1998.
+stats: |
+  65 Home Runs
+  0.278 Batting Average
diff --git a/src/test/resources/pyyaml/spec-02-16.tokens b/src/test/resources/pyyaml/spec-02-16.tokens
new file mode 100644
index 0000000..e4e381b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-16.tokens
@@ -0,0 +1,5 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-17.data b/src/test/resources/pyyaml/spec-02-17.data
new file mode 100644
index 0000000..3e899c0
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-17.data
@@ -0,0 +1,7 @@
+unicode: "Sosa did fine.\u263A"
+control: "\b1998\t1999\t2000\n"
+hexesc:  "\x0D\x0A is \r\n"
+
+single: '"Howdy!" he cried.'
+quoted: ' # not a ''comment''.'
+tie-fighter: '|\-*-/|'
diff --git a/src/test/resources/pyyaml/spec-02-17.tokens b/src/test/resources/pyyaml/spec-02-17.tokens
new file mode 100644
index 0000000..db65540
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-17.tokens
@@ -0,0 +1,8 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-18.data b/src/test/resources/pyyaml/spec-02-18.data
new file mode 100644
index 0000000..e0a8bfa
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-18.data
@@ -0,0 +1,6 @@
+plain:
+  This unquoted scalar
+  spans many lines.
+
+quoted: "So does this
+  quoted scalar.\n"
diff --git a/src/test/resources/pyyaml/spec-02-18.tokens b/src/test/resources/pyyaml/spec-02-18.tokens
new file mode 100644
index 0000000..83b31dc
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-18.tokens
@@ -0,0 +1,4 @@
+{{
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-19.data b/src/test/resources/pyyaml/spec-02-19.data
new file mode 100644
index 0000000..bf69de6
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-19.data
@@ -0,0 +1,5 @@
+canonical: 12345
+decimal: +12,345
+sexagesimal: 3:25:45
+octal: 014
+hexadecimal: 0xC
diff --git a/src/test/resources/pyyaml/spec-02-19.tokens b/src/test/resources/pyyaml/spec-02-19.tokens
new file mode 100644
index 0000000..5bda68f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-19.tokens
@@ -0,0 +1,7 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-20.data b/src/test/resources/pyyaml/spec-02-20.data
new file mode 100644
index 0000000..1d4897f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-20.data
@@ -0,0 +1,6 @@
+canonical: 1.23015e+3
+exponential: 12.3015e+02
+sexagesimal: 20:30.15
+fixed: 1,230.15
+negative infinity: -.inf
+not a number: .NaN
diff --git a/src/test/resources/pyyaml/spec-02-20.tokens b/src/test/resources/pyyaml/spec-02-20.tokens
new file mode 100644
index 0000000..db65540
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-20.tokens
@@ -0,0 +1,8 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-21.data b/src/test/resources/pyyaml/spec-02-21.data
new file mode 100644
index 0000000..dec6a56
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-21.data
@@ -0,0 +1,4 @@
+null: ~
+true: y
+false: n
+string: '12345'
diff --git a/src/test/resources/pyyaml/spec-02-21.tokens b/src/test/resources/pyyaml/spec-02-21.tokens
new file mode 100644
index 0000000..aeccbaf
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-21.tokens
@@ -0,0 +1,6 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-22.data b/src/test/resources/pyyaml/spec-02-22.data
new file mode 100644
index 0000000..aaac185
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-22.data
@@ -0,0 +1,4 @@
+canonical: 2001-12-15T02:59:43.1Z
+iso8601: 2001-12-14t21:59:43.10-05:00
+spaced: 2001-12-14 21:59:43.10 -5
+date: 2002-12-14
diff --git a/src/test/resources/pyyaml/spec-02-22.tokens b/src/test/resources/pyyaml/spec-02-22.tokens
new file mode 100644
index 0000000..aeccbaf
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-22.tokens
@@ -0,0 +1,6 @@
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-23.data b/src/test/resources/pyyaml/spec-02-23.data
new file mode 100644
index 0000000..5dbd992
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-23.data
@@ -0,0 +1,13 @@
+---
+not-date: !!str 2002-04-28
+
+picture: !!binary |
+ R0lGODlhDAAMAIQAAP//9/X
+ 17unp5WZmZgAAAOfn515eXv
+ Pz7Y6OjuDg4J+fn5OTk6enp
+ 56enmleECcgggoBADs=
+
+application specific tag: !something |
+ The semantics of the tag
+ above may be different for
+ different documents.
diff --git a/src/test/resources/pyyaml/spec-02-23.tokens b/src/test/resources/pyyaml/spec-02-23.tokens
new file mode 100644
index 0000000..9ac54aa
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-23.tokens
@@ -0,0 +1,6 @@
+---
+{{
+? _ : ! _
+? _ : ! _
+? _ : ! _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-24.data b/src/test/resources/pyyaml/spec-02-24.data
new file mode 100644
index 0000000..1180757
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-24.data
@@ -0,0 +1,14 @@
+%TAG ! tag:clarkevans.com,2002:
+--- !shape
+  # Use the ! handle for presenting
+  # tag:clarkevans.com,2002:circle
+- !circle
+  center: &ORIGIN {x: 73, y: 129}
+  radius: 7
+- !line
+  start: *ORIGIN
+  finish: { x: 89, y: 102 }
+- !label
+  start: *ORIGIN
+  color: 0xFFEEBB
+  text: Pretty vector drawing.
diff --git a/src/test/resources/pyyaml/spec-02-24.tokens b/src/test/resources/pyyaml/spec-02-24.tokens
new file mode 100644
index 0000000..039c385
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-24.tokens
@@ -0,0 +1,20 @@
+%
+--- !
+[[
+, !
+    {{
+    ? _ : & { ? _ : _ , ? _ : _ }
+    ? _ : _
+    ]}
+, !
+    {{
+    ? _ : *
+    ? _ : { ? _ : _ , ? _ : _ }
+    ]}
+, !
+    {{
+    ? _ : *
+    ? _ : _
+    ? _ : _
+    ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-25.data b/src/test/resources/pyyaml/spec-02-25.data
new file mode 100644
index 0000000..769ac31
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-25.data
@@ -0,0 +1,7 @@
+# sets are represented as a
+# mapping where each key is
+# associated with the empty string
+--- !!set
+? Mark McGwire
+? Sammy Sosa
+? Ken Griff
diff --git a/src/test/resources/pyyaml/spec-02-25.tokens b/src/test/resources/pyyaml/spec-02-25.tokens
new file mode 100644
index 0000000..b700236
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-25.tokens
@@ -0,0 +1,6 @@
+--- !
+{{
+? _
+? _
+? _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-26.data b/src/test/resources/pyyaml/spec-02-26.data
new file mode 100644
index 0000000..3143763
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-26.data
@@ -0,0 +1,7 @@
+# ordered maps are represented as
+# a sequence of mappings, with
+# each mapping having one key
+--- !!omap
+- Mark McGwire: 65
+- Sammy Sosa: 63
+- Ken Griffy: 58
diff --git a/src/test/resources/pyyaml/spec-02-26.tokens b/src/test/resources/pyyaml/spec-02-26.tokens
new file mode 100644
index 0000000..7bee492
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-26.tokens
@@ -0,0 +1,6 @@
+--- !
+[[
+, {{ ? _ : _ ]}
+, {{ ? _ : _ ]}
+, {{ ? _ : _ ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-02-27.data b/src/test/resources/pyyaml/spec-02-27.data
new file mode 100644
index 0000000..4625739
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-27.data
@@ -0,0 +1,29 @@
+--- !<tag:clarkevans.com,2002:invoice>
+invoice: 34843
+date   : 2001-01-23
+bill-to: &id001
+    given  : Chris
+    family : Dumars
+    address:
+        lines: |
+            458 Walkman Dr.
+            Suite #292
+        city    : Royal Oak
+        state   : MI
+        postal  : 48046
+ship-to: *id001
+product:
+    - sku         : BL394D
+      quantity    : 4
+      description : Basketball
+      price       : 450.00
+    - sku         : BL4438H
+      quantity    : 1
+      description : Super Hoop
+      price       : 2392.00
+tax  : 251.42
+total: 4443.52
+comments:
+    Late afternoon is best.
+    Backup contact is Nancy
+    Billsmer @ 338-4338.
diff --git a/src/test/resources/pyyaml/spec-02-27.tokens b/src/test/resources/pyyaml/spec-02-27.tokens
new file mode 100644
index 0000000..2dc1c25
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-27.tokens
@@ -0,0 +1,20 @@
+--- !
+{{
+? _ : _
+? _ : _
+? _ : &
+    {{
+    ? _ : _
+    ? _ : _
+    ? _ : {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+    ]}
+? _ : *
+? _ :
+    [[
+    , {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+    , {{ ? _ : _ ? _ : _ ? _ : _ ? _ : _ ]}
+    ]}
+? _ : _
+? _ : _
+? _ : _
+]}
diff --git a/src/test/resources/pyyaml/spec-02-28.data b/src/test/resources/pyyaml/spec-02-28.data
new file mode 100644
index 0000000..a5c8dc8
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-28.data
@@ -0,0 +1,26 @@
+---
+Time: 2001-11-23 15:01:42 -5
+User: ed
+Warning:
+  This is an error message
+  for the log file
+---
+Time: 2001-11-23 15:02:31 -5
+User: ed
+Warning:
+  A slightly different error
+  message.
+---
+Date: 2001-11-23 15:03:17 -5
+User: ed
+Fatal:
+  Unknown variable "bar"
+Stack:
+  - file: TopClass.py
+    line: 23
+    code: |
+      x = MoreObject("345\n")
+  - file: MoreClass.py
+    line: 58
+    code: |-
+      foo = bar
diff --git a/src/test/resources/pyyaml/spec-02-28.tokens b/src/test/resources/pyyaml/spec-02-28.tokens
new file mode 100644
index 0000000..8d5e1bc
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-02-28.tokens
@@ -0,0 +1,23 @@
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+]}
+---
+{{
+? _ : _
+? _ : _
+? _ : _
+? _ :
+    [[
+        , {{ ? _ : _ ? _ : _ ? _ : _ ]}
+        , {{ ? _ : _ ? _ : _ ? _ : _ ]}
+    ]}
+]}
diff --git a/src/test/resources/pyyaml/spec-05-01-utf16be.data b/src/test/resources/pyyaml/spec-05-01-utf16be.data
new file mode 100644
index 0000000..3525062
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-01-utf16be.data
Binary files differ
diff --git a/src/test/resources/pyyaml/spec-05-01-utf16le.data b/src/test/resources/pyyaml/spec-05-01-utf16le.data
new file mode 100644
index 0000000..0823f74
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-01-utf16le.data
Binary files differ
diff --git a/src/test/resources/pyyaml/spec-05-01-utf8.data b/src/test/resources/pyyaml/spec-05-01-utf8.data
new file mode 100644
index 0000000..780d25b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-01-utf8.data
@@ -0,0 +1 @@
+# Comment only.
diff --git a/src/test/resources/pyyaml/spec-05-02-utf16be.data b/src/test/resources/pyyaml/spec-05-02-utf16be.data
new file mode 100644
index 0000000..5ebbb04
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-02-utf16be.data
Binary files differ
diff --git a/src/test/resources/pyyaml/spec-05-02-utf16le.data b/src/test/resources/pyyaml/spec-05-02-utf16le.data
new file mode 100644
index 0000000..0cd90a2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-02-utf16le.data
Binary files differ
diff --git a/src/test/resources/pyyaml/spec-05-02-utf8.data b/src/test/resources/pyyaml/spec-05-02-utf8.data
new file mode 100644
index 0000000..fb74866
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-02-utf8.data
@@ -0,0 +1,3 @@
+# Invalid use of BOM
+# inside a
+# document.
diff --git a/src/test/resources/pyyaml/spec-05-03.canonical b/src/test/resources/pyyaml/spec-05-03.canonical
new file mode 100644
index 0000000..a143a73
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-03.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "sequence"
+  : !!seq [
+    !!str "one", !!str "two"
+  ],
+  ? !!str "mapping"
+  : !!map {
+    ? !!str "sky" : !!str "blue",
+#    ? !!str "sea" : !!str "green",
+    ? !!map { ? !!str "sea" : !!str "green" } : !!null "",
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-05-03.data b/src/test/resources/pyyaml/spec-05-03.data
new file mode 100644
index 0000000..4661f33
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-03.data
@@ -0,0 +1,7 @@
+sequence:
+- one
+- two
+mapping:
+  ? sky
+  : blue
+  ? sea : green
diff --git a/src/test/resources/pyyaml/spec-05-04.canonical b/src/test/resources/pyyaml/spec-05-04.canonical
new file mode 100644
index 0000000..00c9723
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-04.canonical
@@ -0,0 +1,13 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "sequence"
+  : !!seq [
+    !!str "one", !!str "two"
+  ],
+  ? !!str "mapping"
+  : !!map {
+    ? !!str "sky" : !!str "blue",
+    ? !!str "sea" : !!str "green",
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-05-04.data b/src/test/resources/pyyaml/spec-05-04.data
new file mode 100644
index 0000000..df33847
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-04.data
@@ -0,0 +1,2 @@
+sequence: [ one, two, ]
+mapping: { sky: blue, sea: green }
diff --git a/src/test/resources/pyyaml/spec-05-05.data b/src/test/resources/pyyaml/spec-05-05.data
new file mode 100644
index 0000000..62524c0
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-05.data
@@ -0,0 +1 @@
+# Comment only.
diff --git a/src/test/resources/pyyaml/spec-05-06.canonical b/src/test/resources/pyyaml/spec-05-06.canonical
new file mode 100644
index 0000000..4f30c11
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-06.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "anchored"
+  : &A1 !local "value",
+  ? !!str "alias"
+  : *A1,
+}
diff --git a/src/test/resources/pyyaml/spec-05-06.data b/src/test/resources/pyyaml/spec-05-06.data
new file mode 100644
index 0000000..7a1f9b3
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-06.data
@@ -0,0 +1,2 @@
+anchored: !local &anchor value
+alias: *anchor
diff --git a/src/test/resources/pyyaml/spec-05-07.canonical b/src/test/resources/pyyaml/spec-05-07.canonical
new file mode 100644
index 0000000..dc3732a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-07.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "literal"
+  : !!str "text\n",
+  ? !!str "folded"
+  : !!str "text\n",
+}
diff --git a/src/test/resources/pyyaml/spec-05-07.data b/src/test/resources/pyyaml/spec-05-07.data
new file mode 100644
index 0000000..97eb3a3
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-07.data
@@ -0,0 +1,4 @@
+literal: |
+  text
+folded: >
+  text
diff --git a/src/test/resources/pyyaml/spec-05-08.canonical b/src/test/resources/pyyaml/spec-05-08.canonical
new file mode 100644
index 0000000..610bd68
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-08.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "single"
+  : !!str "text",
+  ? !!str "double"
+  : !!str "text",
+}
diff --git a/src/test/resources/pyyaml/spec-05-08.data b/src/test/resources/pyyaml/spec-05-08.data
new file mode 100644
index 0000000..04ebf69
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-08.data
@@ -0,0 +1,2 @@
+single: 'text'
+double: "text"
diff --git a/src/test/resources/pyyaml/spec-05-09.canonical b/src/test/resources/pyyaml/spec-05-09.canonical
new file mode 100644
index 0000000..597e3de
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-09.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "text"
diff --git a/src/test/resources/pyyaml/spec-05-09.data b/src/test/resources/pyyaml/spec-05-09.data
new file mode 100644
index 0000000..a43431b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-09.data
@@ -0,0 +1,2 @@
+%YAML 1.1
+--- text
diff --git a/src/test/resources/pyyaml/spec-05-10.data b/src/test/resources/pyyaml/spec-05-10.data
new file mode 100644
index 0000000..a4caf91
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-10.data
@@ -0,0 +1,2 @@
+commercial-at: @text
+grave-accent: `text
diff --git a/src/test/resources/pyyaml/spec-05-11.canonical b/src/test/resources/pyyaml/spec-05-11.canonical
new file mode 100644
index 0000000..fc25bef
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-11.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+--- !!str
+"Generic line break (no glyph)\n\
+ Generic line break (glyphed)\n\
+ Line separator\u2028\
+ Paragraph separator\u2029"
diff --git a/src/test/resources/pyyaml/spec-05-11.data b/src/test/resources/pyyaml/spec-05-11.data
new file mode 100644
index 0000000..b448b75
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-11.data
@@ -0,0 +1,3 @@
+|
+  Generic line break (no glyph)
+  Generic line break (glyphed)…  Line separator
  Paragraph separator

\ No newline at end of file
diff --git a/src/test/resources/pyyaml/spec-05-12.data b/src/test/resources/pyyaml/spec-05-12.data
new file mode 100644
index 0000000..7c3ad7f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-12.data
@@ -0,0 +1,9 @@
+# Tabs do's and don'ts:
+# comment: 	
+quoted: "Quoted		"
+block: |
+  void main() {
+  	printf("Hello, world!\n");
+  }
+elsewhere:	# separation
+	indentation, in	plain scalar
diff --git a/src/test/resources/pyyaml/spec-05-13.canonical b/src/test/resources/pyyaml/spec-05-13.canonical
new file mode 100644
index 0000000..90c1c5c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-13.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+--- !!str
+"Text containing \
+ both space and \
+ tab	characters"
diff --git a/src/test/resources/pyyaml/spec-05-13.data b/src/test/resources/pyyaml/spec-05-13.data
new file mode 100644
index 0000000..fce7951
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-13.data
@@ -0,0 +1,3 @@
+  "Text containing   
+  both space and	
+  	tab	characters"
diff --git a/src/test/resources/pyyaml/spec-05-14.canonical b/src/test/resources/pyyaml/spec-05-14.canonical
new file mode 100644
index 0000000..4bff01c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-14.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+"Fun with \x5C
+ \x22 \x07 \x08 \x1B \x0C
+ \x0A \x0D \x09 \x0B \x00
+ \x20 \xA0 \x85 \u2028 \u2029
+ A A A"
diff --git a/src/test/resources/pyyaml/spec-05-14.data b/src/test/resources/pyyaml/spec-05-14.data
new file mode 100644
index 0000000..d6e8ce4
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-14.data
@@ -0,0 +1,2 @@
+"Fun with \\
+ \" \a \b \e \f \… \n \r \t \v \0 \
 \  \_ \N \L \P \
 \x41 \u0041 \U00000041"
diff --git a/src/test/resources/pyyaml/spec-05-15.data b/src/test/resources/pyyaml/spec-05-15.data
new file mode 100644
index 0000000..7bf12b6
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-05-15.data
@@ -0,0 +1,3 @@
+Bad escapes:
+  "\c
+  \xq-"
diff --git a/src/test/resources/pyyaml/spec-06-01.canonical b/src/test/resources/pyyaml/spec-06-01.canonical
new file mode 100644
index 0000000..f17ec92
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-01.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "Not indented"
+  : !!map {
+      ? !!str "By one space"
+      : !!str "By four\n  spaces\n",
+      ? !!str "Flow style"
+      : !!seq [
+          !!str "By two",
+          !!str "Also by two",
+          !!str "Still by two",
+        ]
+    }
+}
diff --git a/src/test/resources/pyyaml/spec-06-01.data b/src/test/resources/pyyaml/spec-06-01.data
new file mode 100644
index 0000000..6134ba1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-01.data
@@ -0,0 +1,14 @@
+  # Leading comment line spaces are
+   # neither content nor indentation.
+    
+Not indented:
+ By one space: |
+    By four
+      spaces
+ Flow style: [    # Leading spaces
+   By two,        # in flow style
+  Also by two,    # are neither
+# Tabs are not allowed:
+#  	Still by two   # content nor
+    Still by two   # content nor
+    ]             # indentation.
diff --git a/src/test/resources/pyyaml/spec-06-02.data b/src/test/resources/pyyaml/spec-06-02.data
new file mode 100644
index 0000000..ff741e5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-02.data
@@ -0,0 +1,3 @@
+  # Comment
+   
+
diff --git a/src/test/resources/pyyaml/spec-06-03.canonical b/src/test/resources/pyyaml/spec-06-03.canonical
new file mode 100644
index 0000000..ec26902
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-03.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "key"
+  : !!str "value"
+}
diff --git a/src/test/resources/pyyaml/spec-06-03.data b/src/test/resources/pyyaml/spec-06-03.data
new file mode 100644
index 0000000..9db0912
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-03.data
@@ -0,0 +1,2 @@
+key:    # Comment
+  value
diff --git a/src/test/resources/pyyaml/spec-06-04.canonical b/src/test/resources/pyyaml/spec-06-04.canonical
new file mode 100644
index 0000000..ec26902
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-04.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "key"
+  : !!str "value"
+}
diff --git a/src/test/resources/pyyaml/spec-06-04.data b/src/test/resources/pyyaml/spec-06-04.data
new file mode 100644
index 0000000..86308dd
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-04.data
@@ -0,0 +1,4 @@
+key:    # Comment
+        # lines
+  value
+
diff --git a/src/test/resources/pyyaml/spec-06-05.canonical b/src/test/resources/pyyaml/spec-06-05.canonical
new file mode 100644
index 0000000..8da431d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-05.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+  ? !!map {
+    ? !!str "first"
+    : !!str "Sammy",
+    ? !!str "last"
+    : !!str "Sosa"
+  }
+  : !!map {
+    ? !!str "hr"
+    : !!int "65",
+    ? !!str "avg"
+    : !!float "0.278"
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-06-05.data b/src/test/resources/pyyaml/spec-06-05.data
new file mode 100644
index 0000000..37613f5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-05.data
@@ -0,0 +1,6 @@
+{ first: Sammy, last: Sosa }:
+# Statistics:
+  hr:  # Home runs
+    65
+  avg: # Average
+    0.278
diff --git a/src/test/resources/pyyaml/spec-06-06.canonical b/src/test/resources/pyyaml/spec-06-06.canonical
new file mode 100644
index 0000000..513d07a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-06.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "plain"
+  : !!str "text lines",
+  ? !!str "quoted"
+  : !!str "text lines",
+  ? !!str "block"
+  : !!str "text\n 	lines\n"
+}
diff --git a/src/test/resources/pyyaml/spec-06-06.data b/src/test/resources/pyyaml/spec-06-06.data
new file mode 100644
index 0000000..2f62d08
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-06.data
@@ -0,0 +1,7 @@
+plain: text
+  lines
+quoted: "text
+  	lines"
+block: |
+  text
+   	lines
diff --git a/src/test/resources/pyyaml/spec-06-07.canonical b/src/test/resources/pyyaml/spec-06-07.canonical
new file mode 100644
index 0000000..11357e4
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-07.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "foo\nbar",
+  !!str "foo\n\nbar"
+]
diff --git a/src/test/resources/pyyaml/spec-06-07.data b/src/test/resources/pyyaml/spec-06-07.data
new file mode 100644
index 0000000..130cfa7
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-07.data
@@ -0,0 +1,8 @@
+- foo
+ 
+  bar
+- |-
+  foo
+ 
+  bar
+  
diff --git a/src/test/resources/pyyaml/spec-06-08.canonical b/src/test/resources/pyyaml/spec-06-08.canonical
new file mode 100644
index 0000000..cc72bc8
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-08.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+--- !!str
+"specific\L\
+ trimmed\n\n\n\
+ as space"
diff --git a/src/test/resources/pyyaml/spec-06-08.data b/src/test/resources/pyyaml/spec-06-08.data
new file mode 100644
index 0000000..f2896ed
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-06-08.data
@@ -0,0 +1,2 @@
+>-
+  specific
  trimmed…  … ……  as…  space
diff --git a/src/test/resources/pyyaml/spec-07-01.canonical b/src/test/resources/pyyaml/spec-07-01.canonical
new file mode 100644
index 0000000..8c8c48d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-01.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+--- !!str
+"foo"
diff --git a/src/test/resources/pyyaml/spec-07-01.data b/src/test/resources/pyyaml/spec-07-01.data
new file mode 100644
index 0000000..2113eb6
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-01.data
@@ -0,0 +1,3 @@
+%FOO  bar baz # Should be ignored
+               # with a warning.
+--- "foo"
diff --git a/src/test/resources/pyyaml/spec-07-02.canonical b/src/test/resources/pyyaml/spec-07-02.canonical
new file mode 100644
index 0000000..cb7dd1c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-02.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "foo"
diff --git a/src/test/resources/pyyaml/spec-07-02.data b/src/test/resources/pyyaml/spec-07-02.data
new file mode 100644
index 0000000..c8b7322
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-02.data
@@ -0,0 +1,4 @@
+%YAML 1.2 # Attempt parsing
+           # with a warning
+---
+"foo"
diff --git a/src/test/resources/pyyaml/spec-07-03.data b/src/test/resources/pyyaml/spec-07-03.data
new file mode 100644
index 0000000..4bfa07a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-03.data
@@ -0,0 +1,3 @@
+%YAML 1.1
+%YAML 1.1
+foo
diff --git a/src/test/resources/pyyaml/spec-07-04.canonical b/src/test/resources/pyyaml/spec-07-04.canonical
new file mode 100644
index 0000000..cb7dd1c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-04.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "foo"
diff --git a/src/test/resources/pyyaml/spec-07-04.data b/src/test/resources/pyyaml/spec-07-04.data
new file mode 100644
index 0000000..50f5ab9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-04.data
@@ -0,0 +1,3 @@
+%TAG !yaml! tag:yaml.org,2002:
+---
+!yaml!str "foo"
diff --git a/src/test/resources/pyyaml/spec-07-05.data b/src/test/resources/pyyaml/spec-07-05.data
new file mode 100644
index 0000000..7276eae
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-05.data
@@ -0,0 +1,3 @@
+%TAG ! !foo
+%TAG ! !foo
+bar
diff --git a/src/test/resources/pyyaml/spec-07-06.canonical b/src/test/resources/pyyaml/spec-07-06.canonical
new file mode 100644
index 0000000..bddf616
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-06.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+  !<!foobar> "baz",
+  !<tag:yaml.org,2002:str> "string"
+]
diff --git a/src/test/resources/pyyaml/spec-07-06.data b/src/test/resources/pyyaml/spec-07-06.data
new file mode 100644
index 0000000..d9854cb
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-06.data
@@ -0,0 +1,5 @@
+%TAG !      !foo
+%TAG !yaml! tag:yaml.org,2002:
+---
+- !bar "baz"
+- !yaml!str "string"
diff --git a/src/test/resources/pyyaml/spec-07-07a.canonical b/src/test/resources/pyyaml/spec-07-07a.canonical
new file mode 100644
index 0000000..fa086df
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-07a.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!<!foo> "bar"
diff --git a/src/test/resources/pyyaml/spec-07-07a.data b/src/test/resources/pyyaml/spec-07-07a.data
new file mode 100644
index 0000000..9d42ec3
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-07a.data
@@ -0,0 +1,2 @@
+# Private application:
+!foo "bar"
diff --git a/src/test/resources/pyyaml/spec-07-07b.canonical b/src/test/resources/pyyaml/spec-07-07b.canonical
new file mode 100644
index 0000000..fe917d8
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-07b.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!<tag:ben-kiki.org,2000:app/foo> "bar"
diff --git a/src/test/resources/pyyaml/spec-07-07b.data b/src/test/resources/pyyaml/spec-07-07b.data
new file mode 100644
index 0000000..2d36d0e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-07b.data
@@ -0,0 +1,4 @@
+# Migrated to global:
+%TAG ! tag:ben-kiki.org,2000:app/
+---
+!foo "bar"
diff --git a/src/test/resources/pyyaml/spec-07-08.canonical b/src/test/resources/pyyaml/spec-07-08.canonical
new file mode 100644
index 0000000..703aa7b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-08.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+  !<!foo> "bar",
+  !<tag:yaml.org,2002:str> "string",
+  !<tag:ben-kiki.org,2000:type> "baz"
+]
diff --git a/src/test/resources/pyyaml/spec-07-08.data b/src/test/resources/pyyaml/spec-07-08.data
new file mode 100644
index 0000000..e2c6d9e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-08.data
@@ -0,0 +1,9 @@
+# Explicitly specify default settings:
+%TAG !     !
+%TAG !!    tag:yaml.org,2002:
+# Named handles have no default:
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !foo "bar"
+- !!str "string"
+- !o!type "baz"
diff --git a/src/test/resources/pyyaml/spec-07-09.canonical b/src/test/resources/pyyaml/spec-07-09.canonical
new file mode 100644
index 0000000..32d9e94
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-09.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!str "foo"
+%YAML 1.1
+---
+!!str "bar"
+%YAML 1.1
+---
+!!str "baz"
diff --git a/src/test/resources/pyyaml/spec-07-09.data b/src/test/resources/pyyaml/spec-07-09.data
new file mode 100644
index 0000000..1209d47
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-09.data
@@ -0,0 +1,11 @@
+---
+foo
+...
+# Repeated end marker.
+...
+---
+bar
+# No end marker.
+---
+baz
+...
diff --git a/src/test/resources/pyyaml/spec-07-10.canonical b/src/test/resources/pyyaml/spec-07-10.canonical
new file mode 100644
index 0000000..1db650a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-10.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!str "Root flow scalar"
+%YAML 1.1
+---
+!!str "Root block scalar\n"
+%YAML 1.1
+---
+!!map {
+  ? !!str "foo"
+  : !!str "bar"
+}
+---
+#!!str ""
+!!null ""
diff --git a/src/test/resources/pyyaml/spec-07-10.data b/src/test/resources/pyyaml/spec-07-10.data
new file mode 100644
index 0000000..6939b39
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-10.data
@@ -0,0 +1,11 @@
+"Root flow
+ scalar"
+--- !!str >
+ Root block
+ scalar
+---
+# Root collection:
+foo : bar
+... # Is optional.
+---
+# Explicit document may be empty.
diff --git a/src/test/resources/pyyaml/spec-07-11.data b/src/test/resources/pyyaml/spec-07-11.data
new file mode 100644
index 0000000..d11302d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-11.data
@@ -0,0 +1,2 @@
+# A stream may contain
+# no documents.
diff --git a/src/test/resources/pyyaml/spec-07-12a.canonical b/src/test/resources/pyyaml/spec-07-12a.canonical
new file mode 100644
index 0000000..efc116f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-12a.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "foo"
+  : !!str "bar"
+}
diff --git a/src/test/resources/pyyaml/spec-07-12a.data b/src/test/resources/pyyaml/spec-07-12a.data
new file mode 100644
index 0000000..3807d57
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-12a.data
@@ -0,0 +1,3 @@
+# Implicit document. Root
+# collection (mapping) node.
+foo : bar
diff --git a/src/test/resources/pyyaml/spec-07-12b.canonical b/src/test/resources/pyyaml/spec-07-12b.canonical
new file mode 100644
index 0000000..04bcffc
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-12b.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "Text content\n"
diff --git a/src/test/resources/pyyaml/spec-07-12b.data b/src/test/resources/pyyaml/spec-07-12b.data
new file mode 100644
index 0000000..43250db
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-12b.data
@@ -0,0 +1,4 @@
+# Explicit document. Root
+# scalar (literal) node.
+--- |
+ Text content
diff --git a/src/test/resources/pyyaml/spec-07-13.canonical b/src/test/resources/pyyaml/spec-07-13.canonical
new file mode 100644
index 0000000..5af71e9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-13.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!str "First document"
+---
+!<!foo> "No directives"
+---
+!<!foobar> "With directives"
+---
+!<!baz> "Reset settings"
diff --git a/src/test/resources/pyyaml/spec-07-13.data b/src/test/resources/pyyaml/spec-07-13.data
new file mode 100644
index 0000000..ba7ec63
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-07-13.data
@@ -0,0 +1,9 @@
+! "First document"
+---
+!foo "No directives"
+%TAG ! !foo
+---
+!bar "With directives"
+%YAML 1.1
+---
+!baz "Reset settings"
diff --git a/src/test/resources/pyyaml/spec-08-01.canonical b/src/test/resources/pyyaml/spec-08-01.canonical
new file mode 100644
index 0000000..69e4161
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-01.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? &A1 !!str "foo"
+  : !!str "bar",
+  ? &A2 !!str "baz"
+  : *A1
+}
diff --git a/src/test/resources/pyyaml/spec-08-01.data b/src/test/resources/pyyaml/spec-08-01.data
new file mode 100644
index 0000000..48986ec
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-01.data
@@ -0,0 +1,2 @@
+!!str &a1 "foo" : !!str bar
+&a2 baz : *a1
diff --git a/src/test/resources/pyyaml/spec-08-02.canonical b/src/test/resources/pyyaml/spec-08-02.canonical
new file mode 100644
index 0000000..dd6f76e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-02.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "First occurrence"
+  : &A !!str "Value",
+  ? !!str "Second occurrence"
+  : *A
+}
diff --git a/src/test/resources/pyyaml/spec-08-02.data b/src/test/resources/pyyaml/spec-08-02.data
new file mode 100644
index 0000000..600d179
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-02.data
@@ -0,0 +1,2 @@
+First occurrence: &anchor Value
+Second occurrence: *anchor
diff --git a/src/test/resources/pyyaml/spec-08-03.canonical b/src/test/resources/pyyaml/spec-08-03.canonical
new file mode 100644
index 0000000..be7ea8f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-03.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!map {
+  ? !<tag:yaml.org,2002:str> "foo"
+  : !<!bar> "baz"
+}
diff --git a/src/test/resources/pyyaml/spec-08-03.data b/src/test/resources/pyyaml/spec-08-03.data
new file mode 100644
index 0000000..8e51f52
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-03.data
@@ -0,0 +1,2 @@
+!<tag:yaml.org,2002:str> foo :
+  !<!bar> baz
diff --git a/src/test/resources/pyyaml/spec-08-04.data b/src/test/resources/pyyaml/spec-08-04.data
new file mode 100644
index 0000000..f7d1b01
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-04.data
@@ -0,0 +1,2 @@
+- !<!> foo
+- !<$:?> bar
diff --git a/src/test/resources/pyyaml/spec-08-05.canonical b/src/test/resources/pyyaml/spec-08-05.canonical
new file mode 100644
index 0000000..a5c710a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-05.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+  !<!local> "foo",
+  !<tag:yaml.org,2002:str> "bar",
+  !<tag:ben-kiki.org,2000:type> "baz",
+]
diff --git a/src/test/resources/pyyaml/spec-08-05.data b/src/test/resources/pyyaml/spec-08-05.data
new file mode 100644
index 0000000..93576ed
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-05.data
@@ -0,0 +1,5 @@
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !local foo
+- !!str bar
+- !o!type baz
diff --git a/src/test/resources/pyyaml/spec-08-06.data b/src/test/resources/pyyaml/spec-08-06.data
new file mode 100644
index 0000000..8580010
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-06.data
@@ -0,0 +1,5 @@
+%TAG !o! tag:ben-kiki.org,2000:
+---
+- !$a!b foo
+- !o! bar
+- !h!type baz
diff --git a/src/test/resources/pyyaml/spec-08-07.canonical b/src/test/resources/pyyaml/spec-08-07.canonical
new file mode 100644
index 0000000..e2f43d9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-07.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+  !<tag:yaml.org,2002:str> "12",
+  !<tag:yaml.org,2002:int> "12",
+#  !<tag:yaml.org,2002:str> "12",
+  !<tag:yaml.org,2002:int> "12",
+]
diff --git a/src/test/resources/pyyaml/spec-08-07.data b/src/test/resources/pyyaml/spec-08-07.data
new file mode 100644
index 0000000..98aa565
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-07.data
@@ -0,0 +1,4 @@
+# Assuming conventional resolution:
+- "12"
+- 12
+- ! 12
diff --git a/src/test/resources/pyyaml/spec-08-08.canonical b/src/test/resources/pyyaml/spec-08-08.canonical
new file mode 100644
index 0000000..d3f8b1a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-08.canonical
@@ -0,0 +1,15 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "foo"
+  : !!str "bar baz"
+}
+%YAML 1.1
+---
+!!str "foo bar"
+%YAML 1.1
+---
+!!str "foo bar"
+%YAML 1.1
+---
+!!str "foo\n"
diff --git a/src/test/resources/pyyaml/spec-08-08.data b/src/test/resources/pyyaml/spec-08-08.data
new file mode 100644
index 0000000..757a93d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-08.data
@@ -0,0 +1,13 @@
+---
+foo:
+ "bar
+ baz"
+---
+"foo
+ bar"
+---
+foo
+ bar
+--- |
+ foo
+...
diff --git a/src/test/resources/pyyaml/spec-08-09.canonical b/src/test/resources/pyyaml/spec-08-09.canonical
new file mode 100644
index 0000000..3805daf
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-09.canonical
@@ -0,0 +1,21 @@
+%YAML 1.1
+--- !!map {
+  ? !!str "scalars" : !!map {
+      ? !!str "plain"
+      : !!str "some text",
+      ? !!str "quoted"
+      : !!map {
+        ? !!str "single"
+        : !!str "some text",
+        ? !!str "double"
+        : !!str "some text"
+  } },
+  ? !!str "collections" : !!map {
+    ? !!str "sequence" : !!seq [
+      !!str "entry",
+      !!map {
+        ? !!str "key" : !!str "value"
+    } ],
+    ? !!str "mapping" : !!map {
+      ? !!str "key" : !!str "value"
+} } }
diff --git a/src/test/resources/pyyaml/spec-08-09.data b/src/test/resources/pyyaml/spec-08-09.data
new file mode 100644
index 0000000..69da042
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-09.data
@@ -0,0 +1,11 @@
+---
+scalars:
+  plain: !!str some text
+  quoted:
+    single: 'some text'
+    double: "some text"
+collections:
+  sequence: !!seq [ !!str entry,
+    # Mapping entry:
+      key: value ]
+  mapping: { key: value }
diff --git a/src/test/resources/pyyaml/spec-08-10.canonical b/src/test/resources/pyyaml/spec-08-10.canonical
new file mode 100644
index 0000000..8281c5e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-10.canonical
@@ -0,0 +1,23 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "block styles" : !!map {
+    ? !!str "scalars" : !!map {
+      ? !!str "literal"
+      : !!str "#!/usr/bin/perl\n\
+          print \"Hello,
+          world!\\n\";\n",
+      ? !!str "folded"
+      : !!str "This sentence
+          is false.\n"
+    },
+    ? !!str "collections" : !!map {
+      ? !!str "sequence" : !!seq [
+        !!str "entry",
+        !!map {
+          ? !!str "key" : !!str "value"
+        }
+      ],
+      ? !!str "mapping" : !!map {
+        ? !!str "key" : !!str "value"
+} } } }
diff --git a/src/test/resources/pyyaml/spec-08-10.data b/src/test/resources/pyyaml/spec-08-10.data
new file mode 100644
index 0000000..72acc56
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-10.data
@@ -0,0 +1,15 @@
+block styles:
+  scalars:
+    literal: !!str |
+      #!/usr/bin/perl
+      print "Hello, world!\n";
+    folded: >
+      This sentence
+      is false.
+  collections: !!map
+    sequence: !!seq # Entry:
+      - entry # Plain
+      # Mapping entry:
+      - key: value
+    mapping: 
+      key: value
diff --git a/src/test/resources/pyyaml/spec-08-11.canonical b/src/test/resources/pyyaml/spec-08-11.canonical
new file mode 100644
index 0000000..dd6f76e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-11.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "First occurrence"
+  : &A !!str "Value",
+  ? !!str "Second occurrence"
+  : *A
+}
diff --git a/src/test/resources/pyyaml/spec-08-11.data b/src/test/resources/pyyaml/spec-08-11.data
new file mode 100644
index 0000000..600d179
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-11.data
@@ -0,0 +1,2 @@
+First occurrence: &anchor Value
+Second occurrence: *anchor
diff --git a/src/test/resources/pyyaml/spec-08-12.canonical b/src/test/resources/pyyaml/spec-08-12.canonical
new file mode 100644
index 0000000..93899f4
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-12.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "Without properties",
+  &A !!str "Anchored",
+  !!str "Tagged",
+  *A,
+  !!str "",
+  !!str "",
+]
diff --git a/src/test/resources/pyyaml/spec-08-12.data b/src/test/resources/pyyaml/spec-08-12.data
new file mode 100644
index 0000000..3d4c6b7
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-12.data
@@ -0,0 +1,8 @@
+[
+  Without properties,
+  &anchor "Anchored",
+  !!str 'Tagged',
+  *anchor, # Alias node
+  !!str ,  # Empty plain scalar
+  '',   # Empty plain scalar
+]
diff --git a/src/test/resources/pyyaml/spec-08-13.canonical b/src/test/resources/pyyaml/spec-08-13.canonical
new file mode 100644
index 0000000..618bb7b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-13.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "foo"
+#  : !!str "",
+#  ? !!str ""
+  : !!null "",
+  ? !!null ""
+  : !!str "bar",
+}
diff --git a/src/test/resources/pyyaml/spec-08-13.data b/src/test/resources/pyyaml/spec-08-13.data
new file mode 100644
index 0000000..ebe663a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-13.data
@@ -0,0 +1,4 @@
+{
+  ? foo :,
+  ? : bar,
+}
diff --git a/src/test/resources/pyyaml/spec-08-14.canonical b/src/test/resources/pyyaml/spec-08-14.canonical
new file mode 100644
index 0000000..11db439
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-14.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "flow in block",
+  !!str "Block scalar\n",
+  !!map {
+    ? !!str "foo"
+    : !!str "bar"
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-08-14.data b/src/test/resources/pyyaml/spec-08-14.data
new file mode 100644
index 0000000..2fbb1f7
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-14.data
@@ -0,0 +1,5 @@
+- "flow in block"
+- >
+ Block scalar
+- !!map # Block collection
+  foo : bar
diff --git a/src/test/resources/pyyaml/spec-08-15.canonical b/src/test/resources/pyyaml/spec-08-15.canonical
new file mode 100644
index 0000000..76f028e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-15.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!seq [
+  !!null "",
+  !!map {
+    ? !!str "foo"
+    : !!null "",
+    ? !!null ""
+    : !!str "bar",
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-08-15.data b/src/test/resources/pyyaml/spec-08-15.data
new file mode 100644
index 0000000..7c86bcf
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-08-15.data
@@ -0,0 +1,5 @@
+- # Empty plain scalar
+- ? foo
+  :
+  ?
+  : bar
diff --git a/src/test/resources/pyyaml/spec-09-01.canonical b/src/test/resources/pyyaml/spec-09-01.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-01.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "simple key"
+  : !!map {
+    ? !!str "also simple"
+    : !!str "value",
+    ? !!str "not a simple key"
+    : !!str "any value"
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-09-01.data b/src/test/resources/pyyaml/spec-09-01.data
new file mode 100644
index 0000000..9e83eaf
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-01.data
@@ -0,0 +1,6 @@
+"simple key" : {
+  "also simple" : value,
+  ? "not a
+  simple key" : "any
+  value"
+}
diff --git a/src/test/resources/pyyaml/spec-09-02.canonical b/src/test/resources/pyyaml/spec-09-02.canonical
new file mode 100644
index 0000000..6f8f41a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-02.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "as space \
+  trimmed\n\
+  specific\L\n\
+  escaped\t\n\
+  none"
diff --git a/src/test/resources/pyyaml/spec-09-02.data b/src/test/resources/pyyaml/spec-09-02.data
new file mode 100644
index 0000000..d84883d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-02.data
@@ -0,0 +1,6 @@
+ "as space	
+ trimmed 
+
+ specific

+ escaped	\
 
+ none"
diff --git a/src/test/resources/pyyaml/spec-09-03.canonical b/src/test/resources/pyyaml/spec-09-03.canonical
new file mode 100644
index 0000000..658c6df
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-03.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+  !!str " last",
+  !!str " last",
+  !!str " \tfirst last",
+]
diff --git a/src/test/resources/pyyaml/spec-09-03.data b/src/test/resources/pyyaml/spec-09-03.data
new file mode 100644
index 0000000..e0b914d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-03.data
@@ -0,0 +1,6 @@
+- "
+  last"
+- " 	
+  last"
+- " 	first
+  last"
diff --git a/src/test/resources/pyyaml/spec-09-04.canonical b/src/test/resources/pyyaml/spec-09-04.canonical
new file mode 100644
index 0000000..fa46632
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-04.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "first \
+  inner 1  \
+  inner 2 \
+  last"
diff --git a/src/test/resources/pyyaml/spec-09-04.data b/src/test/resources/pyyaml/spec-09-04.data
new file mode 100644
index 0000000..313a91b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-04.data
@@ -0,0 +1,4 @@
+ "first
+ 	inner 1	
+ \ inner 2 \
+ last"
diff --git a/src/test/resources/pyyaml/spec-09-05.canonical b/src/test/resources/pyyaml/spec-09-05.canonical
new file mode 100644
index 0000000..24d1052
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-05.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "first ",
+  !!str "first\nlast",
+  !!str "first inner  \tlast",
+]
diff --git a/src/test/resources/pyyaml/spec-09-05.data b/src/test/resources/pyyaml/spec-09-05.data
new file mode 100644
index 0000000..624c30e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-05.data
@@ -0,0 +1,8 @@
+- "first
+  	"
+- "first
+
+  	last"
+- "first
+ inner
+ \ 	last"
diff --git a/src/test/resources/pyyaml/spec-09-06.canonical b/src/test/resources/pyyaml/spec-09-06.canonical
new file mode 100644
index 0000000..5028772
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-06.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "here's to \"quotes\""
diff --git a/src/test/resources/pyyaml/spec-09-06.data b/src/test/resources/pyyaml/spec-09-06.data
new file mode 100644
index 0000000..b038078
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-06.data
@@ -0,0 +1 @@
+ 'here''s to "quotes"'
diff --git a/src/test/resources/pyyaml/spec-09-07.canonical b/src/test/resources/pyyaml/spec-09-07.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-07.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "simple key"
+  : !!map {
+    ? !!str "also simple"
+    : !!str "value",
+    ? !!str "not a simple key"
+    : !!str "any value"
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-09-07.data b/src/test/resources/pyyaml/spec-09-07.data
new file mode 100644
index 0000000..755b54a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-07.data
@@ -0,0 +1,6 @@
+'simple key' : {
+  'also simple' : value,
+  ? 'not a
+  simple key' : 'any
+  value'
+}
diff --git a/src/test/resources/pyyaml/spec-09-08.canonical b/src/test/resources/pyyaml/spec-09-08.canonical
new file mode 100644
index 0000000..06abdb5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-08.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "as space \
+  trimmed\n\
+  specific\L\n\
+  none"
diff --git a/src/test/resources/pyyaml/spec-09-08.data b/src/test/resources/pyyaml/spec-09-08.data
new file mode 100644
index 0000000..aa4d458
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-08.data
@@ -0,0 +1 @@
+ 'as space	… trimmed …… specific
… none'
diff --git a/src/test/resources/pyyaml/spec-09-09.canonical b/src/test/resources/pyyaml/spec-09-09.canonical
new file mode 100644
index 0000000..658c6df
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-09.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!seq [
+  !!str " last",
+  !!str " last",
+  !!str " \tfirst last",
+]
diff --git a/src/test/resources/pyyaml/spec-09-09.data b/src/test/resources/pyyaml/spec-09-09.data
new file mode 100644
index 0000000..52171df
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-09.data
@@ -0,0 +1,6 @@
+- '
+  last'
+- ' 	
+  last'
+- ' 	first
+  last'
diff --git a/src/test/resources/pyyaml/spec-09-10.canonical b/src/test/resources/pyyaml/spec-09-10.canonical
new file mode 100644
index 0000000..2028d04
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-10.canonical
@@ -0,0 +1,5 @@
+%YAML 1.1
+---
+!!str "first \
+  inner \
+  last"
diff --git a/src/test/resources/pyyaml/spec-09-10.data b/src/test/resources/pyyaml/spec-09-10.data
new file mode 100644
index 0000000..0e41449
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-10.data
@@ -0,0 +1,3 @@
+ 'first
+ 	inner	
+ last'
diff --git a/src/test/resources/pyyaml/spec-09-11.canonical b/src/test/resources/pyyaml/spec-09-11.canonical
new file mode 100644
index 0000000..4eb222c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-11.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "first ",
+  !!str "first\nlast",
+]
diff --git a/src/test/resources/pyyaml/spec-09-11.data b/src/test/resources/pyyaml/spec-09-11.data
new file mode 100644
index 0000000..5efa873
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-11.data
@@ -0,0 +1,5 @@
+- 'first
+  	'
+- 'first
+
+  	last'
diff --git a/src/test/resources/pyyaml/spec-09-12.canonical b/src/test/resources/pyyaml/spec-09-12.canonical
new file mode 100644
index 0000000..d8e6dce
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-12.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "::std::vector",
+  !!str "Up, up, and away!",
+  !!int "-123",
+  !!seq [
+    !!str "::std::vector",
+    !!str "Up, up, and away!",
+    !!int "-123",
+  ]
+]
diff --git a/src/test/resources/pyyaml/spec-09-12.data b/src/test/resources/pyyaml/spec-09-12.data
new file mode 100644
index 0000000..b9a3ac5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-12.data
@@ -0,0 +1,8 @@
+# Outside flow collection:
+- ::std::vector
+- Up, up, and away!
+- -123
+# Inside flow collection:
+- [ '::std::vector',
+  "Up, up, and away!",
+  -123 ]
diff --git a/src/test/resources/pyyaml/spec-09-13.canonical b/src/test/resources/pyyaml/spec-09-13.canonical
new file mode 100644
index 0000000..e71a548
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-13.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "simple key"
+  : !!map {
+    ? !!str "also simple"
+    : !!str "value",
+    ? !!str "not a simple key"
+    : !!str "any value"
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-09-13.data b/src/test/resources/pyyaml/spec-09-13.data
new file mode 100644
index 0000000..b156386
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-13.data
@@ -0,0 +1,6 @@
+simple key : {
+  also simple : value,
+  ? not a
+  simple key : any
+  value
+}
diff --git a/src/test/resources/pyyaml/spec-09-14.data b/src/test/resources/pyyaml/spec-09-14.data
new file mode 100644
index 0000000..97f2316
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-14.data
@@ -0,0 +1,14 @@
+---
+--- ||| : foo
+... >>>: bar
+---
+[
+---
+,
+... ,
+{
+--- :
+... # Nested
+}
+]
+...
diff --git a/src/test/resources/pyyaml/spec-09-15.canonical b/src/test/resources/pyyaml/spec-09-15.canonical
new file mode 100644
index 0000000..df02040
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-15.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "---"
+  : !!str "foo",
+  ? !!str "..."
+  : !!str "bar"
+}
+%YAML 1.1
+---
+!!seq [
+  !!str "---",
+  !!str "...",
+  !!map {
+    ? !!str "---"
+    : !!str "..."
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-09-15.data b/src/test/resources/pyyaml/spec-09-15.data
new file mode 100644
index 0000000..e6863b0
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-15.data
@@ -0,0 +1,13 @@
+---
+"---" : foo
+...: bar
+---
+[
+---,
+...,
+{
+? ---
+: ...
+}
+]
+...
diff --git a/src/test/resources/pyyaml/spec-09-16.canonical b/src/test/resources/pyyaml/spec-09-16.canonical
new file mode 100644
index 0000000..06abdb5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-16.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!str "as space \
+  trimmed\n\
+  specific\L\n\
+  none"
diff --git a/src/test/resources/pyyaml/spec-09-16.data b/src/test/resources/pyyaml/spec-09-16.data
new file mode 100644
index 0000000..473beb9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-16.data
@@ -0,0 +1,3 @@
+# Tabs are confusing:
+# as space/trimmed/specific/none
+ as space … trimmed …… specific
… none
diff --git a/src/test/resources/pyyaml/spec-09-17.canonical b/src/test/resources/pyyaml/spec-09-17.canonical
new file mode 100644
index 0000000..68cb70d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-17.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "first line\n\
+      more line"
diff --git a/src/test/resources/pyyaml/spec-09-17.data b/src/test/resources/pyyaml/spec-09-17.data
new file mode 100644
index 0000000..97bc46c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-17.data
@@ -0,0 +1,3 @@
+ first line 
+   
+  more line
diff --git a/src/test/resources/pyyaml/spec-09-18.canonical b/src/test/resources/pyyaml/spec-09-18.canonical
new file mode 100644
index 0000000..f21428f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-18.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "literal\n",
+  !!str " folded\n",
+  !!str "keep\n\n",
+  !!str " strip",
+]
diff --git a/src/test/resources/pyyaml/spec-09-18.data b/src/test/resources/pyyaml/spec-09-18.data
new file mode 100644
index 0000000..68c5d7c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-18.data
@@ -0,0 +1,9 @@
+- | # Just the style
+ literal
+- >1 # Indentation indicator
+  folded
+- |+ # Chomping indicator
+ keep
+
+- >-1 # Both indicators
+  strip
diff --git a/src/test/resources/pyyaml/spec-09-19.canonical b/src/test/resources/pyyaml/spec-09-19.canonical
new file mode 100644
index 0000000..3e828d7
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-19.canonical
@@ -0,0 +1,6 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "literal\n",
+  !!str "folded\n",
+]
diff --git a/src/test/resources/pyyaml/spec-09-19.data b/src/test/resources/pyyaml/spec-09-19.data
new file mode 100644
index 0000000..f0e589d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-19.data
@@ -0,0 +1,4 @@
+- |
+ literal
+- >
+ folded
diff --git a/src/test/resources/pyyaml/spec-09-20.canonical b/src/test/resources/pyyaml/spec-09-20.canonical
new file mode 100644
index 0000000..d03bef5
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-20.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "detected\n",
+  !!str "\n\n# detected\n",
+  !!str " explicit\n",
+  !!str "\t\ndetected\n",
+]
diff --git a/src/test/resources/pyyaml/spec-09-20.data b/src/test/resources/pyyaml/spec-09-20.data
new file mode 100644
index 0000000..39bee04
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-20.data
@@ -0,0 +1,11 @@
+- |
+ detected
+- >
+ 
+  
+  # detected
+- |1
+  explicit
+- >
+ 	
+ detected
diff --git a/src/test/resources/pyyaml/spec-09-21.data b/src/test/resources/pyyaml/spec-09-21.data
new file mode 100644
index 0000000..0fdd14f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-21.data
@@ -0,0 +1,8 @@
+- |
+  
+ text
+- >
+  text
+ text
+- |1
+ text
diff --git a/src/test/resources/pyyaml/spec-09-22.canonical b/src/test/resources/pyyaml/spec-09-22.canonical
new file mode 100644
index 0000000..c1bbcd2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-22.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "strip"
+  : !!str "text",
+  ? !!str "clip"
+  : !!str "text\n",
+  ? !!str "keep"
+  : !!str "text\L",
+}
diff --git a/src/test/resources/pyyaml/spec-09-22.data b/src/test/resources/pyyaml/spec-09-22.data
new file mode 100644
index 0000000..0dd51eb
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-22.data
@@ -0,0 +1,4 @@
+strip: |-
+  text
clip: |
+  text…keep: |+
+  text

\ No newline at end of file
diff --git a/src/test/resources/pyyaml/spec-09-23.canonical b/src/test/resources/pyyaml/spec-09-23.canonical
new file mode 100644
index 0000000..c4444ca
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-23.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "strip"
+  : !!str "# text",
+  ? !!str "clip"
+  : !!str "# text\n",
+  ? !!str "keep"
+  : !!str "# text\L\n",
+}
diff --git a/src/test/resources/pyyaml/spec-09-23.data b/src/test/resources/pyyaml/spec-09-23.data
new file mode 100644
index 0000000..8972d2b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-23.data
@@ -0,0 +1,11 @@
+ # Strip
+  # Comments:
+strip: |-
+  # text
  
 # Clip
+  # comments:
+…clip: |
+  # text… 
 # Keep
+  # comments:
+…keep: |+
+  # text
… # Trail
+  # comments.
diff --git a/src/test/resources/pyyaml/spec-09-24.canonical b/src/test/resources/pyyaml/spec-09-24.canonical
new file mode 100644
index 0000000..45a99b0
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-24.canonical
@@ -0,0 +1,10 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "strip"
+  : !!str "",
+  ? !!str "clip"
+  : !!str "",
+  ? !!str "keep"
+  : !!str "\n",
+}
diff --git a/src/test/resources/pyyaml/spec-09-24.data b/src/test/resources/pyyaml/spec-09-24.data
new file mode 100644
index 0000000..de0b64b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-24.data
@@ -0,0 +1,6 @@
+strip: >-
+
+clip: >
+
+keep: |+
+
diff --git a/src/test/resources/pyyaml/spec-09-25.canonical b/src/test/resources/pyyaml/spec-09-25.canonical
new file mode 100644
index 0000000..9d2327b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-25.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "literal\n\
+      \ttext\n"
diff --git a/src/test/resources/pyyaml/spec-09-25.data b/src/test/resources/pyyaml/spec-09-25.data
new file mode 100644
index 0000000..f6303a1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-25.data
@@ -0,0 +1,3 @@
+| # Simple block scalar
+ literal
+ 	text
diff --git a/src/test/resources/pyyaml/spec-09-26.canonical b/src/test/resources/pyyaml/spec-09-26.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-26.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/src/test/resources/pyyaml/spec-09-26.data b/src/test/resources/pyyaml/spec-09-26.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-26.data
@@ -0,0 +1,8 @@
+|
+ 
+  
+  literal
+ 
+  text
+
+ # Comment
diff --git a/src/test/resources/pyyaml/spec-09-27.canonical b/src/test/resources/pyyaml/spec-09-27.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-27.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/src/test/resources/pyyaml/spec-09-27.data b/src/test/resources/pyyaml/spec-09-27.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-27.data
@@ -0,0 +1,8 @@
+|
+ 
+  
+  literal
+ 
+  text
+
+ # Comment
diff --git a/src/test/resources/pyyaml/spec-09-28.canonical b/src/test/resources/pyyaml/spec-09-28.canonical
new file mode 100644
index 0000000..3029a11
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-28.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+!!str "\n\nliteral\n\ntext\n"
diff --git a/src/test/resources/pyyaml/spec-09-28.data b/src/test/resources/pyyaml/spec-09-28.data
new file mode 100644
index 0000000..f28555a
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-28.data
@@ -0,0 +1,8 @@
+|
+ 
+  
+  literal
+ 
+  text
+
+ # Comment
diff --git a/src/test/resources/pyyaml/spec-09-29.canonical b/src/test/resources/pyyaml/spec-09-29.canonical
new file mode 100644
index 0000000..0980789
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-29.canonical
@@ -0,0 +1,4 @@
+%YAML 1.1
+---
+!!str "folded text\n\
+      \tlines\n"
diff --git a/src/test/resources/pyyaml/spec-09-29.data b/src/test/resources/pyyaml/spec-09-29.data
new file mode 100644
index 0000000..82e611f
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-29.data
@@ -0,0 +1,4 @@
+> # Simple folded scalar
+ folded
+ text
+ 	lines
diff --git a/src/test/resources/pyyaml/spec-09-30.canonical b/src/test/resources/pyyaml/spec-09-30.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-30.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+      next line\n\n\
+      \  * bullet\n\
+      \  * list\n\n\
+      last line\n"
diff --git a/src/test/resources/pyyaml/spec-09-30.data b/src/test/resources/pyyaml/spec-09-30.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-30.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+   * bullet
+   * list
+
+ last
+ line
+
+# Comment
diff --git a/src/test/resources/pyyaml/spec-09-31.canonical b/src/test/resources/pyyaml/spec-09-31.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-31.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+      next line\n\n\
+      \  * bullet\n\
+      \  * list\n\n\
+      last line\n"
diff --git a/src/test/resources/pyyaml/spec-09-31.data b/src/test/resources/pyyaml/spec-09-31.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-31.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+   * bullet
+   * list
+
+ last
+ line
+
+# Comment
diff --git a/src/test/resources/pyyaml/spec-09-32.canonical b/src/test/resources/pyyaml/spec-09-32.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-32.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+      next line\n\n\
+      \  * bullet\n\
+      \  * list\n\n\
+      last line\n"
diff --git a/src/test/resources/pyyaml/spec-09-32.data b/src/test/resources/pyyaml/spec-09-32.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-32.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+   * bullet
+   * list
+
+ last
+ line
+
+# Comment
diff --git a/src/test/resources/pyyaml/spec-09-33.canonical b/src/test/resources/pyyaml/spec-09-33.canonical
new file mode 100644
index 0000000..fc37db1
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-33.canonical
@@ -0,0 +1,7 @@
+%YAML 1.1
+---
+!!str "folded line\n\
+      next line\n\n\
+      \  * bullet\n\
+      \  * list\n\n\
+      last line\n"
diff --git a/src/test/resources/pyyaml/spec-09-33.data b/src/test/resources/pyyaml/spec-09-33.data
new file mode 100644
index 0000000..a4d8c36
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-09-33.data
@@ -0,0 +1,14 @@
+>
+ folded
+ line
+
+ next
+ line
+
+   * bullet
+   * list
+
+ last
+ line
+
+# Comment
diff --git a/src/test/resources/pyyaml/spec-10-01.canonical b/src/test/resources/pyyaml/spec-10-01.canonical
new file mode 100644
index 0000000..d08cdd4
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-01.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!seq [
+  !!seq [
+    !!str "inner",
+    !!str "inner",
+  ],
+  !!seq [
+    !!str "inner",
+    !!str "last",
+  ],
+]
diff --git a/src/test/resources/pyyaml/spec-10-01.data b/src/test/resources/pyyaml/spec-10-01.data
new file mode 100644
index 0000000..e668d38
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-01.data
@@ -0,0 +1,2 @@
+- [ inner, inner, ]
+- [inner,last]
diff --git a/src/test/resources/pyyaml/spec-10-02.canonical b/src/test/resources/pyyaml/spec-10-02.canonical
new file mode 100644
index 0000000..82fe0d9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-02.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!seq [
+  !!str "double quoted",
+  !!str "single quoted",
+  !!str "plain text",
+  !!seq [
+    !!str "nested",
+  ],
+  !!map {
+    ? !!str "single"
+    : !!str "pair"
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-10-02.data b/src/test/resources/pyyaml/spec-10-02.data
new file mode 100644
index 0000000..3b23351
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-02.data
@@ -0,0 +1,8 @@
+[
+"double
+ quoted", 'single
+           quoted',
+plain
+ text, [ nested ],
+single: pair ,
+]
diff --git a/src/test/resources/pyyaml/spec-10-03.canonical b/src/test/resources/pyyaml/spec-10-03.canonical
new file mode 100644
index 0000000..1443395
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-03.canonical
@@ -0,0 +1,12 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "block"
+  : !!seq [
+    !!str "one",
+    !!map {
+      ? !!str "two"
+      : !!str "three"
+    }
+  ]
+}
diff --git a/src/test/resources/pyyaml/spec-10-03.data b/src/test/resources/pyyaml/spec-10-03.data
new file mode 100644
index 0000000..9e15f83
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-03.data
@@ -0,0 +1,4 @@
+block: # Block
+       # sequence
+- one
+- two : three
diff --git a/src/test/resources/pyyaml/spec-10-04.canonical b/src/test/resources/pyyaml/spec-10-04.canonical
new file mode 100644
index 0000000..ae486a3
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-04.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "block"
+  : !!seq [
+    !!str "one",
+    !!seq [
+      !!str "two"
+    ]
+  ]
+}
diff --git a/src/test/resources/pyyaml/spec-10-04.data b/src/test/resources/pyyaml/spec-10-04.data
new file mode 100644
index 0000000..2905b0d
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-04.data
@@ -0,0 +1,4 @@
+block:
+- one
+-
+ - two
diff --git a/src/test/resources/pyyaml/spec-10-05.canonical b/src/test/resources/pyyaml/spec-10-05.canonical
new file mode 100644
index 0000000..07cc0c9
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-05.canonical
@@ -0,0 +1,14 @@
+%YAML 1.1
+---
+!!seq [
+  !!null "",
+  !!str "block node\n",
+  !!seq [
+    !!str "one",
+    !!str "two",
+  ],
+  !!map {
+    ? !!str "one"
+    : !!str "two",
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-10-05.data b/src/test/resources/pyyaml/spec-10-05.data
new file mode 100644
index 0000000..f19a99e
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-05.data
@@ -0,0 +1,7 @@
+- # Empty
+- |
+ block node
+- - one # in-line
+  - two # sequence
+- one: two # in-line
+           # mapping
diff --git a/src/test/resources/pyyaml/spec-10-06.canonical b/src/test/resources/pyyaml/spec-10-06.canonical
new file mode 100644
index 0000000..d9986c2
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-06.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!seq [
+  !!map {
+    ? !!str "inner"
+    : !!str "entry",
+    ? !!str "also"
+    : !!str "inner"
+  },
+  !!map {
+    ? !!str "inner"
+    : !!str "entry",
+    ? !!str "last"
+    : !!str "entry"
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-10-06.data b/src/test/resources/pyyaml/spec-10-06.data
new file mode 100644
index 0000000..860ba25
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-06.data
@@ -0,0 +1,2 @@
+- { inner : entry , also: inner , }
+- {inner: entry,last : entry}
diff --git a/src/test/resources/pyyaml/spec-10-07.canonical b/src/test/resources/pyyaml/spec-10-07.canonical
new file mode 100644
index 0000000..ec74230
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-07.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+  ? !!null ""
+  : !!str "value",
+  ? !!str "explicit key"
+  : !!str "value",
+  ? !!str "simple key"
+  : !!str "value",
+  ? !!seq [
+    !!str "collection",
+    !!str "simple",
+    !!str "key"
+  ]
+  : !!str "value"
+}
diff --git a/src/test/resources/pyyaml/spec-10-07.data b/src/test/resources/pyyaml/spec-10-07.data
new file mode 100644
index 0000000..ff943fb
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-07.data
@@ -0,0 +1,7 @@
+{
+? : value, # Empty key
+? explicit
+ key: value,
+simple key : value,
+[ collection, simple, key ]: value
+}
diff --git a/src/test/resources/pyyaml/spec-10-08.data b/src/test/resources/pyyaml/spec-10-08.data
new file mode 100644
index 0000000..55bd788
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-08.data
@@ -0,0 +1,5 @@
+{
+multi-line
+ simple key : value,
+very long ...................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................(>1KB)................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... key: value
+}
diff --git a/src/test/resources/pyyaml/spec-10-09.canonical b/src/test/resources/pyyaml/spec-10-09.canonical
new file mode 100644
index 0000000..4d9827b
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-09.canonical
@@ -0,0 +1,8 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "key"
+  : !!str "value",
+  ? !!str "empty"
+  : !!null "",
+}
diff --git a/src/test/resources/pyyaml/spec-10-09.data b/src/test/resources/pyyaml/spec-10-09.data
new file mode 100644
index 0000000..4d55e21
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-09.data
@@ -0,0 +1,4 @@
+{
+key : value,
+empty: # empty value↓
+}
diff --git a/src/test/resources/pyyaml/spec-10-10.canonical b/src/test/resources/pyyaml/spec-10-10.canonical
new file mode 100644
index 0000000..016fb64
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-10.canonical
@@ -0,0 +1,16 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "explicit key1"
+  : !!str "explicit value",
+  ? !!str "explicit key2"
+  : !!null "",
+  ? !!str "explicit key3"
+  : !!null "",
+  ? !!str "simple key1"
+  : !!str "explicit value",
+  ? !!str "simple key2"
+  : !!null "",
+  ? !!str "simple key3"
+  : !!null "",
+}
diff --git a/src/test/resources/pyyaml/spec-10-10.data b/src/test/resources/pyyaml/spec-10-10.data
new file mode 100644
index 0000000..0888b05
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-10.data
@@ -0,0 +1,8 @@
+{
+? explicit key1 : explicit value,
+? explicit key2 : , # Explicit empty
+? explicit key3,     # Empty value
+simple key1 : explicit value,
+simple key2 : ,     # Explicit empty
+simple key3,         # Empty value
+}
diff --git a/src/test/resources/pyyaml/spec-10-11.canonical b/src/test/resources/pyyaml/spec-10-11.canonical
new file mode 100644
index 0000000..7309544
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-11.canonical
@@ -0,0 +1,24 @@
+%YAML 1.1
+---
+!!seq [
+  !!map {
+    ? !!str "explicit key1"
+    : !!str "explicit value",
+  },
+  !!map {
+    ? !!str "explicit key2"
+    : !!null "",
+  },
+  !!map {
+    ? !!str "explicit key3"
+    : !!null "",
+  },
+  !!map {
+    ? !!str "simple key1"
+    : !!str "explicit value",
+  },
+  !!map {
+    ? !!str "simple key2"
+    : !!null "",
+  },
+]
diff --git a/src/test/resources/pyyaml/spec-10-11.data b/src/test/resources/pyyaml/spec-10-11.data
new file mode 100644
index 0000000..9f05568
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-11.data
@@ -0,0 +1,7 @@
+[
+? explicit key1 : explicit value,
+? explicit key2 : , # Explicit empty
+? explicit key3,     # Implicit empty
+simple key1 : explicit value,
+simple key2 : ,     # Explicit empty
+]
diff --git a/src/test/resources/pyyaml/spec-10-12.canonical b/src/test/resources/pyyaml/spec-10-12.canonical
new file mode 100644
index 0000000..a95dd40
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-12.canonical
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "block"
+  : !!map {
+    ? !!str "key"
+    : !!str "value"
+  }
+}
diff --git a/src/test/resources/pyyaml/spec-10-12.data b/src/test/resources/pyyaml/spec-10-12.data
new file mode 100644
index 0000000..5521443
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-12.data
@@ -0,0 +1,3 @@
+block: # Block
+    # mapping
+ key: value
diff --git a/src/test/resources/pyyaml/spec-10-13.canonical b/src/test/resources/pyyaml/spec-10-13.canonical
new file mode 100644
index 0000000..e183c50
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-13.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "explicit key"
+  : !!null "",
+  ? !!str "block key\n"
+  : !!seq [
+    !!str "one",
+    !!str "two",
+  ]
+}
diff --git a/src/test/resources/pyyaml/spec-10-13.data b/src/test/resources/pyyaml/spec-10-13.data
new file mode 100644
index 0000000..b5b97db
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-13.data
@@ -0,0 +1,5 @@
+? explicit key # implicit value
+? |
+  block key
+: - one # explicit in-line
+  - two # block value
diff --git a/src/test/resources/pyyaml/spec-10-14.canonical b/src/test/resources/pyyaml/spec-10-14.canonical
new file mode 100644
index 0000000..e87c880
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-14.canonical
@@ -0,0 +1,11 @@
+%YAML 1.1
+---
+!!map {
+  ? !!str "plain key"
+  : !!null "",
+  ? !!str "quoted key"
+  : !!seq [
+    !!str "one",
+    !!str "two",
+  ]
+}
diff --git a/src/test/resources/pyyaml/spec-10-14.data b/src/test/resources/pyyaml/spec-10-14.data
new file mode 100644
index 0000000..7f5995c
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-14.data
@@ -0,0 +1,4 @@
+plain key: # empty value
+"quoted key":
+- one # explicit next-line
+- two # block value
diff --git a/src/test/resources/pyyaml/spec-10-15.canonical b/src/test/resources/pyyaml/spec-10-15.canonical
new file mode 100644
index 0000000..85fbbd0
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-15.canonical
@@ -0,0 +1,18 @@
+%YAML 1.1
+---
+!!seq [
+  !!map {
+    ? !!str "sun"
+    : !!str "yellow"
+  },
+  !!map {
+    ? !!map {
+      ? !!str "earth"
+      : !!str "blue"
+    }
+    : !!map {
+      ? !!str "moon"
+      : !!str "white"
+    }
+  }
+]
diff --git a/src/test/resources/pyyaml/spec-10-15.data b/src/test/resources/pyyaml/spec-10-15.data
new file mode 100644
index 0000000..d675cfd
--- /dev/null
+++ b/src/test/resources/pyyaml/spec-10-15.data
@@ -0,0 +1,3 @@
+- sun: yellow
+- ? earth: blue
+  : moon: white
diff --git a/src/test/resources/pyyaml/str.data b/src/test/resources/pyyaml/str.data
new file mode 100644
index 0000000..7cbdb7c
--- /dev/null
+++ b/src/test/resources/pyyaml/str.data
@@ -0,0 +1 @@
+- abcd
diff --git a/src/test/resources/pyyaml/tab-in-scalar.canonical b/src/test/resources/pyyaml/tab-in-scalar.canonical
new file mode 100644
index 0000000..d5bc00d
--- /dev/null
+++ b/src/test/resources/pyyaml/tab-in-scalar.canonical
@@ -0,0 +1,3 @@
+%YAML 1.1
+---
+"L\tS"
\ No newline at end of file
diff --git a/src/test/resources/pyyaml/tab-in-scalar.data b/src/test/resources/pyyaml/tab-in-scalar.data
new file mode 100644
index 0000000..13a6322
--- /dev/null
+++ b/src/test/resources/pyyaml/tab-in-scalar.data
@@ -0,0 +1 @@
+L	S
diff --git a/src/test/resources/pyyaml/tags.events b/src/test/resources/pyyaml/tags.events
new file mode 100644
index 0000000..bb93dce
--- /dev/null
+++ b/src/test/resources/pyyaml/tags.events
@@ -0,0 +1,12 @@
+- !StreamStart
+- !DocumentStart
+- !SequenceStart
+- !Scalar { value: 'data' }
+#- !Scalar { tag: '!', value: 'data' }
+- !Scalar { tag: 'tag:yaml.org,2002:str', value: 'data' }
+- !Scalar { tag: '!myfunnytag', value: 'data' }
+- !Scalar { tag: '!my!ugly!tag', value: 'data' }
+- !Scalar { tag: 'tag:my.domain.org,2002:data!? #', value: 'data' }
+- !SequenceEnd
+- !DocumentEnd
+- !StreamEnd
diff --git a/src/test/resources/pyyaml/test_mark.marks b/src/test/resources/pyyaml/test_mark.marks
new file mode 100644
index 0000000..cf257b4
--- /dev/null
+++ b/src/test/resources/pyyaml/test_mark.marks
@@ -0,0 +1,38 @@
+---

+*The first line.

+The last line.

+---

+The first*line.

+The last line.

+---

+The first line.*

+The last line.

+---

+The first line.

+*The last line.

+---

+The first line.

+The last*line.

+---

+The first line.

+The last line.*

+---

+The first line.

+*The selected line.

+The last line.

+---

+The first line.

+The selected*line.

+The last line.

+---

+The first line.

+The selected line.*

+The last line.

+---

+*The only line.

+---

+The only*line.

+---

+The only line.*

+---

+Loooooooooooooooooooooooooooooooooooooooooooooong*Liiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiine

diff --git a/src/test/resources/pyyaml/timestamp-bugs.data b/src/test/resources/pyyaml/timestamp-bugs.data
new file mode 100644
index 0000000..721d290
--- /dev/null
+++ b/src/test/resources/pyyaml/timestamp-bugs.data
@@ -0,0 +1,6 @@
+- 2001-12-14 21:59:43.10 -5:30
+- 2001-12-14 21:59:43.10 +5:30
+- 2001-12-14 21:59:43.00101
+- 2001-12-14 21:59:43+1
+- 2001-12-14 21:59:43-1:30
+- 2005-07-08 17:35:04.517600
diff --git a/src/test/resources/pyyaml/timestamp.data b/src/test/resources/pyyaml/timestamp.data
new file mode 100644
index 0000000..7d214ce
--- /dev/null
+++ b/src/test/resources/pyyaml/timestamp.data
@@ -0,0 +1,5 @@
+- 2001-12-15T02:59:43.1Z
+- 2001-12-14t21:59:43.10-05:00
+- 2001-12-14 21:59:43.10 -5
+- 2001-12-15 2:59:43.10
+- 2002-12-14
diff --git a/src/test/resources/pyyaml/unacceptable-key.loader-error b/src/test/resources/pyyaml/unacceptable-key.loader-error
new file mode 100644
index 0000000..d748e37
--- /dev/null
+++ b/src/test/resources/pyyaml/unacceptable-key.loader-error
@@ -0,0 +1,4 @@
+---
+? - foo
+  - bar
+: baz
diff --git a/src/test/resources/pyyaml/unclosed-bracket.loader-error b/src/test/resources/pyyaml/unclosed-bracket.loader-error
new file mode 100644
index 0000000..8c82077
--- /dev/null
+++ b/src/test/resources/pyyaml/unclosed-bracket.loader-error
@@ -0,0 +1,6 @@
+test:
+    - [ foo: bar
+# comment the rest of the stream to let the scanner detect the problem.
+#    - baz
+#"we could have detected the unclosed bracket on the above line, but this would forbid such syntax as": {
+#}
diff --git a/src/test/resources/pyyaml/unclosed-quoted-scalar.loader-error b/src/test/resources/pyyaml/unclosed-quoted-scalar.loader-error
new file mode 100644
index 0000000..8537429
--- /dev/null
+++ b/src/test/resources/pyyaml/unclosed-quoted-scalar.loader-error
@@ -0,0 +1,2 @@
+'foo
+ bar
diff --git a/src/test/resources/pyyaml/undefined-anchor.loader-error b/src/test/resources/pyyaml/undefined-anchor.loader-error
new file mode 100644
index 0000000..9469103
--- /dev/null
+++ b/src/test/resources/pyyaml/undefined-anchor.loader-error
@@ -0,0 +1,3 @@
+- foo
+- &bar baz
+- *bat
diff --git a/src/test/resources/pyyaml/undefined-constructor.loader-error b/src/test/resources/pyyaml/undefined-constructor.loader-error
new file mode 100644
index 0000000..9a37ccc
--- /dev/null
+++ b/src/test/resources/pyyaml/undefined-constructor.loader-error
@@ -0,0 +1 @@
+--- !foo bar
diff --git a/src/test/resources/pyyaml/undefined-tag-handle.loader-error b/src/test/resources/pyyaml/undefined-tag-handle.loader-error
new file mode 100644
index 0000000..82ba335
--- /dev/null
+++ b/src/test/resources/pyyaml/undefined-tag-handle.loader-error
@@ -0,0 +1 @@
+--- !foo!bar    baz
diff --git a/src/test/resources/pyyaml/value.data b/src/test/resources/pyyaml/value.data
new file mode 100644
index 0000000..c5b7680
--- /dev/null
+++ b/src/test/resources/pyyaml/value.data
@@ -0,0 +1 @@
+- =
diff --git a/src/test/resources/pyyaml/yaml.data b/src/test/resources/pyyaml/yaml.data
new file mode 100644
index 0000000..a4bb3f8
--- /dev/null
+++ b/src/test/resources/pyyaml/yaml.data
@@ -0,0 +1,3 @@
+- !!yaml '!'
+- !!yaml '&'
+- !!yaml '*'
diff --git a/src/test/resources/reader/large.yaml b/src/test/resources/reader/large.yaml
new file mode 100644
index 0000000..12d5495
--- /dev/null
+++ b/src/test/resources/reader/large.yaml
@@ -0,0 +1,182 @@
+- !!map
+  id: id0
+  list: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
+  map: {'3': 3, '2': 2, '1': 1, '0': 0, '7': 7, '6': 6, '5': 5, '4': 4, '9': 9, '8': 8}
+- !!map
+  id: id1
+  list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
+  map: {'3': 4, '2': 3, '1': 2, '0': 1, '7': 8, '6': 7, '5': 6, '4': 5, '9': 10, '8': 9}
+- !!map
+  id: id2
+  list: ['2', '3', '4', '5', '6', '7', '8', '9', '10', '11']
+  map: {'3': 5, '2': 4, '1': 3, '0': 2, '7': 9, '6': 8, '5': 7, '4': 6, '9': 11, '8': 10}
+- !!map
+  id: id3
+  list: ['3', '4', '5', '6', '7', '8', '9', '10', '11', '12']
+  map: {'3': 6, '2': 5, '1': 4, '0': 3, '7': 10, '6': 9, '5': 8, '4': 7, '9': 12,
+    '8': 11}
+- !!map
+  id: id4
+  list: ['4', '5', '6', '7', '8', '9', '10', '11', '12', '13']
+  map: {'3': 7, '2': 6, '1': 5, '0': 4, '7': 11, '6': 10, '5': 9, '4': 8, '9': 13,
+    '8': 12}
+- !!map
+  id: id5
+  list: ['5', '6', '7', '8', '9', '10', '11', '12', '13', '14']
+  map: {'3': 8, '2': 7, '1': 6, '0': 5, '7': 12, '6': 11, '5': 10, '4': 9, '9': 14,
+    '8': 13}
+- !!map
+  id: id6
+  list: ['6', '7', '8', '9', '10', '11', '12', '13', '14', '15']
+  map: {'3': 9, '2': 8, '1': 7, '0': 6, '7': 13, '6': 12, '5': 11, '4': 10, '9': 15,
+    '8': 14}
+- !!map
+  id: id7
+  list: ['7', '8', '9', '10', '11', '12', '13', '14', '15', '16']
+  map: {'3': 10, '2': 9, '1': 8, '0': 7, '7': 14, '6': 13, '5': 12, '4': 11, '9': 16,
+    '8': 15}
+- !!map
+  id: id8
+  list: ['8', '9', '10', '11', '12', '13', '14', '15', '16', '17']
+  map: {'3': 11, '2': 10, '1': 9, '0': 8, '7': 15, '6': 14, '5': 13, '4': 12, '9': 17,
+    '8': 16}
+- !!map
+  id: id9
+  list: ['9', '10', '11', '12', '13', '14', '15', '16', '17', '18']
+  map: {'3': 12, '2': 11, '1': 10, '0': 9, '7': 16, '6': 15, '5': 14, '4': 13, '9': 18,
+    '8': 17}
+- !!map
+  id: id10
+  list: ['10', '11', '12', '13', '14', '15', '16', '17', '18', '19']
+  map: {'3': 13, '2': 12, '1': 11, '0': 10, '7': 17, '6': 16, '5': 15, '4': 14, '9': 19,
+    '8': 18}
+- !!map
+  id: id11
+  list: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20']
+  map: {'3': 14, '2': 13, '1': 12, '0': 11, '7': 18, '6': 17, '5': 16, '4': 15, '9': 20,
+    '8': 19}
+- !!map
+  id: id12
+  list: ['12', '13', '14', '15', '16', '17', '18', '19', '20', '21']
+  map: {'3': 15, '2': 14, '1': 13, '0': 12, '7': 19, '6': 18, '5': 17, '4': 16, '9': 21,
+    '8': 20}
+- !!map
+  id: id13
+  list: ['13', '14', '15', '16', '17', '18', '19', '20', '21', '22']
+  map: {'3': 16, '2': 15, '1': 14, '0': 13, '7': 20, '6': 19, '5': 18, '4': 17, '9': 22,
+    '8': 21}
+- !!map
+  id: id14
+  list: ['14', '15', '16', '17', '18', '19', '20', '21', '22', '23']
+  map: {'3': 17, '2': 16, '1': 15, '0': 14, '7': 21, '6': 20, '5': 19, '4': 18, '9': 23,
+    '8': 22}
+- !!map
+  id: id15
+  list: ['15', '16', '17', '18', '19', '20', '21', '22', '23', '24']
+  map: {'3': 18, '2': 17, '1': 16, '0': 15, '7': 22, '6': 21, '5': 20, '4': 19, '9': 24,
+    '8': 23}
+- !!map
+  id: id16
+  list: ['16', '17', '18', '19', '20', '21', '22', '23', '24', '25']
+  map: {'3': 19, '2': 18, '1': 17, '0': 16, '7': 23, '6': 22, '5': 21, '4': 20, '9': 25,
+    '8': 24}
+- !!map
+  id: id17
+  list: ['17', '18', '19', '20', '21', '22', '23', '24', '25', '26']
+  map: {'3': 20, '2': 19, '1': 18, '0': 17, '7': 24, '6': 23, '5': 22, '4': 21, '9': 26,
+    '8': 25}
+- !!map
+  id: id18
+  list: ['18', '19', '20', '21', '22', '23', '24', '25', '26', '27']
+  map: {'3': 21, '2': 20, '1': 19, '0': 18, '7': 25, '6': 24, '5': 23, '4': 22, '9': 27,
+    '8': 26}
+- !!map
+  id: id19
+  list: ['19', '20', '21', '22', '23', '24', '25', '26', '27', '28']
+  map: {'3': 22, '2': 21, '1': 20, '0': 19, '7': 26, '6': 25, '5': 24, '4': 23, '9': 28,
+    '8': 27}
+- !!map
+  id: id20
+  list: ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29']
+  map: {'3': 23, '2': 22, '1': 21, '0': 20, '7': 27, '6': 26, '5': 25, '4': 24, '9': 29,
+    '8': 28}
+- !!map
+  id: id21
+  list: ['21', '22', '23', '24', '25', '26', '27', '28', '29', '30']
+  map: {'3': 24, '2': 23, '1': 22, '0': 21, '7': 28, '6': 27, '5': 26, '4': 25, '9': 30,
+    '8': 29}
+- !!map
+  id: id22
+  list: ['22', '23', '24', '25', '26', '27', '28', '29', '30', '31']
+  map: {'3': 25, '2': 24, '1': 23, '0': 22, '7': 29, '6': 28, '5': 27, '4': 26, '9': 31,
+    '8': 30}
+- !!map
+  id: id23
+  list: ['23', '24', '25', '26', '27', '28', '29', '30', '31', '32']
+  map: {'3': 26, '2': 25, '1': 24, '0': 23, '7': 30, '6': 29, '5': 28, '4': 27, '9': 32,
+    '8': 31}
+- !!map
+  id: id24
+  list: ['24', '25', '26', '27', '28', '29', '30', '31', '32', '33']
+  map: {'3': 27, '2': 26, '1': 25, '0': 24, '7': 31, '6': 30, '5': 29, '4': 28, '9': 33,
+    '8': 32}
+- !!map
+  id: id25
+  list: ['25', '26', '27', '28', '29', '30', '31', '32', '33', '34']
+  map: {'3': 28, '2': 27, '1': 26, '0': 25, '7': 32, '6': 31, '5': 30, '4': 29, '9': 34,
+    '8': 33}
+- !!map
+  id: id26
+  list: ['26', '27', '28', '29', '30', '31', '32', '33', '34', '35']
+  map: {'3': 29, '2': 28, '1': 27, '0': 26, '7': 33, '6': 32, '5': 31, '4': 30, '9': 35,
+    '8': 34}
+- !!map
+  id: id27
+  list: ['27', '28', '29', '30', '31', '32', '33', '34', '35', '36']
+  map: {'3': 30, '2': 29, '1': 28, '0': 27, '7': 34, '6': 33, '5': 32, '4': 31, '9': 36,
+    '8': 35}
+- !!map
+  id: id28
+  list: ['28', '29', '30', '31', '32', '33', '34', '35', '36', '37']
+  map: {'3': 31, '2': 30, '1': 29, '0': 28, '7': 35, '6': 34, '5': 33, '4': 32, '9': 37,
+    '8': 36}
+- !!map
+  id: id29
+  list: ['29', '30', '31', '32', '33', '34', '35', '36', '37', '38']
+  map: {'3': 32, '2': 31, '1': 30, '0': 29, '7': 36, '6': 35, '5': 34, '4': 33, '9': 38,
+    '8': 37}
+- !!map
+  id: id30
+  list: ['30', '31', '32', '33', '34', '35', '36', '37', '38', '39']
+  map: {'3': 33, '2': 32, '1': 31, '0': 30, '7': 37, '6': 36, '5': 35, '4': 34, '9': 39,
+    '8': 38}
+- !!map
+  id: id31
+  list: ['31', '32', '33', '34', '35', '36', '37', '38', '39', '40']
+  map: {'3': 34, '2': 33, '1': 32, '0': 31, '7': 38, '6': 37, '5': 36, '4': 35, '9': 40,
+    '8': 39}
+- !!map
+  id: id32
+  list: ['32', '33', '34', '35', '36', '37', '38', '39', '40', '41']
+  map: {'3': 35, '2': 34, '1': 33, '0': 32, '7': 39, '6': 38, '5': 37, '4': 36, '9': 41,
+    '8': 40}
+- !!map
+  id: id33
+  list: ['33', '34', '35', '36', '37', '38', '39', '40', '41', '42']
+  map: {'3': 36, '2': 35, '1': 34, '0': 33, '7': 40, '6': 39, '5': 38, '4': 37, '9': 42,
+    '8': 41}
+- !!map
+  id: id34
+  list: ['34', '35', '36', '37', '38', '39', '40', '41', '42', '43']
+  map: {'3': 37, '2': 36, '1': 35, '0': 34, '7': 41, '6': 40, '5': 39, '4': 38, '9': 43,
+    '8': 42}
+- !!map
+  id: id35
+  list: ['35', '36', '37', '38', '39', '40', '41', '42', '43', '44']
+  map: {'3': 38, '2': 37, '1': 36, '0': 35, '7': 42, '6': 41, '5': 40, '4': 39, '9': 44,
+    '8': 43}
+- !!map
+  id: id36
+  list: ['36', '37', '38', '39', '40', '41', '42', '43', '44', '45']
+  map: {'3': 39, '2': 38, '1': 37, '0': 36, '7': 43, '6': 42, '5': 41, '4': 40, '9': 45,
+    '8': 44}
diff --git a/src/test/resources/reader/unicode-16be.txt b/src/test/resources/reader/unicode-16be.txt
new file mode 100644
index 0000000..18e8f25
--- /dev/null
+++ b/src/test/resources/reader/unicode-16be.txt
Binary files differ
diff --git a/src/test/resources/reader/unicode-16le.txt b/src/test/resources/reader/unicode-16le.txt
new file mode 100644
index 0000000..2f84586
--- /dev/null
+++ b/src/test/resources/reader/unicode-16le.txt
Binary files differ
diff --git a/src/test/resources/reader/utf-8.txt b/src/test/resources/reader/utf-8.txt
new file mode 100644
index 0000000..b0540d2
--- /dev/null
+++ b/src/test/resources/reader/utf-8.txt
@@ -0,0 +1 @@
+test
\ No newline at end of file
diff --git a/src/test/resources/recursive/beanring-3.yaml b/src/test/resources/recursive/beanring-3.yaml
new file mode 100644
index 0000000..eb97a4e
--- /dev/null
+++ b/src/test/resources/recursive/beanring-3.yaml
@@ -0,0 +1,25 @@
+&id001 !!org.yaml.snakeyaml.recursive.Human
+bankAccountOwner:
+  bankAccountOwner:
+    bankAccountOwner: *id001
+    birthPlace: null
+    birthday: null
+    children: !!set {}
+    father: null
+    mother: null
+    name: Man 3
+    partner: null
+  birthPlace: null
+  birthday: null
+  children: !!set {}
+  father: null
+  mother: null
+  name: Man 2
+  partner: null
+birthPlace: null
+birthday: null
+children: !!set {}
+father: null
+mother: null
+name: Man 1
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/beanring-3.yaml b/src/test/resources/recursive/generics/beanring-3.yaml
new file mode 100644
index 0000000..56b9207
--- /dev/null
+++ b/src/test/resources/recursive/generics/beanring-3.yaml
@@ -0,0 +1,25 @@
+&id001 !!org.yaml.snakeyaml.recursive.generics.HumanGen
+bankAccountOwner:
+  bankAccountOwner:
+    bankAccountOwner: *id001
+    birthPlace: null
+    birthday: null
+    children: !!set {}
+    father: null
+    mother: null
+    name: Man 3
+    partner: null
+  birthPlace: null
+  birthday: null
+  children: !!set {}
+  father: null
+  mother: null
+  name: Man 2
+  partner: null
+birthPlace: null
+birthday: null
+children: !!set {}
+father: null
+mother: null
+name: Man 1
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/no-children-1.yaml b/src/test/resources/recursive/generics/no-children-1.yaml
new file mode 100644
index 0000000..cef7c7b
--- /dev/null
+++ b/src/test/resources/recursive/generics/no-children-1.yaml
@@ -0,0 +1,17 @@
+&id001 !!org.yaml.snakeyaml.recursive.generics.HumanGen
+bankAccountOwner: *id001
+birthPlace: Leningrad
+birthday: 1970-01-12T13:46:40Z
+children: !!set {}
+father: null
+mother: null
+name: Father
+partner:
+  bankAccountOwner: *id001
+  birthPlace: Saint-Petersburg
+  birthday: 1973-03-03T09:46:40Z
+  children: !!set {}
+  father: null
+  mother: null
+  name: Mother
+  partner: *id001
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/no-children-2.yaml b/src/test/resources/recursive/generics/no-children-2.yaml
new file mode 100644
index 0000000..fd31206
--- /dev/null
+++ b/src/test/resources/recursive/generics/no-children-2.yaml
@@ -0,0 +1,17 @@
+&id001
+bankAccountOwner: *id001
+birthPlace: Leningrad
+birthday: 1970-01-12T13:46:40Z
+children: !!set {}
+father: null
+mother: null
+name: Father
+partner:
+  bankAccountOwner: *id001
+  birthPlace: Saint-Petersburg
+  birthday: 1973-03-03T09:46:40Z
+  children: !!set {}
+  father: null
+  mother: null
+  name: Mother
+  partner: *id001
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-2.yaml b/src/test/resources/recursive/generics/with-children-2.yaml
new file mode 100644
index 0000000..dcc9144
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-2.yaml
@@ -0,0 +1,35 @@
+&id002
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+    *id002: son
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        partner: *id001
+      name: Daughter
+      partner: null
+    : daughter
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: {}
+father: *id001
+mother: *id004
+name: Son
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-3.yaml b/src/test/resources/recursive/generics/with-children-3.yaml
new file mode 100644
index 0000000..0172893
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-3.yaml
@@ -0,0 +1,35 @@
+&id002 !!org.yaml.snakeyaml.recursive.generics.HumanGen3
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - !!org.yaml.snakeyaml.recursive.generics.HumanGen3
+    bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+    name: Daughter
+    partner: null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+partner: null
diff --git a/src/test/resources/recursive/generics/with-children-as-list.yaml b/src/test/resources/recursive/generics/with-children-as-list.yaml
new file mode 100644
index 0000000..54558ba
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-list.yaml
@@ -0,0 +1,35 @@
+&id002
+- !!org.yaml.snakeyaml.recursive.generics.HumanGen3
+  bankAccountOwner: &id001
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+- !!org.yaml.snakeyaml.recursive.generics.HumanGen3
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
diff --git a/src/test/resources/recursive/generics/with-children-as-map.yaml b/src/test/resources/recursive/generics/with-children-as-map.yaml
new file mode 100644
index 0000000..538abea
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-map.yaml
@@ -0,0 +1,37 @@
+&id002 !!java.util.LinkedHashMap
+? !!org.yaml.snakeyaml.recursive.generics.HumanGen2
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.HumanGen2
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003 !!org.yaml.snakeyaml.recursive.generics.HumanGen2
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+: This is My Son
+? !!org.yaml.snakeyaml.recursive.generics.HumanGen2
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
+: This Is My Daughter
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children-as-set.yaml b/src/test/resources/recursive/generics/with-children-as-set.yaml
new file mode 100644
index 0000000..5f0db80
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children-as-set.yaml
@@ -0,0 +1,37 @@
+&id002 !!set
+? !!org.yaml.snakeyaml.recursive.generics.HumanGen
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.generics.HumanGen
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003 !!org.yaml.snakeyaml.recursive.generics.HumanGen
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!set {}
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+: null
+? !!org.yaml.snakeyaml.recursive.generics.HumanGen
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!set {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
+: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/generics/with-children.yaml b/src/test/resources/recursive/generics/with-children.yaml
new file mode 100644
index 0000000..fd3e976
--- /dev/null
+++ b/src/test/resources/recursive/generics/with-children.yaml
@@ -0,0 +1,36 @@
+&id002 !!org.yaml.snakeyaml.recursive.generics.HumanGen
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003 !!set
+    *id002: null
+    ? !!org.yaml.snakeyaml.recursive.generics.HumanGen
+      bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: !!set {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        partner: *id001
+      name: Daughter
+      partner: null
+    : null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: !!set {}
+father: *id001
+mother: *id004
+name: Son
+partner: null
diff --git a/src/test/resources/recursive/no-children-1-pretty.yaml b/src/test/resources/recursive/no-children-1-pretty.yaml
new file mode 100644
index 0000000..5a6e4c3
--- /dev/null
+++ b/src/test/resources/recursive/no-children-1-pretty.yaml
@@ -0,0 +1,21 @@
+&id001 !!org.yaml.snakeyaml.recursive.Human {
+  bankAccountOwner: *id001,
+  birthPlace: Leningrad,
+  birthday: !!timestamp '1970-01-12T13:46:40Z',
+  children: !!set {
+    },
+  father: null,
+  mother: null,
+  name: Father,
+  partner: {
+    bankAccountOwner: *id001,
+    birthPlace: Saint-Petersburg,
+    birthday: !!timestamp '1973-03-03T09:46:40Z',
+    children: !!set {
+      },
+    father: null,
+    mother: null,
+    name: Mother,
+    partner: *id001
+  }
+}
diff --git a/src/test/resources/recursive/no-children-1.yaml b/src/test/resources/recursive/no-children-1.yaml
new file mode 100644
index 0000000..8f80490
--- /dev/null
+++ b/src/test/resources/recursive/no-children-1.yaml
@@ -0,0 +1,17 @@
+&id001 !!org.yaml.snakeyaml.recursive.Human
+bankAccountOwner: *id001
+birthPlace: Leningrad
+birthday: 1970-01-12T13:46:40Z
+children: !!set {}
+father: null
+mother: null
+name: Father
+partner:
+  bankAccountOwner: *id001
+  birthPlace: Saint-Petersburg
+  birthday: 1973-03-03T09:46:40Z
+  children: !!set {}
+  father: null
+  mother: null
+  name: Mother
+  partner: *id001
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-2.yaml b/src/test/resources/recursive/with-children-2.yaml
new file mode 100644
index 0000000..e319e53
--- /dev/null
+++ b/src/test/resources/recursive/with-children-2.yaml
@@ -0,0 +1,35 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human2
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+    *id002: son
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        partner: *id001
+      name: Daughter
+      partner: null
+    : daughter
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: {}
+father: *id001
+mother: *id004
+name: Son
+partner: null
diff --git a/src/test/resources/recursive/with-children-3.yaml b/src/test/resources/recursive/with-children-3.yaml
new file mode 100644
index 0000000..d3e0b37
--- /dev/null
+++ b/src/test/resources/recursive/with-children-3.yaml
@@ -0,0 +1,34 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human3
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+    name: Daughter
+    partner: null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+partner: null
diff --git a/src/test/resources/recursive/with-children-as-list.yaml b/src/test/resources/recursive/with-children-as-list.yaml
new file mode 100644
index 0000000..b7a1874
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-list.yaml
@@ -0,0 +1,35 @@
+&id002
+- !!org.yaml.snakeyaml.recursive.Human3
+  bankAccountOwner: &id001
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+- !!org.yaml.snakeyaml.recursive.Human3
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: []
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
diff --git a/src/test/resources/recursive/with-children-as-map.yaml b/src/test/resources/recursive/with-children-as-map.yaml
new file mode 100644
index 0000000..b09fc4a
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-map.yaml
@@ -0,0 +1,37 @@
+&id002 !!java.util.LinkedHashMap
+? !!org.yaml.snakeyaml.recursive.Human2
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.Human2
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003 !!org.yaml.snakeyaml.recursive.Human2
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+: This is My Son
+? !!org.yaml.snakeyaml.recursive.Human2
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashMap {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
+: This Is My Daughter
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-as-set.yaml b/src/test/resources/recursive/with-children-as-set.yaml
new file mode 100644
index 0000000..00799f2
--- /dev/null
+++ b/src/test/resources/recursive/with-children-as-set.yaml
@@ -0,0 +1,37 @@
+&id002 !!set
+? !!org.yaml.snakeyaml.recursive.Human
+  bankAccountOwner: &id001 !!org.yaml.snakeyaml.recursive.Human
+    bankAccountOwner: *id001
+    birthPlace: Leningrad
+    birthday: 1970-01-12T13:46:40Z
+    children: *id002
+    father: null
+    mother: null
+    name: Father
+    partner: &id003 !!org.yaml.snakeyaml.recursive.Human
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id002
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+  birthPlace: Munich
+  birthday: 1979-10-28T23:06:40Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Son
+  partner: null
+: null
+? !!org.yaml.snakeyaml.recursive.Human
+  bankAccountOwner: *id001
+  birthPlace: New York
+  birthday: 1983-04-24T02:40:00Z
+  children: !!java.util.LinkedHashSet {}
+  father: *id001
+  mother: *id003
+  name: Daughter
+  partner: null
+: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-no-root-tag.yaml b/src/test/resources/recursive/with-children-no-root-tag.yaml
new file mode 100644
index 0000000..57aa0b3
--- /dev/null
+++ b/src/test/resources/recursive/with-children-no-root-tag.yaml
@@ -0,0 +1,35 @@
+&id002
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003 !!set
+    *id002: null
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: !!set {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        partner: *id001
+      name: Daughter
+      partner: null
+    : null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: !!set {}
+father: *id001
+mother: *id004
+name: Son
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-children-pretty.yaml b/src/test/resources/recursive/with-children-pretty.yaml
new file mode 100644
index 0000000..5e77fe8
--- /dev/null
+++ b/src/test/resources/recursive/with-children-pretty.yaml
@@ -0,0 +1,43 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human {
+  bankAccountOwner: &id001 {
+    bankAccountOwner: *id001,
+    birthPlace: Leningrad,
+    birthday: !!timestamp '1970-01-12T13:46:40Z',
+    children: &id003 !!set {
+      *id002: null,
+      ? {
+        bankAccountOwner: *id001,
+        birthPlace: New York,
+        birthday: !!timestamp '1983-04-24T02:40:00Z',
+        children: !!set {
+          },
+        father: *id001,
+        mother: &id004 {
+          bankAccountOwner: *id001,
+          birthPlace: Saint-Petersburg,
+          birthday: !!timestamp '1973-03-03T09:46:40Z',
+          children: *id003,
+          father: null,
+          mother: null,
+          name: Mother,
+          partner: *id001
+        },
+        name: Daughter,
+        partner: null
+      }
+      : null
+    },
+    father: null,
+    mother: null,
+    name: Father,
+    partner: *id004
+  },
+  birthPlace: Munich,
+  birthday: !!timestamp '1979-10-28T23:06:40Z',
+  children: !!set {
+    },
+  father: *id001,
+  mother: *id004,
+  name: Son,
+  partner: null
+}
diff --git a/src/test/resources/recursive/with-children.yaml b/src/test/resources/recursive/with-children.yaml
new file mode 100644
index 0000000..117bd28
--- /dev/null
+++ b/src/test/resources/recursive/with-children.yaml
@@ -0,0 +1,35 @@
+&id002
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003 !!set
+    *id002: null
+    ? bankAccountOwner: *id001
+      birthPlace: New York
+      birthday: 1983-04-24T02:40:00Z
+      children: !!set {}
+      father: *id001
+      mother: &id004
+        bankAccountOwner: *id001
+        birthPlace: Saint-Petersburg
+        birthday: 1973-03-03T09:46:40Z
+        children: *id003
+        father: null
+        mother: null
+        name: Mother
+        partner: *id001
+      name: Daughter
+      partner: null
+    : null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: !!set {}
+father: *id001
+mother: *id004
+name: Son
+partner: null
diff --git a/src/test/resources/recursive/with-childrenArray-no-root-tag.yaml b/src/test/resources/recursive/with-childrenArray-no-root-tag.yaml
new file mode 100644
index 0000000..c76437b
--- /dev/null
+++ b/src/test/resources/recursive/with-childrenArray-no-root-tag.yaml
@@ -0,0 +1,34 @@
+&id002
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+    name: Daughter
+    partner: null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/recursive/with-childrenArray.yaml b/src/test/resources/recursive/with-childrenArray.yaml
new file mode 100644
index 0000000..b174b7b
--- /dev/null
+++ b/src/test/resources/recursive/with-childrenArray.yaml
@@ -0,0 +1,34 @@
+&id002 !!org.yaml.snakeyaml.recursive.Human_WithArrayOfChildrenTest$Human_WithArrayOfChildren
+bankAccountOwner: &id001
+  bankAccountOwner: *id001
+  birthPlace: Leningrad
+  birthday: 1970-01-12T13:46:40Z
+  children: &id003
+  - *id002
+  - bankAccountOwner: *id001
+    birthPlace: New York
+    birthday: 1983-04-24T02:40:00Z
+    children: []
+    father: *id001
+    mother: &id004
+      bankAccountOwner: *id001
+      birthPlace: Saint-Petersburg
+      birthday: 1973-03-03T09:46:40Z
+      children: *id003
+      father: null
+      mother: null
+      name: Mother
+      partner: *id001
+    name: Daughter
+    partner: null
+  father: null
+  mother: null
+  name: Father
+  partner: *id004
+birthPlace: Munich
+birthday: 1979-10-28T23:06:40Z
+children: []
+father: *id001
+mother: *id004
+name: Son
+partner: null
\ No newline at end of file
diff --git a/src/test/resources/representer/scalar-style1.yaml b/src/test/resources/representer/scalar-style1.yaml
new file mode 100644
index 0000000..a1c6842
--- /dev/null
+++ b/src/test/resources/representer/scalar-style1.yaml
@@ -0,0 +1,6 @@
+name: Steve Jobs
+address: |-
+  Name
+  Street Number
+  Country
+description: 1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000
diff --git a/src/test/resources/representer/scalar-style2.yaml b/src/test/resources/representer/scalar-style2.yaml
new file mode 100644
index 0000000..cbbc13d
--- /dev/null
+++ b/src/test/resources/representer/scalar-style2.yaml
@@ -0,0 +1,10 @@
+name: Steve Jobs
+address: |-
+  Name
+  Street Number
+  Country
+description: >-
+  1111111111 2222222222 3333333333
+  4444444444 5555555555 6666666666
+  7777777777 8888888888 9999999999
+  0000000000
diff --git a/src/test/resources/representer/scalar-style3.yaml b/src/test/resources/representer/scalar-style3.yaml
new file mode 100644
index 0000000..8402790
--- /dev/null
+++ b/src/test/resources/representer/scalar-style3.yaml
@@ -0,0 +1,7 @@
+name: Steve Jobs
+address: |-
+  Name
+  Street Number
+  Country
+description: >-
+  1111111111 2222222222 3333333333 4444444444 5555555555 6666666666 7777777777 8888888888 9999999999 0000000000
diff --git a/src/test/resources/representer/stacktrace1.txt b/src/test/resources/representer/stacktrace1.txt
new file mode 100644
index 0000000..d03a10d
--- /dev/null
+++ b/src/test/resources/representer/stacktrace1.txt
@@ -0,0 +1,6 @@
+Exception in thread "main" org.apache.commons.lang.UnhandledException: org.apache.commons.lang.UnhandledException: while parsing a block mapping
+    at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:575)
+    at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:162)
+    at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:147)
+    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:227)
+    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:159)
diff --git a/src/test/resources/representer/stacktrace1.yaml b/src/test/resources/representer/stacktrace1.yaml
new file mode 100644
index 0000000..9d21a75
--- /dev/null
+++ b/src/test/resources/representer/stacktrace1.yaml
@@ -0,0 +1,8 @@
+|
+  Exception in thread "main" org.apache.commons.lang.UnhandledException: org.apache.commons.lang.UnhandledException: while parsing a block mapping
+      at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java:575)
+      at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java:162)
+      at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java:147)
+      at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java:227)
+      at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java:159)
+
diff --git a/src/test/resources/representer/stacktrace2.txt b/src/test/resources/representer/stacktrace2.txt
new file mode 100644
index 0000000..8e6736a
--- /dev/null
+++ b/src/test/resources/representer/stacktrace2.txt
@@ -0,0 +1,6 @@
+org.apache.commons.lang.UnhandledException@ while parsing a block mapping
+    at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java@575)
+    at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java@162)
+    at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java@147)
+    at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java@227)
+    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java@159)
diff --git a/src/test/resources/representer/stacktrace3.txt b/src/test/resources/representer/stacktrace3.txt
new file mode 100644
index 0000000..e9a8f21
--- /dev/null
+++ b/src/test/resources/representer/stacktrace3.txt
@@ -0,0 +1,6 @@
+org.apache.commons.lang.UnhandledException@ while parsing a block mapping
+    at org.yaml.snakeyaml.parser.ParserImpl$ParseBlockMappingKey.produce(ParserImpl.java@575)
+    at org.yaml.snakeyaml.parser.ParserImpl.peekEvent(ParserImpl.java@162)
+	at org.yaml.snakeyaml.parser.ParserImpl.checkEvent(ParserImpl.java@147)
+	at org.yaml.snakeyaml.composer.Composer.composeMappingNode(Composer.java@227)
+    at org.yaml.snakeyaml.composer.Composer.composeNode(Composer.java@159)
diff --git a/src/test/resources/ruby/ruby1.yaml b/src/test/resources/ruby/ruby1.yaml
new file mode 100644
index 0000000..e1329ec
--- /dev/null
+++ b/src/test/resources/ruby/ruby1.yaml
@@ -0,0 +1,10 @@
+--- !ruby/object:Test::Module::Object
+  sub1: !ruby/object:Test::Module::Sub1
+    att1: []
+    att2: 0
+    att3: []
+  sub2: !ruby/object:Test::Module::Sub2
+    att1: MyString
+    att2:
+    - entry1
+    att3: 12345
diff --git a/src/test/resources/specification/example2_1.yaml b/src/test/resources/specification/example2_1.yaml
new file mode 100644
index 0000000..d12e671
--- /dev/null
+++ b/src/test/resources/specification/example2_1.yaml
@@ -0,0 +1,3 @@
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
diff --git a/src/test/resources/specification/example2_10.yaml b/src/test/resources/specification/example2_10.yaml
new file mode 100644
index 0000000..61808f6
--- /dev/null
+++ b/src/test/resources/specification/example2_10.yaml
@@ -0,0 +1,8 @@
+---
+hr:
+  - Mark McGwire
+  # Following node labeled SS
+  - &SS Sammy Sosa
+rbi:
+  - *SS # Subsequent occurrence
+  - Ken Griffey
diff --git a/src/test/resources/specification/example2_11.yaml b/src/test/resources/specification/example2_11.yaml
new file mode 100644
index 0000000..9123ce2
--- /dev/null
+++ b/src/test/resources/specification/example2_11.yaml
@@ -0,0 +1,9 @@
+? - Detroit Tigers
+  - Chicago cubs
+:
+  - 2001-07-23
+
+? [ New York Yankees,
+    Atlanta Braves ]
+: [ 2001-07-02, 2001-08-12,
+    2001-08-14 ]
diff --git a/src/test/resources/specification/example2_12.yaml b/src/test/resources/specification/example2_12.yaml
new file mode 100644
index 0000000..1fc33f9
--- /dev/null
+++ b/src/test/resources/specification/example2_12.yaml
@@ -0,0 +1,8 @@
+---
+# products purchased
+- item    : Super Hoop
+  quantity: 1
+- item    : Basketball
+  quantity: 4
+- item    : Big Shoes
+  quantity: 1
diff --git a/src/test/resources/specification/example2_13.yaml b/src/test/resources/specification/example2_13.yaml
new file mode 100644
index 0000000..13fb656
--- /dev/null
+++ b/src/test/resources/specification/example2_13.yaml
@@ -0,0 +1,4 @@
+# ASCII Art
+--- |
+  \//||\/||
+  // ||  ||__
diff --git a/src/test/resources/specification/example2_14.yaml b/src/test/resources/specification/example2_14.yaml
new file mode 100644
index 0000000..59943de
--- /dev/null
+++ b/src/test/resources/specification/example2_14.yaml
@@ -0,0 +1,4 @@
+---
+  Mark McGwire's
+  year was crippled
+  by a knee injury.
diff --git a/src/test/resources/specification/example2_15.yaml b/src/test/resources/specification/example2_15.yaml
new file mode 100644
index 0000000..80b89a6
--- /dev/null
+++ b/src/test/resources/specification/example2_15.yaml
@@ -0,0 +1,8 @@
+>
+ Sammy Sosa completed another
+ fine season with great stats.
+
+   63 Home Runs
+   0.288 Batting Average
+
+ What a year!
diff --git a/src/test/resources/specification/example2_15_dumped.yaml b/src/test/resources/specification/example2_15_dumped.yaml
new file mode 100644
index 0000000..cc2d963
--- /dev/null
+++ b/src/test/resources/specification/example2_15_dumped.yaml
@@ -0,0 +1,7 @@
+>
+  Sammy Sosa completed another fine season with great stats.
+
+    63 Home Runs
+    0.288 Batting Average
+
+  What a year!
\ No newline at end of file
diff --git a/src/test/resources/specification/example2_16.yaml b/src/test/resources/specification/example2_16.yaml
new file mode 100644
index 0000000..9f66d88
--- /dev/null
+++ b/src/test/resources/specification/example2_16.yaml
@@ -0,0 +1,7 @@
+name: Mark McGwire
+accomplishment: >
+  Mark set a major league
+  home run record in 1998.
+stats: |
+  65 Home Runs
+  0.278 Batting Average
diff --git a/src/test/resources/specification/example2_17.yaml b/src/test/resources/specification/example2_17.yaml
new file mode 100644
index 0000000..3e899c0
--- /dev/null
+++ b/src/test/resources/specification/example2_17.yaml
@@ -0,0 +1,7 @@
+unicode: "Sosa did fine.\u263A"
+control: "\b1998\t1999\t2000\n"
+hexesc:  "\x0D\x0A is \r\n"
+
+single: '"Howdy!" he cried.'
+quoted: ' # not a ''comment''.'
+tie-fighter: '|\-*-/|'
diff --git a/src/test/resources/specification/example2_17_control.yaml b/src/test/resources/specification/example2_17_control.yaml
new file mode 100644
index 0000000..59398a6
--- /dev/null
+++ b/src/test/resources/specification/example2_17_control.yaml
@@ -0,0 +1,2 @@
+control: "\b1998\t1999\t2000\n"
+
diff --git a/src/test/resources/specification/example2_17_hexesc.yaml b/src/test/resources/specification/example2_17_hexesc.yaml
new file mode 100644
index 0000000..7ddff26
--- /dev/null
+++ b/src/test/resources/specification/example2_17_hexesc.yaml
@@ -0,0 +1,2 @@
+hexesc:  "\x0D\x0A is \r\n"
+
diff --git a/src/test/resources/specification/example2_17_quoted.yaml b/src/test/resources/specification/example2_17_quoted.yaml
new file mode 100644
index 0000000..bedc4a5
--- /dev/null
+++ b/src/test/resources/specification/example2_17_quoted.yaml
@@ -0,0 +1,2 @@
+quoted: ' # not a ''comment''.'
+
diff --git a/src/test/resources/specification/example2_17_single.yaml b/src/test/resources/specification/example2_17_single.yaml
new file mode 100644
index 0000000..c3fe6aa
--- /dev/null
+++ b/src/test/resources/specification/example2_17_single.yaml
@@ -0,0 +1 @@
+single: '"Howdy!" he cried.'
diff --git a/src/test/resources/specification/example2_17_tie_fighter.yaml b/src/test/resources/specification/example2_17_tie_fighter.yaml
new file mode 100644
index 0000000..9d82173
--- /dev/null
+++ b/src/test/resources/specification/example2_17_tie_fighter.yaml
@@ -0,0 +1 @@
+tie-fighter: '|\-*-/|'
diff --git a/src/test/resources/specification/example2_17_unicode.yaml b/src/test/resources/specification/example2_17_unicode.yaml
new file mode 100644
index 0000000..2b378bd
--- /dev/null
+++ b/src/test/resources/specification/example2_17_unicode.yaml
@@ -0,0 +1,2 @@
+unicode: "Sosa did fine.\u263A"
+
diff --git a/src/test/resources/specification/example2_18.yaml b/src/test/resources/specification/example2_18.yaml
new file mode 100644
index 0000000..e0a8bfa
--- /dev/null
+++ b/src/test/resources/specification/example2_18.yaml
@@ -0,0 +1,6 @@
+plain:
+  This unquoted scalar
+  spans many lines.
+
+quoted: "So does this
+  quoted scalar.\n"
diff --git a/src/test/resources/specification/example2_19.yaml b/src/test/resources/specification/example2_19.yaml
new file mode 100644
index 0000000..8aeb1a4
--- /dev/null
+++ b/src/test/resources/specification/example2_19.yaml
@@ -0,0 +1,5 @@
+canonical: 12345
+decimal: +12_345
+sexagesimal: 3:25:45
+octal: 014
+hexadecimal: 0xC
diff --git a/src/test/resources/specification/example2_2.yaml b/src/test/resources/specification/example2_2.yaml
new file mode 100644
index 0000000..7b7ec94
--- /dev/null
+++ b/src/test/resources/specification/example2_2.yaml
@@ -0,0 +1,3 @@
+hr:  65    # Home runs
+avg: 0.278 # Batting average
+rbi: 147   # Runs Batted In
diff --git a/src/test/resources/specification/example2_20.yaml b/src/test/resources/specification/example2_20.yaml
new file mode 100644
index 0000000..60bfc06
--- /dev/null
+++ b/src/test/resources/specification/example2_20.yaml
@@ -0,0 +1,6 @@
+canonical: 1.23015e+3
+exponential: 12.3015e+02
+sexagesimal: 20:30.15
+fixed: 1_230.15
+negative infinity: -.inf
+not a number: .NaN
diff --git a/src/test/resources/specification/example2_21.yaml b/src/test/resources/specification/example2_21.yaml
new file mode 100644
index 0000000..c065b2a
--- /dev/null
+++ b/src/test/resources/specification/example2_21.yaml
@@ -0,0 +1,4 @@
+null: ~
+true: yes
+false: no
+string: '12345'
diff --git a/src/test/resources/specification/example2_22.yaml b/src/test/resources/specification/example2_22.yaml
new file mode 100644
index 0000000..aaac185
--- /dev/null
+++ b/src/test/resources/specification/example2_22.yaml
@@ -0,0 +1,4 @@
+canonical: 2001-12-15T02:59:43.1Z
+iso8601: 2001-12-14t21:59:43.10-05:00
+spaced: 2001-12-14 21:59:43.10 -5
+date: 2002-12-14
diff --git a/src/test/resources/specification/example2_23.yaml b/src/test/resources/specification/example2_23.yaml
new file mode 100644
index 0000000..adbe4e6
--- /dev/null
+++ b/src/test/resources/specification/example2_23.yaml
@@ -0,0 +1,14 @@
+---
+not-date: !!str 2002-04-28
+
+picture: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X\
+ 17unp5WZmZgAAAOfn515eXv\
+ Pz7Y6OjuDg4J+fn5OTk6enp\
+ 56enmleECcgggoBADs="
+
+application specific tag: !something |
+ The semantics of the tag
+ above may be different for
+ different documents.
+
diff --git a/src/test/resources/specification/example2_23_application.yaml b/src/test/resources/specification/example2_23_application.yaml
new file mode 100644
index 0000000..03cc760
--- /dev/null
+++ b/src/test/resources/specification/example2_23_application.yaml
@@ -0,0 +1,5 @@
+---
+application specific tag: !something |
+ The semantics of the tag
+ above may be different for
+ different documents.
diff --git a/src/test/resources/specification/example2_23_non_date.yaml b/src/test/resources/specification/example2_23_non_date.yaml
new file mode 100644
index 0000000..2e95415
--- /dev/null
+++ b/src/test/resources/specification/example2_23_non_date.yaml
@@ -0,0 +1,3 @@
+---
+not-date: !!str 2002-04-28
+
diff --git a/src/test/resources/specification/example2_23_picture.yaml b/src/test/resources/specification/example2_23_picture.yaml
new file mode 100644
index 0000000..b87063e
--- /dev/null
+++ b/src/test/resources/specification/example2_23_picture.yaml
@@ -0,0 +1,9 @@
+---
+picture: !!binary "\
+ R0lGODlhDAAMAIQAAP//9/X\
+ 17unp5WZmZgAAAOfn515eXv\
+ Pz7Y6OjuDg4J+fn5OTk6enp\
+ 56enmleECcgggoBADs="
+ 
+ 
+ 
\ No newline at end of file
diff --git a/src/test/resources/specification/example2_24.yaml b/src/test/resources/specification/example2_24.yaml
new file mode 100644
index 0000000..1180757
--- /dev/null
+++ b/src/test/resources/specification/example2_24.yaml
@@ -0,0 +1,14 @@
+%TAG ! tag:clarkevans.com,2002:
+--- !shape
+  # Use the ! handle for presenting
+  # tag:clarkevans.com,2002:circle
+- !circle
+  center: &ORIGIN {x: 73, y: 129}
+  radius: 7
+- !line
+  start: *ORIGIN
+  finish: { x: 89, y: 102 }
+- !label
+  start: *ORIGIN
+  color: 0xFFEEBB
+  text: Pretty vector drawing.
diff --git a/src/test/resources/specification/example2_24_dumped.yaml b/src/test/resources/specification/example2_24_dumped.yaml
new file mode 100644
index 0000000..1742cd2
--- /dev/null
+++ b/src/test/resources/specification/example2_24_dumped.yaml
@@ -0,0 +1,11 @@
+!shape
+- !circle
+  center: &id001 {x: 73, y: 129}
+  radius: 7
+- !line
+  finish: {x: 89, y: 102}
+  start: *id001
+- !label
+  color: 0xFFEEBB
+  start: *id001
+  text: Pretty vector drawing.
\ No newline at end of file
diff --git a/src/test/resources/specification/example2_25.yaml b/src/test/resources/specification/example2_25.yaml
new file mode 100644
index 0000000..769ac31
--- /dev/null
+++ b/src/test/resources/specification/example2_25.yaml
@@ -0,0 +1,7 @@
+# sets are represented as a
+# mapping where each key is
+# associated with the empty string
+--- !!set
+? Mark McGwire
+? Sammy Sosa
+? Ken Griff
diff --git a/src/test/resources/specification/example2_26.yaml b/src/test/resources/specification/example2_26.yaml
new file mode 100644
index 0000000..3143763
--- /dev/null
+++ b/src/test/resources/specification/example2_26.yaml
@@ -0,0 +1,7 @@
+# ordered maps are represented as
+# a sequence of mappings, with
+# each mapping having one key
+--- !!omap
+- Mark McGwire: 65
+- Sammy Sosa: 63
+- Ken Griffy: 58
diff --git a/src/test/resources/specification/example2_27.yaml b/src/test/resources/specification/example2_27.yaml
new file mode 100644
index 0000000..395e79c
--- /dev/null
+++ b/src/test/resources/specification/example2_27.yaml
@@ -0,0 +1,29 @@
+--- !<tag:clarkevans.com,2002:invoice>
+invoice: 34843
+date   : 2001-01-23
+billTo: &id001
+    given  : Chris
+    family : Dumars
+    address:
+        lines: |
+            458 Walkman Dr.
+            Suite #292
+        city    : Royal Oak
+        state   : MI
+        postal  : 48046
+shipTo: *id001
+product:
+    - sku         : BL394D
+      quantity    : 4
+      description : Basketball
+      price       : 450.00
+    - sku         : BL4438H
+      quantity    : 1
+      description : Super Hoop
+      price       : 2392.00
+tax  : 251.42
+total: 4443.52
+comments:
+    Late afternoon is best.
+    Backup contact is Nancy
+    Billsmer @ 338-4338.
diff --git a/src/test/resources/specification/example2_27_dumped.yaml b/src/test/resources/specification/example2_27_dumped.yaml
new file mode 100644
index 0000000..51a89b8
--- /dev/null
+++ b/src/test/resources/specification/example2_27_dumped.yaml
@@ -0,0 +1,20 @@
+!!org.yaml.snakeyaml.Invoice
+billTo: &id001
+  address:
+    city: Royal Oak
+    lines: |
+      458 Walkman Dr.
+      Suite #292
+    postal: '48046'
+    state: MI
+  family: Dumars
+  given: Chris
+comments: Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.
+date: '2001-01-23'
+invoice: 34843
+product:
+- {description: Basketball, price: 450.0, quantity: 4, sku: BL394D}
+- {description: Super Hoop, price: 2392.0, quantity: 1, sku: BL4438H}
+shipTo: *id001
+tax: 251.42
+total: 4443.52
\ No newline at end of file
diff --git a/src/test/resources/specification/example2_28.yaml b/src/test/resources/specification/example2_28.yaml
new file mode 100644
index 0000000..eb5fb8a
--- /dev/null
+++ b/src/test/resources/specification/example2_28.yaml
@@ -0,0 +1,29 @@
+---
+Time: 2001-11-23 15:01:42 -5
+User: ed
+Warning:
+  This is an error message
+  for the log file
+---
+Time: 2001-11-23 15:02:31 -5
+User: ed
+Warning:
+  A slightly different error
+  message.
+---
+Date: 2001-11-23 15:03:17 -5
+User: ed
+Fatal:
+  Unknown variable "bar"
+Stack:
+  - file: TopClass.py
+    line: 23
+    code: |
+      x = MoreObject("345\n")
+  - file: MoreClass.py
+    line: 58
+    code: |-
+      foo = bar
+
+
+
diff --git a/src/test/resources/specification/example2_3.yaml b/src/test/resources/specification/example2_3.yaml
new file mode 100644
index 0000000..2c884b7
--- /dev/null
+++ b/src/test/resources/specification/example2_3.yaml
@@ -0,0 +1,8 @@
+american:
+  - Boston Red Sox
+  - Detroit Tigers
+  - New York Yankees
+national:
+  - New York Mets
+  - Chicago Cubs
+  - Atlanta Braves
\ No newline at end of file
diff --git a/src/test/resources/specification/example2_4.yaml b/src/test/resources/specification/example2_4.yaml
new file mode 100644
index 0000000..430f6b3
--- /dev/null
+++ b/src/test/resources/specification/example2_4.yaml
@@ -0,0 +1,8 @@
+-
+  name: Mark McGwire
+  hr:   65
+  avg:  0.278
+-
+  name: Sammy Sosa
+  hr:   63
+  avg:  0.288
diff --git a/src/test/resources/specification/example2_5.yaml b/src/test/resources/specification/example2_5.yaml
new file mode 100644
index 0000000..cdd7770
--- /dev/null
+++ b/src/test/resources/specification/example2_5.yaml
@@ -0,0 +1,3 @@
+- [name        , hr, avg  ]
+- [Mark McGwire, 65, 0.278]
+- [Sammy Sosa  , 63, 0.288]
diff --git a/src/test/resources/specification/example2_6.yaml b/src/test/resources/specification/example2_6.yaml
new file mode 100644
index 0000000..7a957b2
--- /dev/null
+++ b/src/test/resources/specification/example2_6.yaml
@@ -0,0 +1,5 @@
+Mark McGwire: {hr: 65, avg: 0.278}
+Sammy Sosa: {
+    hr: 63,
+    avg: 0.288
+  }
diff --git a/src/test/resources/specification/example2_7.yaml b/src/test/resources/specification/example2_7.yaml
new file mode 100644
index 0000000..bc711d5
--- /dev/null
+++ b/src/test/resources/specification/example2_7.yaml
@@ -0,0 +1,10 @@
+# Ranking of 1998 home runs
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+# Team ranking
+---
+- Chicago Cubs
+- St Louis Cardinals
diff --git a/src/test/resources/specification/example2_8.yaml b/src/test/resources/specification/example2_8.yaml
new file mode 100644
index 0000000..05e102d
--- /dev/null
+++ b/src/test/resources/specification/example2_8.yaml
@@ -0,0 +1,10 @@
+---
+time: 20:03:20
+player: Sammy Sosa
+action: strike (miss)
+...
+---
+time: 20:03:47
+player: Sammy Sosa
+action: grand slam
+...
diff --git a/src/test/resources/specification/example2_9.yaml b/src/test/resources/specification/example2_9.yaml
new file mode 100644
index 0000000..e264180
--- /dev/null
+++ b/src/test/resources/specification/example2_9.yaml
@@ -0,0 +1,8 @@
+---
+hr: # 1998 hr ranking
+  - Mark McGwire
+  - Sammy Sosa
+rbi:
+  # 1998 rbi ranking
+  - Sammy Sosa
+  - Ken Griffey
diff --git a/src/test/resources/specification/types/map.yaml b/src/test/resources/specification/types/map.yaml
new file mode 100644
index 0000000..022446d
--- /dev/null
+++ b/src/test/resources/specification/types/map.yaml
@@ -0,0 +1,6 @@
+# Unordered set of key: value pairs.
+Block style: !!map
+  Clark : Evans
+  Brian : Ingerson
+  Oren  : Ben-Kiki
+Flow style: !!map { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki }
diff --git a/src/test/resources/specification/types/map_mixed_tags.yaml b/src/test/resources/specification/types/map_mixed_tags.yaml
new file mode 100644
index 0000000..a5d35b0
--- /dev/null
+++ b/src/test/resources/specification/types/map_mixed_tags.yaml
@@ -0,0 +1,6 @@
+# Unordered set of key: value pairs.
+Block style: !<tag:yaml.org,2002:map>
+  Clark : Evans
+  Brian : Ingerson
+  Oren  : Ben-Kiki
+Flow style: { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki }
diff --git a/src/test/resources/specification/types/merge.yaml b/src/test/resources/specification/types/merge.yaml
new file mode 100644
index 0000000..ee4a48f
--- /dev/null
+++ b/src/test/resources/specification/types/merge.yaml
@@ -0,0 +1,27 @@
+---
+- &CENTER { x: 1, y: 2 }
+- &LEFT { x: 0, y: 2 }
+- &BIG { r: 10 }
+- &SMALL { r: 1 }
+
+# All the following maps are equal:
+
+- # Explicit keys
+  x: 1
+  y: 2
+  r: 10
+  label: center/big
+
+- # Merge one map
+  << : *CENTER
+  r: 10
+  label: center/big
+
+- # Merge multiple maps
+  << : [ *CENTER, *BIG ]
+  label: center/big
+
+- # Override
+  << : [ *BIG, *LEFT, *SMALL ]
+  x: 1
+  label: center/big
diff --git a/src/test/resources/specification/types/omap.yaml b/src/test/resources/specification/types/omap.yaml
new file mode 100644
index 0000000..4fa0f45
--- /dev/null
+++ b/src/test/resources/specification/types/omap.yaml
@@ -0,0 +1,8 @@
+# Explicitly typed ordered map (dictionary).
+Bestiary: !!omap
+  - aardvark: African pig-like ant eater. Ugly.
+  - anteater: South-American ant eater. Two species.
+  - anaconda: South-American constrictor snake. Scaly.
+  # Etc.
+# Flow style
+Numbers: !!omap [ one: 1, two: 2, three : 3 ]
diff --git a/src/test/resources/specification/types/pairs.yaml b/src/test/resources/specification/types/pairs.yaml
new file mode 100644
index 0000000..05f55b9
--- /dev/null
+++ b/src/test/resources/specification/types/pairs.yaml
@@ -0,0 +1,7 @@
+# Explicitly typed pairs.
+Block tasks: !!pairs
+  - meeting: with team.
+  - meeting: with boss.
+  - break: lunch.
+  - meeting: with client.
+Flow tasks: !!pairs [ meeting: with team, meeting: with boss ]
diff --git a/src/test/resources/specification/types/seq.yaml b/src/test/resources/specification/types/seq.yaml
new file mode 100644
index 0000000..5849115
--- /dev/null
+++ b/src/test/resources/specification/types/seq.yaml
@@ -0,0 +1,14 @@
+# Ordered sequence of nodes
+Block style: !!seq
+- Mercury   # Rotates - no light/dark sides.
+- Venus     # Deadliest. Aptly named.
+- Earth     # Mostly dirt.
+- Mars      # Seems empty.
+- Jupiter   # The king.
+- Saturn    # Pretty.
+- Uranus    # Where the sun hardly shines.
+- Neptune   # Boring. No rings.
+- Pluto     # You call this a planet?
+Flow style: !!seq [ Mercury, Venus, Earth, Mars,      # Rocks
+                    Jupiter, Saturn, Uranus, Neptune, # Gas
+                    Pluto ]                           # Overrated
diff --git a/src/test/resources/specification/types/set.yaml b/src/test/resources/specification/types/set.yaml
new file mode 100644
index 0000000..e05dc88
--- /dev/null
+++ b/src/test/resources/specification/types/set.yaml
@@ -0,0 +1,7 @@
+# Explicitly typed set.
+baseball players: !!set
+  ? Mark McGwire
+  ? Sammy Sosa
+  ? Ken Griffey
+# Flow style
+baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees }
diff --git a/src/test/resources/specification/types/v.yaml b/src/test/resources/specification/types/v.yaml
new file mode 100644
index 0000000..81c5d51
--- /dev/null
+++ b/src/test/resources/specification/types/v.yaml
@@ -0,0 +1,4 @@
+---     # New schema
+link with:
+  - = : library1.dll
+    version: 1.2
diff --git a/src/test/resources/specification/types/value.yaml b/src/test/resources/specification/types/value.yaml
new file mode 100644
index 0000000..3eb7919
--- /dev/null
+++ b/src/test/resources/specification/types/value.yaml
@@ -0,0 +1,10 @@
+---     # Old schema
+link with:
+  - library1.dll
+  - library2.dll
+---     # New schema
+link with:
+  - = : library1.dll
+    version: 1.2
+  - = : library2.dll
+    version: 2.3
diff --git a/src/test/resources/template/etalon1.yaml b/src/test/resources/template/etalon1.yaml
new file mode 100644
index 0000000..21e45a9
--- /dev/null
+++ b/src/test/resources/template/etalon1.yaml
@@ -0,0 +1,9 @@
+empty: []
+id: id123
+list:
+- aaa
+- bbb
+- ccc
+point:
+  x: 1.0
+  y: 2.0
\ No newline at end of file
diff --git a/src/test/resources/template/etalon2-template.yaml b/src/test/resources/template/etalon2-template.yaml
new file mode 100644
index 0000000..979892f
--- /dev/null
+++ b/src/test/resources/template/etalon2-template.yaml
@@ -0,0 +1,9 @@
+%YAML 1.1
+---
+#Welcome to my Beans !!!
+id: id123 # the ID must be first
+point: [1.0, 2.0]
+#collection are at the end
+list: [aaa, bbb, ccc]
+
+#empty collection is not emitted
diff --git a/src/test/resources/template/mybean1.vm b/src/test/resources/template/mybean1.vm
new file mode 100644
index 0000000..a4c2804
--- /dev/null
+++ b/src/test/resources/template/mybean1.vm
@@ -0,0 +1,12 @@
+%YAML 1.1

+---

+#Welcome to my Beans !!!

+id: $bean.id # the ID must be first

+point: [$bean.point.x, $bean.point.y]

+#collection are at the end

+list: $list

+#foreach( $item in $bean.empty )

+  - $item

+#end

+#empty collection is not emitted

+