Issue 66: prepare flexible scalar style
diff --git a/src/main/java/org/yaml/snakeyaml/DumperOptions.java b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
index 535e0f9..80ec8b1 100644
--- a/src/main/java/org/yaml/snakeyaml/DumperOptions.java
+++ b/src/main/java/org/yaml/snakeyaml/DumperOptions.java
@@ -31,6 +31,7 @@
      * double-quoted style. These styles offer a range of trade-offs between
      * expressive power and readability.
      * 
+     * @see http://yaml.org/spec/1.1/#id903915
      * @see http://yaml.org/spec/1.1/#id858081
      */
     public enum ScalarStyle {
diff --git a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
index 21fe6cb..c9c5876 100644
--- a/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
+++ b/src/main/java/org/yaml/snakeyaml/emitter/Emitter.java
@@ -1044,9 +1044,9 @@
             allowFlowPlain = allowBlockPlain = allowSingleQuoted = allowBlock = false;
         }
         // Although the plain scalar writer supports breaks, we never emit
-        // multiline plain scalars.
+        // multiline plain scalars in the flow context.
         if (lineBreaks) {
-            allowFlowPlain = allowBlockPlain = false;
+            allowFlowPlain = false;
         }
         // Flow indicators are forbidden for flow plain scalars.
         if (flowIndicators) {
diff --git a/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
index f967b35..1cbf00d 100644
--- a/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
+++ b/src/main/java/org/yaml/snakeyaml/events/ScalarEvent.java
@@ -54,7 +54,7 @@
     /**
      * Style of the scalar.
      * <dl>
-     * <dt>''</dt>
+     * <dt>null</dt>
      * <dd>Flow Style - Plain</dd>
      * <dt>'\''</dt>
      * <dd>Flow Style - Single-Quoted</dd>
diff --git a/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
index fd4d9b6..5108f45 100644
--- a/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
+++ b/src/main/java/org/yaml/snakeyaml/nodes/ScalarNode.java
@@ -47,7 +47,7 @@
      * Get scalar style of this node.
      * 
      * @see org.yaml.snakeyaml.events.ScalarEvent
-     * @see http://yaml.org/spec/1.1/#id864487
+     * @see http://yaml.org/spec/1.1/#id903915
      * @return
      */
     public Character getStyle() {
diff --git a/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java b/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java
index 3f2aa91..d4d7602 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/BaseRepresenter.java
@@ -48,7 +48,7 @@
     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>();

-    private Character defaultStyle;

+    protected Character defaultScalarStyle;

     protected FlowStyle defaultFlowStyle = FlowStyle.AUTO;

     protected final Map<Object, Node> representedObjects = new IdentityHashMap<Object, Node>() {

         private static final long serialVersionUID = -5576159264232131854L;

@@ -115,7 +115,7 @@
 

     protected Node representScalar(Tag tag, String value, Character style) {

         if (style == null) {

-            style = this.defaultStyle;

+            style = this.defaultScalarStyle;

         }

         Node node = new ScalarNode(tag, value, null, null, style);

         // representedObjects.put(objectToRepresent, node);

@@ -181,7 +181,7 @@
     }

 

     public void setDefaultScalarStyle(ScalarStyle defaultStyle) {

-        this.defaultStyle = defaultStyle.getChar();

+        this.defaultScalarStyle = defaultStyle.getChar();

     }

 

     public void setDefaultFlowStyle(FlowStyle defaultFlowStyle) {

diff --git a/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java b/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java
index b52d22e..0f2491f 100644
--- a/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java
+++ b/src/main/java/org/yaml/snakeyaml/representer/SafeRepresenter.java
@@ -105,6 +105,7 @@
     }

 

     public static Pattern BINARY_PATTERN = Pattern.compile("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]");

+    public static Pattern MULTILINE_PATTERN = Pattern.compile("\n|\u0085|\u2028|\u2029");

 

     protected class RepresentString implements Represent {

         public Node representData(Object data) {

@@ -118,6 +119,11 @@
                 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);

         }

     }

diff --git a/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java b/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java
index 25b7ea1..5d19e08 100644
--- a/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java
+++ b/src/test/java/org/yaml/snakeyaml/Chapter2_3Test.java
@@ -21,6 +21,8 @@
 

 import junit.framework.TestCase;

 

+import org.yaml.snakeyaml.DumperOptions.ScalarStyle;

+

 /**

  * Test Chapter 2.3 from the YAML specification

  * 

@@ -45,13 +47,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");

-        Yaml yaml = new 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);

-        assertTrue(dumped.contains("Sammy Sosa completed another fine season with great stats"));

-        assertEquals("Must be splitted into 2 lines.", 2, dumped.split("\n").length);

+        String etalonDumped = Util.getLocalResource("specification/example2_15_dumped.yaml");

+        assertEquals(etalonDumped, dumped);

     }

 

     @SuppressWarnings("unchecked")

diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java
index 1e42317..dbe4504 100644
--- a/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmitterMultiLineTest.java
@@ -16,6 +16,8 @@
 

 package org.yaml.snakeyaml.emitter;

 

+import java.util.ArrayList;

+import java.util.List;

 import java.util.Map;

 

 import junit.framework.TestCase;

@@ -31,17 +33,39 @@
         Yaml yaml = new Yaml();

         String output = yaml.dump(plain);

         // System.out.println(output);

-        assertEquals("'mama\n\n  mila\n\n  ramu'\n", 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 testWriteMultiLinePlainList() {

+        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 testWriteMultiLineLiteralNoChomping() {

         String source = "a: 1\nb: |\n  mama\n  mila\n  ramu\n";

         // System.out.println("Source:\n" + source);

-        DumperOptions options = new MyOptions();

-        Yaml yaml = new Yaml(options);

+        Yaml yaml = new Yaml();

         @SuppressWarnings("unchecked")

         Map<String, Object> parsed = (Map<String, Object>) yaml.load(source);

         String value = (String) parsed.get("b");

@@ -49,7 +73,7 @@
         assertEquals("mama\nmila\nramu\n", value);

         String dumped = yaml.dump(parsed);

         // System.out.println(dumped);

-        assertEquals("{a: 1, b: |\n    mama\n    mila\n    ramu\n}\n", dumped);

+        assertEquals("a: 1\nb: |\n  mama\n  mila\n  ramu\n", dumped);

     }

 

     public void testWriteMultiLineSingleQuotedInFlowContext() {

@@ -65,13 +89,13 @@
         assertEquals("mama\nmila\nramu", value);

         String dumped = yaml.dump(parsed);

         // System.out.println(dumped);

-        assertEquals(source, 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 MyOptions();

+        DumperOptions options = new DumperOptions();

         options.setDefaultFlowStyle(FlowStyle.BLOCK);

         Yaml yaml = new Yaml(options);

         @SuppressWarnings("unchecked")

@@ -83,16 +107,4 @@
         // System.out.println(dumped);

         assertEquals(source, dumped);

     }

-

-    private class MyOptions extends DumperOptions {

-        @Override

-        public DumperOptions.ScalarStyle calculateScalarStyle(ScalarAnalysis analysis,

-                DumperOptions.ScalarStyle style) {

-            if (analysis.scalar.length() > 8) {

-                return ScalarStyle.LITERAL;

-            } else {

-                return super.calculateScalarStyle(analysis, style);

-            }

-        }

-    }

 }

diff --git a/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
index fd2a807..634b291 100644
--- a/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
+++ b/src/test/java/org/yaml/snakeyaml/emitter/EmitterTest.java
@@ -62,7 +62,7 @@
         map.put("bbb", "\nbla-bla");

         Yaml yaml = new Yaml(options);

         String output = yaml.dump(map);

-        String etalon = "{aaa: '0123456789 0123456789\n\n    0123456789 0123456789', bbb: '\n\n    bla-bla'}\n";

+        String etalon = "aaa: |-\n  0123456789 0123456789\n  0123456789 0123456789\nbbb: |2-\n\n  bla-bla\n";

         assertEquals(etalon, output);

     }

 

@@ -78,7 +78,7 @@
 

         Yaml yaml = new Yaml(options);

         String output = yaml.dump(map);

-        String etalon = "{\n  aaa: '0123456789 0123456789\n\n    0123456789 0123456789',\n  bbb: '\n\n    bla-bla'\n}\n";

+        String etalon = "aaa: |-\n  0123456789 0123456789\n  0123456789 0123456789\nbbb: |2-\n\n  bla-bla\n";

         assertEquals(etalon, output);

     }

 

diff --git a/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java b/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java
index bd1eded..fd6c852 100644
--- a/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java
+++ b/src/test/java/org/yaml/snakeyaml/types/StrTagTest.java
@@ -117,7 +117,7 @@
     public void testEmitLongStringWithCR() {

         String str = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\n";

         String output = dump(str);

-        assertEquals("'" + str + "\n  '\n", output);

+        assertEquals("|+\n  " + str, output);

     }

 

     public void testEmitDoubleQuoted() {

@@ -156,7 +156,7 @@
     public void testEmitEndOfLine() {

         String str = "xxxxxxx\n";

         String output = dump(str);

-        assertEquals("'" + str + "\n  '\n", output);

+        assertEquals("|\n  " + str, output);

     }

 

     public void testDumpUtf16() throws UnsupportedEncodingException {

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..b7516cc
--- /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_27_dumped.yaml b/src/test/resources/specification/example2_27_dumped.yaml
index 1d865e0..5a36138 100644
--- a/src/test/resources/specification/example2_27_dumped.yaml
+++ b/src/test/resources/specification/example2_27_dumped.yaml
@@ -1,10 +1,12 @@
 !!org.yaml.snakeyaml.Invoice

 billTo: &id001

-  address: {city: Royal Oak, lines: '458 Walkman Dr.

-

+  address:

+    city: Royal Oak

+    lines: |

+      458 Walkman Dr.

       Suite #292

-

-      ', postal: '48046', state: MI}

+    postal: '48046'

+    state: MI

   family: Dumars

   given: Chris

 comments: Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.