From c394fce27357a12edad588dbc93a27f4bd797169 Mon Sep 17 00:00:00 2001 From: Bradlee Speice Date: Tue, 11 Nov 2014 10:09:47 -0500 Subject: [PATCH] Use a delegate to make OsisParser cleaner --- .../minimalbible/test/FinalDelegateTest.java | 39 +++++++++++++++++++ .../format/osisparser/OsisParserTest.java | 2 +- .../service/book/VerseLookupService.java | 3 +- .../org/bspeice/minimalbible/FinalDelegate.kt | 24 ++++++++++++ .../service/format/osisparser/OsisParser.kt | 15 ++++--- .../service/format/osisparser/VerseContent.kt | 17 ++++---- 6 files changed, 85 insertions(+), 15 deletions(-) create mode 100644 app/src/androidTest/java/org/bspeice/minimalbible/test/FinalDelegateTest.java create mode 100644 app/src/main/kotlin/org/bspeice/minimalbible/FinalDelegate.kt diff --git a/app/src/androidTest/java/org/bspeice/minimalbible/test/FinalDelegateTest.java b/app/src/androidTest/java/org/bspeice/minimalbible/test/FinalDelegateTest.java new file mode 100644 index 0000000..a766a74 --- /dev/null +++ b/app/src/androidTest/java/org/bspeice/minimalbible/test/FinalDelegateTest.java @@ -0,0 +1,39 @@ +package org.bspeice.minimalbible.test; + +import org.bspeice.minimalbible.FinalDelegate; +import org.bspeice.minimalbible.MBTestCase; + +import kotlin.PropertyMetadataImpl; + +/** + * Test that the FinalDelegate actually obeys its contract + */ +public class FinalDelegateTest extends MBTestCase { + + FinalDelegate delegate; + + public void setUp() { + delegate = new FinalDelegate(); + } + + public void testDelegateNullSafety() { + try { + delegate.get(null, new PropertyMetadataImpl("")); + } catch (IllegalStateException e) { + return; + } + + fail("Exception not thrown!"); + } + + public void testDelegateAssignOnce() { + try { + delegate.set(null, new PropertyMetadataImpl(""), ""); + delegate.set(null, new PropertyMetadataImpl(""), ""); + } catch (IllegalStateException e) { + return; + } + + fail("Allowed to set twice!"); + } +} diff --git a/app/src/androidTest/java/org/bspeice/minimalbible/test/format/osisparser/OsisParserTest.java b/app/src/androidTest/java/org/bspeice/minimalbible/test/format/osisparser/OsisParserTest.java index d96d5e5..4a98d36 100644 --- a/app/src/androidTest/java/org/bspeice/minimalbible/test/format/osisparser/OsisParserTest.java +++ b/app/src/androidTest/java/org/bspeice/minimalbible/test/format/osisparser/OsisParserTest.java @@ -17,7 +17,7 @@ public class OsisParserTest extends MBTestCase { OsisParser parser; public void setUp() { - parser = new OsisParser(null); + parser = new OsisParser(); } @SuppressLint("NewApi") diff --git a/app/src/main/java/org/bspeice/minimalbible/service/book/VerseLookupService.java b/app/src/main/java/org/bspeice/minimalbible/service/book/VerseLookupService.java index c638938..86fbb3b 100644 --- a/app/src/main/java/org/bspeice/minimalbible/service/book/VerseLookupService.java +++ b/app/src/main/java/org/bspeice/minimalbible/service/book/VerseLookupService.java @@ -80,7 +80,8 @@ public class VerseLookupService implements Action1 { BookData bookData = new BookData(book, v); try { SAXEventProvider provider = bookData.getSAXEventProvider(); - OsisParser handler = new OsisParser(v); + OsisParser handler = new OsisParser(); + handler.setVerse(v); provider.provideSAXEvents(handler); return handler.getVerseContent().toJson(); } catch (BookException e) { diff --git a/app/src/main/kotlin/org/bspeice/minimalbible/FinalDelegate.kt b/app/src/main/kotlin/org/bspeice/minimalbible/FinalDelegate.kt new file mode 100644 index 0000000..d8c69dc --- /dev/null +++ b/app/src/main/kotlin/org/bspeice/minimalbible/FinalDelegate.kt @@ -0,0 +1,24 @@ +package org.bspeice.minimalbible + +/** + * The purpose of this delegate is to guarantee null-safety, while + * also ensuring a pseudo-val type. If you try to read before use, error. + * If you try to set multiple times, error. + */ + +public class FinalDelegate() { + private var value: T? = null + private var didAssign: Boolean = false + + public fun get(thisRef: Any?, desc: PropertyMetadata): T { + return value ?: throw IllegalStateException("Property ${desc.name} should be initialized before get") + } + + public fun set(thisRef: Any?, desc: PropertyMetadata, value: T) { + if (!didAssign) { + this.value = value + this.didAssign = true + } else + throw IllegalStateException("Property ${desc.name} should not be assigned multiple times") + } +} diff --git a/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/OsisParser.kt b/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/OsisParser.kt index da82cff..274d4cf 100644 --- a/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/OsisParser.kt +++ b/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/OsisParser.kt @@ -5,17 +5,20 @@ import org.crosswire.jsword.passage.Verse import java.util.ArrayDeque import org.xml.sax.Attributes import org.crosswire.jsword.book.OSISUtil +import org.bspeice.minimalbible.FinalDelegate /** * Created by bspeice on 9/10/14. */ -class OsisParser(v: Verse?) : DefaultHandler() { +class OsisParser() : DefaultHandler() { + + // Don't pass a verse as part of the constructor, but still guarantee + // that it will exist + public var verse: Verse by FinalDelegate() + val verseContent: VerseContent + get() = VerseContent(verse) - val verseContent: VerseContent? = when (v) { - is Verse -> VerseContent(v) // not null - else -> null - } // TODO: Implement a stack to keep min API 8 val doWrite = ArrayDeque() @@ -33,6 +36,6 @@ class OsisParser(v: Verse?) : DefaultHandler() { override fun characters(ch: CharArray, start: Int, length: Int) { if (doWrite.peek()) - verseContent?.appendContent(String(ch)) + verseContent.appendContent(String(ch)) } } \ No newline at end of file diff --git a/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/VerseContent.kt b/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/VerseContent.kt index e3e6b07..5b67a78 100644 --- a/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/VerseContent.kt +++ b/app/src/main/kotlin/org/bspeice/minimalbible/service/format/osisparser/VerseContent.kt @@ -9,14 +9,17 @@ import java.util.ArrayList //TODO: JSON Streaming parsing? http://instagram-engineering.tumblr.com/post/97147584853/json-parsing class VerseContent(v: Verse) { - var id = v.getOrdinal() - var bookName = v.getName() - var chapter = v.getChapter() - var verseNum = v.getVerse() + val id = v.getOrdinal() + val bookName = v.getName() + val chapter = v.getChapter() + val verseNum = v.getVerse() + val chapterTitle = "" + val paraTitle = "" + val references: MutableList = ArrayList() var content = "" - var chapterTitle = "" - var paraTitle = "" - var references: MutableList = ArrayList() + + public val json: String + get() = Gson().toJson(this) public fun toJson(): String { // Lazy load Gson - not likely that we'll call this method multiple times, so