diff --git a/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java
index a5c61b4..b73dc68 100644
--- a/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java
@@ -28,7 +28,7 @@ public enum Feature // implements FormatFeature // for 2.7
/**
* Whether we are to write an explicit document start marker ("---")
* or not.
- *
+ *
* @since 2.3
*/
WRITE_DOC_START_MARKER(true),
@@ -38,17 +38,17 @@ public enum Feature // implements FormatFeature // for 2.7
* or "generic" Object Id mechanism (false). Former works better for systems that
* are YAML-centric; latter may be better choice for interoperability, when
* converting between formats or accepting other formats.
- *
+ *
* @since 2.5
*/
USE_NATIVE_OBJECT_ID(true),
-
+
/**
* Whether to use YAML native Type Id construct for indicating type (true);
* or "generic" type property (false). Former works better for systems that
* are YAML-centric; latter may be better choice for interoperability, when
* converting between formats or accepting other formats.
- *
+ *
* @since 2.5
*/
USE_NATIVE_TYPE_ID(true),
@@ -80,7 +80,7 @@ public enum Feature // implements FormatFeature // for 2.7
* @since 2.7
*/
MINIMIZE_QUOTES(false),
-
+
/**
* Whether numbers stored as strings will be rendered with quotes (true) or
* without quotes (false, default) when MINIMIZE_QUOTES is enabled.
@@ -91,12 +91,21 @@ public enum Feature // implements FormatFeature // for 2.7
*
* @since 2.8.2
*/
- ALWAYS_QUOTE_NUMBERS_AS_STRINGS(false)
+ ALWAYS_QUOTE_NUMBERS_AS_STRINGS(false),
+
+ /**
+ * Whether for string containing newlines a literal block style
+ * should be used. This automatically enabled when {@link #MINIMIZE_QUOTES} is set.
+ *
+ * The content of such strings is limited to printable characters according to the rules of
+ * literal block style.
+ */
+ LITERAL_BLOCK_STYLE(false)
;
protected final boolean _defaultState;
protected final int _mask;
-
+
/**
* Method that calculates bit set (flags) of all features that
* are enabled by default.
@@ -111,14 +120,14 @@ public static int collectDefaults()
}
return flags;
}
-
+
private Feature(boolean defaultState) {
_defaultState = defaultState;
_mask = (1 << ordinal());
}
-
+
public boolean enabledByDefault() { return _defaultState; }
- public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
+ public boolean enabledIn(int flags) { return (flags & _mask) != 0; }
public int getMask() { return _mask; }
}
@@ -127,7 +136,7 @@ private Feature(boolean defaultState) {
/* Internal constants
/**********************************************************
*/
-
+
protected final static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
protected final static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
protected final static Pattern PLAIN_NUMBER_P = Pattern.compile("[0-9]*(\\.[0-9]*)?");
@@ -153,7 +162,7 @@ private Feature(boolean defaultState) {
// for field names, leave out quotes
private final static Character STYLE_NAME = null;
-
+
// numbers, booleans, should use implicit
private final static Character STYLE_SCALAR = null;
// Strings quoted for fun
@@ -185,7 +194,7 @@ private Feature(boolean defaultState) {
* need to output one.
*/
protected String _typeId;
-
+
/*
/**********************************************************
/* Life-cycle
@@ -203,14 +212,14 @@ public YAMLGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures,
_writer = out;
_outputOptions = buildDumperOptions(jsonFeatures, yamlFeatures, version);
-
+
_emitter = new Emitter(_writer, _outputOptions);
// should we start output now, or try to defer?
_emitter.emit(new StreamStartEvent(null, null));
Map noTags = Collections.emptyMap();
-
+
boolean startMarker = Feature.WRITE_DOC_START_MARKER.enabledIn(yamlFeatures);
-
+
_emitter.emit(new DocumentStartEvent(null, null, startMarker,
version, // for 1.10 was: ((version == null) ? null : version.getArray()),
noTags));
@@ -232,10 +241,10 @@ protected DumperOptions buildDumperOptions(int jsonFeatures, int yamlFeatures, o
return opt;
}
- /*
- /**********************************************************
- /* Versioned
- /**********************************************************
+ /*
+ /**********************************************************
+ /* Versioned
+ /**********************************************************
*/
@Override
@@ -291,7 +300,7 @@ public JsonGenerator overrideFormatFeatures(int values, int mask) {
_formatFeatures = (_formatFeatures & ~mask) | (values & mask);
return this;
}
-
+
@Override
public boolean canUseSchema(FormatSchema schema) {
return false;
@@ -330,13 +339,13 @@ public YAMLGenerator configure(Feature f, boolean state) {
}
return this;
}
-
+
/*
/**********************************************************************
/* Overridden methods; writing field names
/**********************************************************************
*/
-
+
/* And then methods overridden to make final, streamline some
* aspects...
*/
@@ -389,7 +398,7 @@ public final void flush() throws IOException
{
_writer.flush();
}
-
+
@Override
public void close() throws IOException
{
@@ -406,7 +415,7 @@ public void close() throws IOException
/* Public API: structural output
/**********************************************************
*/
-
+
@Override
public final void writeStartArray() throws IOException
{
@@ -422,7 +431,7 @@ public final void writeStartArray() throws IOException
_emitter.emit(new SequenceStartEvent(anchor, yamlTag,
implicit, null, null, style));
}
-
+
@Override
public final void writeEndArray() throws IOException
{
@@ -430,7 +439,7 @@ public final void writeEndArray() throws IOException
_reportError("Current context not Array but "+_writeContext.typeDesc());
}
// just to make sure we don't "leak" type ids
- _typeId = null;
+ _typeId = null;
_writeContext = _writeContext.getParent();
_emitter.emit(new SequenceEndEvent(null, null));
}
@@ -458,7 +467,7 @@ public final void writeEndObject() throws IOException
_reportError("Current context not Object but "+_writeContext.typeDesc());
}
// just to make sure we don't "leak" type ids
- _typeId = null;
+ _typeId = null;
_writeContext = _writeContext.getParent();
_emitter.emit(new MappingEndEvent(null, null));
}
@@ -488,6 +497,8 @@ public void writeString(String text) throws IOException,JsonGenerationException
} else {
style = STYLE_PLAIN;
}
+ } else if (Feature.LITERAL_BLOCK_STYLE.enabledIn(_formatFeatures) && text.indexOf('\n') >= 0) {
+ style = STYLE_LITERAL;
}
_writeScalar(text, "string", style);
}
@@ -565,7 +576,7 @@ public void writeRawValue(char[] text, int offset, int len) throws IOException {
/* Output method implementations, base64-encoded binary
/**********************************************************
*/
-
+
@Override
public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException
{
@@ -624,13 +635,13 @@ public void writeNumber(BigInteger v) throws IOException
_verifyValueWrite("write number");
_writeScalar(String.valueOf(v.toString()), "java.math.BigInteger", STYLE_SCALAR);
}
-
+
@Override
public void writeNumber(double d) throws IOException
{
_verifyValueWrite("write number");
_writeScalar(String.valueOf(d), "double", STYLE_SCALAR);
- }
+ }
@Override
public void writeNumber(float f) throws IOException
@@ -681,14 +692,14 @@ public boolean canWriteObjectId() {
// yes, YAML does support Native Type Ids!
// 10-Sep-2014, tatu: Except as per [#23] might not want to...
return Feature.USE_NATIVE_OBJECT_ID.enabledIn(_formatFeatures);
- }
+ }
@Override
public boolean canWriteTypeId() {
// yes, YAML does support Native Type Ids!
// 10-Sep-2014, tatu: Except as per [#22] might not want to...
return Feature.USE_NATIVE_TYPE_ID.enabledIn(_formatFeatures);
- }
+ }
@Override
public void writeTypeId(Object id)
@@ -706,7 +717,7 @@ public void writeObjectRef(Object id)
AliasEvent evt = new AliasEvent(String.valueOf(id), null, null);
_emitter.emit(evt);
}
-
+
@Override
public void writeObjectId(Object id)
throws IOException
@@ -749,7 +760,7 @@ protected void _writeScalar(String value, String type, Character style) throws I
{
_emitter.emit(_scalarEvent(value, style));
}
-
+
protected ScalarEvent _scalarEvent(String value, Character style)
{
String yamlTag = _typeId;
diff --git a/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java
index 1571fa3..f6c6240 100644
--- a/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java
+++ b/src/test/java/com/fasterxml/jackson/dataformat/yaml/SimpleGenerationTest.java
@@ -40,7 +40,7 @@ public void testStreamingObject() throws Exception
assertEquals("name: \"Brad\"\nage: 39", yaml);
gen.close();
}
-
+
public void testStreamingNested() throws Exception
{
YAMLFactory f = new YAMLFactory();
@@ -54,9 +54,9 @@ public void testStreamingNested() throws Exception
gen.writeString("b");
gen.writeEndArray();
gen.writeEndObject();
-
+
gen.close();
-
+
String yaml = w.toString();
// note: 1.12 uses more compact notation; 1.10 has prefix
@@ -139,7 +139,7 @@ public void testWithFile2() throws Exception
assertEquals(1, result.size());
assertEquals("Foobar", result.get("name"));
}
-
+
@SuppressWarnings("resource")
public void testStartMarker() throws Exception
{
@@ -190,7 +190,7 @@ public void testSplitLines() throws Exception
assertEquals("---\n" +
"- \"1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890\"",
yaml);
- }
+ }
public void testLiteralStringsSingleLine() throws Exception
{
@@ -237,7 +237,7 @@ public void testQuoteNumberStoredAsString() throws Exception
f.configure(YAMLGenerator.Feature.MINIMIZE_QUOTES, true);
f.configure(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS, true);
-
+
YAMLMapper mapper = new YAMLMapper(f);
Map content = new HashMap();
@@ -246,14 +246,14 @@ public void testQuoteNumberStoredAsString() throws Exception
assertEquals("---\n" +
"key: \"20\"", yaml);
-
+
content.clear();
content.put("key", "2.0");
yaml = mapper.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: \"2.0\"", yaml);
-
+
content.clear();
content.put("key", "2.0.1.2.3");
yaml = mapper.writeValueAsString(content).trim();
@@ -270,7 +270,7 @@ public void testNonQuoteNumberStoredAsString() throws Exception
assertFalse(f.isEnabled(YAMLGenerator.Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS));
f.configure(YAMLGenerator.Feature.MINIMIZE_QUOTES, true);
-
+
YAMLMapper mapper = new YAMLMapper(f);
Map content = new HashMap();
@@ -279,14 +279,14 @@ public void testNonQuoteNumberStoredAsString() throws Exception
assertEquals("---\n" +
"key: 20", yaml);
-
+
content.clear();
content.put("key", "2.0");
yaml = mapper.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: 2.0", yaml);
-
+
content.clear();
content.put("key", "2.0.1.2.3");
yaml = mapper.writeValueAsString(content).trim();
@@ -295,13 +295,38 @@ public void testNonQuoteNumberStoredAsString() throws Exception
"key: 2.0.1.2.3", yaml);
}
+ public void testLiteralBlockStyle() throws Exception
+ {
+ YAMLFactory f = new YAMLFactory();
+ // verify default settings
+ assertFalse(f.isEnabled(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE));
+
+ f.configure(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE, true);
+
+ YAMLMapper mapper = new YAMLMapper(f);
+
+ Map content = new HashMap();
+ content.put("text", "Hello\nWorld");
+ String yaml = mapper.writeValueAsString(content).trim();
+
+ assertEquals("---\n" +
+ "text: |-\n Hello\n World", yaml);
+
+ content.clear();
+ content.put("text", "Hello World");
+ yaml = mapper.writeValueAsString(content).trim();
+
+ assertEquals("---\n" +
+ "text: \"Hello World\"", yaml);
+ }
+
/*
/**********************************************************************
/* Helper methods
/**********************************************************************
*/
-
-
+
+
protected void _writeBradDoc(JsonGenerator gen) throws IOException
{
gen.writeStartObject();