Created
March 23, 2013 01:33
-
-
Save i-takehiro/5225990 to your computer and use it in GitHub Desktop.
TDDBC Tokyo 2013-03 のお題にチャレンジしてみた (お題の参考)https://github.com/sue445/tddbc_tokyo_20130316
⇒お題の1枚目、2枚目まで実装
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ltsv; | |
import java.util.LinkedHashMap; | |
import java.util.Map; | |
/** | |
* TDDBC Tokyo 2013-03 | |
* お題 (1枚目、2枚目まで実装) | |
* https://github.com/sue445/tddbc_tokyo_20130316 | |
*/ | |
public class LTSV { | |
private static final char NEWLINE = '\n'; | |
private static final char TAB = '\t'; | |
private static final char COLON = ':'; | |
private static final String EMPTY_LINE = "" + NEWLINE; | |
private final LinkedHashMap<String, String> values; | |
public LTSV() { | |
// 挿入順を保持するため LinkedHashMap を使用する | |
this.values = new LinkedHashMap<String, String>(); | |
} | |
public String dump() { | |
if (this.values.isEmpty()) { | |
return EMPTY_LINE; | |
} | |
final StringBuilder builder = new StringBuilder(); | |
boolean isFirst = true; | |
for (Map.Entry<String, String> entry : values.entrySet()) { | |
if (isFirst == false) { | |
builder.append(TAB); | |
} | |
final String key = entry.getKey(); | |
final String value = entry.getValue(); | |
builder.append(escape(key)).append(COLON).append(escape(value)); | |
isFirst = false; | |
} | |
builder.append(NEWLINE); | |
return builder.toString(); | |
} | |
public String get(final String key) { | |
isNotNull(key, "key must not be null."); | |
isNotEmpty(key, "key must not be empty."); | |
return this.values.get(key); | |
} | |
public String set(final String key, final String value) { | |
isNotNull(key, "key must not be null."); | |
isNotEmpty(key, "key must not be empty."); | |
isNotNull(value, "value must not be null."); | |
// キーをマップに「再挿入」する場合は挿入順に影響を受けないので、明示的に削除と挿入を行う | |
final String oldValue = this.values.remove(key); | |
this.values.put(key, value); | |
return oldValue; | |
} | |
private String escape(final String value) { | |
if (value == null || value.length() == 0) { | |
return value; | |
} | |
final int length = value.length(); | |
final StringBuilder escaped = new StringBuilder(length * 2); | |
for (int i = 0; i < length; i++) { | |
char c = value.charAt(i); | |
switch (c) { | |
case COLON: | |
escaped.append("\\:"); | |
break; | |
case TAB: | |
escaped.append("\\t"); | |
break; | |
case NEWLINE: | |
escaped.append("\\n"); | |
break; | |
default: | |
escaped.append(c); | |
break; | |
} | |
} | |
return escaped.toString(); | |
} | |
private <T> void isNotNull(final T object, final String message) { | |
isTrue(object != null, message); | |
} | |
private void isNotEmpty(final CharSequence chars, final String message) { | |
isTrue(0 < chars.length(), message); | |
} | |
private void isTrue(final boolean condition, final String message) { | |
if (condition == false) { | |
throw new IllegalArgumentException(message); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ltsv; | |
import static org.hamcrest.CoreMatchers.is; | |
import static org.hamcrest.CoreMatchers.nullValue; | |
import static org.junit.Assert.assertThat; | |
import org.junit.Before; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.junit.experimental.runners.Enclosed; | |
import org.junit.rules.ExpectedException; | |
import org.junit.runner.RunWith; | |
@RunWith(Enclosed.class) | |
public class LTSVTest { | |
public static class getの例外条件のテスト { | |
private LTSV sut; | |
@Rule | |
public ExpectedException expected = ExpectedException.none(); | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
expected.expect(IllegalArgumentException.class); | |
} | |
@Test | |
public void nullキーは例外発生() throws Exception { | |
// SetUp | |
// Exercise | |
sut.get(null); | |
// Verify | |
} | |
@Test | |
public void 空文字キーは例外発生() throws Exception { | |
// SetUp | |
// Exercise | |
sut.get(""); | |
// Verify | |
} | |
} | |
public static class setの例外条件のテスト { | |
// ltsv.set( nil , "momo" ) # nullキーは例外発生 | |
// ltsv.set( "" , "gogo" ) # 空文字キーは例外発生 | |
// ltsv.set( "key", null ) # null値は例外発生 | |
// ltsv.set( "key", "" ) # 空文字値はOK -> dumpは? | |
private LTSV sut; | |
@Rule | |
public ExpectedException expected = ExpectedException.none(); | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
} | |
@Test | |
public void nullキーは例外発生() throws Exception { | |
// SetUp | |
expected.expect(IllegalArgumentException.class); | |
// Exercise | |
sut.set(null, "momo"); | |
// Verify | |
} | |
@Test | |
public void 空文字列キーは例外発生() throws Exception { | |
// SetUp | |
expected.expect(IllegalArgumentException.class); | |
// Exercise | |
sut.set("", "gogo"); | |
// Verify | |
} | |
@Test | |
public void null値は例外発生() throws Exception { | |
// SetUp | |
expected.expect(IllegalArgumentException.class); | |
// Exercise | |
sut.set("key", null); | |
// Verify | |
} | |
@Test | |
public void 空文字値はOK() throws Exception { | |
// SetUp | |
// Exercise | |
sut.set("key", ""); | |
// Verify | |
} | |
} | |
public static class データが存在しない { | |
private LTSV sut; | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
} | |
@Test | |
public void dumpすると改行のみが返る() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("\n")); | |
} | |
@Test | |
public void 存在しないキーでsetするとnullが返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.set("foo", "hoge"), nullValue()); | |
} | |
@Test | |
public void 存在しないキーの値はnull() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.get("toto"), nullValue()); | |
} | |
} | |
public static class データが1つsetされている { | |
private LTSV sut; | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
sut.set("foo", "hoge"); | |
} | |
@Test | |
public void set済みのキーでgetすると値が返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.get("foo"), is("hoge")); | |
} | |
@Test | |
public void dump() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:hoge\n")); | |
} | |
@Test | |
public void 新しいキーでsetするとnullが返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.set("bar", "fuga"), nullValue()); | |
} | |
} | |
public static class データが2つsetされている { | |
private LTSV sut; | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
sut.set("foo", "hoge"); | |
sut.set("bar", "fuga"); | |
} | |
@Test | |
public void fooでgetするとhogeが返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.get("foo"), is("hoge")); | |
} | |
@Test | |
public void barでgetするとfugaが返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
// (コメント) お題の1枚目に以下のコードもあると良い?(getの返り値がキーの値に関係なく最後にsetした値と誤解する?) | |
// ltsv.get( "bar" ) #=> fuga | |
assertThat(sut.get("bar"), is("fuga")); | |
} | |
@Test | |
public void 格納順にdumpされる() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:hoge\tbar:fuga\n")); | |
} | |
@Test | |
public void 設定済みのキーでsetすると設定済みの値が返される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.set("foo", "piyo"), is("hoge")); | |
} | |
} | |
public static class 同じキーで2回以上値がセットされている場合 { | |
private LTSV sut; | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
sut.set("foo", "hoge"); | |
sut.set("bar", "fuga"); | |
sut.set("foo", "piyo"); | |
} | |
@Test | |
public void 後からsetした値が取得される() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.get("foo"), is("piyo")); | |
} | |
@Test | |
public void 格納順にdumpされる() throws Exception { | |
// SetUp | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("bar:fuga\tfoo:piyo\n")); | |
} | |
} | |
public static class データに特殊文字が含まれる場合のdump { | |
private LTSV sut; | |
@Before | |
public void setUp() throws Exception { | |
sut = new LTSV(); | |
} | |
@Test | |
public void キーにコロンが含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("key1:key2", "value"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("key1\\:key2:value\n")); | |
} | |
@Test | |
public void キーにタブが含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("key1\tkey2", "value"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("key1\\tkey2:value\n")); | |
} | |
@Test | |
public void キーに改行が含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("key1\nkey2", "value"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("key1\\nkey2:value\n")); | |
} | |
@Test | |
public void キーにコロン_タブ_改行が混在したときに全てエスケープ() throws Exception { | |
// SetUp | |
sut.set("key1\tkey2\nkey3:key4", "value"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("key1\\tkey2\\nkey3\\:key4:value\n")); | |
} | |
@Test | |
public void 値が空文字列の場合は空で出力() throws Exception { | |
// SetUp | |
sut.set("foo", ""); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:\n")); | |
} | |
@Test | |
public void 値にコロンが含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("foo", "value1:value2"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:value1\\:value2\n")); | |
} | |
@Test | |
public void 値にタブが含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("foo", "value1\tvalue2"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:value1\\tvalue2\n")); | |
} | |
@Test | |
public void 値に改行が含まれる場合バックスラッシュでエスケープ() throws Exception { | |
// SetUp | |
sut.set("foo", "value1\nvalue2"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:value1\\nvalue2\n")); | |
} | |
@Test | |
public void 値にコロン_タブ_改行が混在したときに全てエスケープ() throws Exception { | |
// SetUp | |
sut.set("foo", "aaa\nbbb\tccc:ddd"); | |
// Exercise | |
// Verify | |
assertThat(sut.dump(), is("foo:aaa\\nbbb\\tccc\\:ddd\n")); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment