Add writer indentation policy controls.

Set the default indentation to 0.
This commit is contained in:
Jonathan Wood 2015-06-28 10:30:35 -07:00
parent 24a6503d19
commit 72941c146d
7 changed files with 185 additions and 37 deletions

View file

@ -56,7 +56,7 @@ class MapValueWriter implements ValueWriter {
ValueWriter valueWriter = WRITERS.findWriterFor(fromValue);
if (valueWriter.isTable() || valueWriter == TABLE_ARRAY_VALUE_WRITER) {
valueWriter.write(fromValue, context.extend(quoteKey(key)));
valueWriter.write(fromValue, context.pushTable(quoteKey(key)));
}
}
}

View file

@ -16,7 +16,7 @@ class TableArrayValueWriter extends ArrayValueWriter {
public void write(Object value, WriterContext context) {
Collection values = normalize(value);
WriterContext subContext = context.extend().setIsArrayOfTable(true);
WriterContext subContext = context.pushTableFromArray();
for (Object elem : values) {
WRITERS.write(elem, subContext);

View file

@ -24,6 +24,9 @@ import static com.moandjiezana.toml.ValueWriters.WRITERS;
* </code></pre>
*/
public class TomlWriter {
private WriterIndentationPolicy indentationPolicy = new WriterIndentationPolicy();
/**
* Creates a TomlWriter instance.
*/
@ -36,7 +39,7 @@ public class TomlWriter {
* @return a string containing the TOML representation of the given Object
*/
public String write(Object from) {
return WRITERS.write(from);
return WRITERS.write(from, this);
}
/**
@ -73,4 +76,21 @@ public class TomlWriter {
writer.write(write(from));
writer.close();
}
public WriterIndentationPolicy getIndentationPolicy() {
return indentationPolicy;
}
/**
* Set the {@link WriterIndentationPolicy} for this writer.
*
* If unset, the default policy (no indentation) is used.
*
* @param indentationPolicy the new policy
* @return this TomlWriter instance
*/
public TomlWriter setIndentationPolicy(WriterIndentationPolicy indentationPolicy) {
this.indentationPolicy = indentationPolicy;
return this;
}
}

View file

@ -23,8 +23,8 @@ class ValueWriters {
return OBJECT_VALUE_WRITER;
}
String write(Object value) {
WriterContext context = new WriterContext();
String write(Object value, TomlWriter tomlWriter) {
WriterContext context = new WriterContext(tomlWriter);
write(value, context);
return context.output.toString();

View file

@ -1,26 +1,43 @@
package com.moandjiezana.toml;
import java.util.Arrays;
class WriterContext {
private String key = "";
private String currentTableIndent = "";
private String currentFieldIndent = "";
private boolean isArrayOfTable = false;
private final TomlWriter tomlWriter;
StringBuilder output = new StringBuilder();
WriterContext(String key, StringBuilder output) {
WriterContext(String key, String tableIndent, StringBuilder output, TomlWriter tomlWriter) {
this.key = key;
this.currentTableIndent = tableIndent;
this.currentFieldIndent = tableIndent + fillStringWithSpaces(tomlWriter.getIndentationPolicy().getKeyValueIndent());
this.output = output;
this.tomlWriter = tomlWriter;
}
WriterContext() {
WriterContext(TomlWriter tomlWriter) {
this.tomlWriter = tomlWriter;
}
WriterContext pushTable(String newKey) {
String newIndent = "";
if (!key.isEmpty()) {
newIndent = growIndent(tomlWriter.getIndentationPolicy());
}
WriterContext extend(String newKey) {
String fullKey = key + (key.isEmpty() ? newKey : "." + newKey);
return new WriterContext(fullKey, output);
return new WriterContext(fullKey, newIndent, output, tomlWriter);
}
WriterContext extend() {
return new WriterContext(key, output);
WriterContext pushTableFromArray() {
WriterContext subContext = new WriterContext(key, currentTableIndent, output, tomlWriter);
subContext.setIsArrayOfTable(true);
return subContext;
}
void writeKey() {
@ -32,6 +49,8 @@ class WriterContext {
output.append('\n');
}
output.append(currentTableIndent);
if (isArrayOfTable) {
output.append("[[").append(key).append("]]\n");
} else {
@ -40,11 +59,24 @@ class WriterContext {
}
void indent() {
output.append(key.isEmpty() ? "" : " ");
if (!key.isEmpty()) {
output.append(currentFieldIndent);
}
}
WriterContext setIsArrayOfTable(boolean isArrayOfTable) {
this.isArrayOfTable = isArrayOfTable;
return this;
}
private String growIndent(WriterIndentationPolicy indentationPolicy) {
return currentTableIndent + fillStringWithSpaces(indentationPolicy.getTableIndent());
}
private String fillStringWithSpaces(int count) {
char[] chars = new char[count];
Arrays.fill(chars, ' ');
return new String(chars);
}
}

View file

@ -0,0 +1,41 @@
package com.moandjiezana.toml;
/**
* Controls how a {@link TomlWriter} indents tables and key/value pairs.
*
* The default policy is to not indent.
*/
public class WriterIndentationPolicy {
private int tableIndent = 0;
private int keyValueIndent = 0;
public int getTableIndent() {
return tableIndent;
}
/**
* Sets the number of spaces a nested table name is indented.
*
* @param tableIndent number of spaces to indent
* @return this WriterIndentationPolicy instance
*/
public WriterIndentationPolicy setTableIndent(int tableIndent) {
this.tableIndent = tableIndent;
return this;
}
public int getKeyValueIndent() {
return keyValueIndent;
}
/**
* Sets the number of spaces key/value pairs within a table are indented.
*
* @param keyValueIndent number of spaces to indent
* @return this WriterIndentationPolicy instance
*/
public WriterIndentationPolicy setKeyValueIndent(int keyValueIndent) {
this.keyValueIndent = keyValueIndent;
return this;
}
}

View file

@ -60,8 +60,6 @@ public class ValueWriterTest {
return dateString;
}
@Test
public void should_write_nested_map() {
class SubChild {
int anInt;
}
@ -75,6 +73,7 @@ public class ValueWriterTest {
boolean aBoolean;
}
private Parent buildNestedMap() {
Parent parent = new Parent();
parent.aMap = new LinkedHashMap<String, Object>();
parent.aMap.put("foo", 1);
@ -86,7 +85,29 @@ public class ValueWriterTest {
parent.child.subChild.anInt = 4;
parent.aBoolean = true;
String output = new TomlWriter().write(parent);
return parent;
}
@Test
public void should_write_nested_map() {
String output = new TomlWriter().write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
"foo = 1\n" +
"bar = \"value1\"\n" +
"\"baz.x\" = true\n\n" +
"[child]\n" +
"anInt = 2\n\n" +
"[child.subChild]\n" +
"anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_values() {
String output = new TomlWriter().
setIndentationPolicy(new WriterIndentationPolicy().setKeyValueIndent(2)).
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
" foo = 1\n" +
@ -99,6 +120,40 @@ public class ValueWriterTest {
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_tables() {
String output = new TomlWriter().
setIndentationPolicy(new WriterIndentationPolicy().setTableIndent(2)).
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
"foo = 1\n" +
"bar = \"value1\"\n" +
"\"baz.x\" = true\n\n" +
"[child]\n" +
"anInt = 2\n\n" +
" [child.subChild]\n" +
" anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_follow_indentation_policy_of_indented_tables_and_values() {
String output = new TomlWriter().
setIndentationPolicy(new WriterIndentationPolicy().setTableIndent(2).setKeyValueIndent(2)).
write(buildNestedMap());
String expected = "aBoolean = true\n\n" +
"[aMap]\n" +
" foo = 1\n" +
" bar = \"value1\"\n" +
" \"baz.x\" = true\n\n" +
"[child]\n" +
" anInt = 2\n\n" +
" [child.subChild]\n" +
" anInt = 4\n";
assertEquals(expected, output);
}
@Test
public void should_write_array_of_primitive() {
class ArrayTest {
@ -128,9 +183,9 @@ public class ValueWriterTest {
String output = new TomlWriter().write(config);
String expected = "[[table]]\n" +
" anInt = 1\n\n" +
"anInt = 1\n\n" +
"[[table]]\n" +
" anInt = 2\n";
"anInt = 2\n";
assertEquals(expected, output);
}
@ -179,7 +234,7 @@ public class ValueWriterTest {
B b = new B();
}
assertEquals("[b.c]\n anInt = 1\n", new TomlWriter().write(new A()));
assertEquals("[b.c]\nanInt = 1\n", new TomlWriter().write(new A()));
}
@Test
@ -221,23 +276,23 @@ public class ValueWriterTest {
basket.fruit[1].variety[0].name = "plantain";
String expected = "[[fruit]]\n" +
" name = \"apple\"\n" +
"name = \"apple\"\n" +
"\n" +
"[fruit.physical]\n" +
" color = \"red\"\n" +
" shape = \"round\"\n" +
"color = \"red\"\n" +
"shape = \"round\"\n" +
"\n" +
"[[fruit.variety]]\n" +
" name = \"red delicious\"\n" +
"name = \"red delicious\"\n" +
"\n" +
"[[fruit.variety]]\n" +
" name = \"granny smith\"\n" +
"name = \"granny smith\"\n" +
"\n" +
"[[fruit]]\n" +
" name = \"banana\"\n" +
"name = \"banana\"\n" +
"\n" +
"[[fruit.variety]]\n" +
" name = \"plantain\"" +
"name = \"plantain\"" +
"\n";