diff --git a/src/main/kotlin/com/marvinelsen/willow/Model.kt b/src/main/kotlin/com/marvinelsen/willow/Model.kt index a8e4fa2..d483ea5 100644 --- a/src/main/kotlin/com/marvinelsen/willow/Model.kt +++ b/src/main/kotlin/com/marvinelsen/willow/Model.kt @@ -1,28 +1,25 @@ package com.marvinelsen.willow import com.marvinelsen.willow.domain.SearchMode +import com.marvinelsen.willow.domain.SqliteDictionary import com.marvinelsen.willow.ui.DictionaryEntryFx import com.marvinelsen.willow.ui.SentenceFx -import com.marvinelsen.willow.ui.services.FindCharacterService -import com.marvinelsen.willow.ui.services.FindSentencesService -import com.marvinelsen.willow.ui.services.FindWordsBeginningService -import com.marvinelsen.willow.ui.services.FindWordsContainingService -import com.marvinelsen.willow.ui.services.SearchService +import com.marvinelsen.willow.ui.toDomain +import com.marvinelsen.willow.ui.toFx import com.marvinelsen.willow.ui.util.ClipboardHelper +import javafx.beans.property.BooleanProperty import javafx.beans.property.ObjectProperty import javafx.beans.property.ReadOnlyBooleanProperty import javafx.beans.property.ReadOnlyObjectProperty +import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleObjectProperty import javafx.collections.FXCollections import javafx.collections.ObservableList -import javafx.event.EventHandler +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch class Model( - private val searchService: SearchService, - private val findWordsBeginningService: FindWordsBeginningService, - private val findWordsContainingService: FindWordsContainingService, - private val findCharacterService: FindCharacterService, - private val findSentencesService: FindSentencesService, + private val dictionary: SqliteDictionary, ) { private val internalSelectedEntry: ObjectProperty = SimpleObjectProperty() private val internalSearchResults: ObservableList = FXCollections.observableArrayList() @@ -31,6 +28,12 @@ class Model( private val internalCharacters: ObservableList = FXCollections.observableArrayList() private val internalSentences: ObservableList = FXCollections.observableArrayList() + private val internalIsSearching: BooleanProperty = SimpleBooleanProperty(false) + private val internalIsFindingWordsBeginning: BooleanProperty = SimpleBooleanProperty(false) + private val internalIsFindingWordsContaining: BooleanProperty = SimpleBooleanProperty(false) + private val internalIsFindingCharacters: BooleanProperty = SimpleBooleanProperty(false) + private val internalIsFindingSentences: BooleanProperty = SimpleBooleanProperty(false) + val selectedEntry: ReadOnlyObjectProperty = internalSelectedEntry val searchResults: ObservableList = @@ -44,54 +47,68 @@ class Model( val sentences: ObservableList = FXCollections.unmodifiableObservableList(internalSentences) - val isSearching: ReadOnlyBooleanProperty = searchService.runningProperty() - val isFindingWordsBeginning: ReadOnlyBooleanProperty = findWordsBeginningService.runningProperty() - val isFindingWordsContaining: ReadOnlyBooleanProperty = findWordsContainingService.runningProperty() - val isFindingCharacters: ReadOnlyBooleanProperty = findCharacterService.runningProperty() - val isFindingSentences: ReadOnlyBooleanProperty = findSentencesService.runningProperty() + val isSearching: ReadOnlyBooleanProperty = internalIsSearching + val isFindingWordsBeginning: ReadOnlyBooleanProperty = internalIsFindingWordsBeginning + val isFindingWordsContaining: ReadOnlyBooleanProperty = internalIsFindingWordsContaining + val isFindingCharacters: ReadOnlyBooleanProperty = internalIsFindingCharacters + val isFindingSentences: ReadOnlyBooleanProperty = internalIsFindingSentences - init { - searchService.onSucceeded = EventHandler { - internalSearchResults.setAll(searchService.value) - } - findWordsBeginningService.onSucceeded = EventHandler { - internalWordsBeginning.setAll(findWordsBeginningService.value) - } - findWordsContainingService.onSucceeded = EventHandler { - internalWordsContaining.setAll(findWordsContainingService.value) - } - findCharacterService.onSucceeded = EventHandler { - internalCharacters.setAll(findCharacterService.value) - } - findSentencesService.onSucceeded = EventHandler { - internalSentences.setAll(findSentencesService.value) - } - } + private val coroutineScope = MainScope() fun search(query: String, searchMode: SearchMode) { - searchService.searchQuery = query - searchService.searchMode = searchMode - searchService.restart() + coroutineScope.launch { + internalIsSearching.value = true + internalSearchResults.setAll(dictionary.search(query, searchMode).map { it.toFx() }) + internalIsSearching.value = false + } } fun findWordsBeginning() { - findWordsBeginningService.entry = internalSelectedEntry.value - findWordsBeginningService.restart() + coroutineScope.launch { + internalIsFindingWordsBeginning.value = true + internalWordsBeginning.setAll( + dictionary + .findWordsBeginningWith(internalSelectedEntry.value.toDomain()) + .map { it.toFx() } + ) + internalIsFindingWordsBeginning.value = false + } } fun findWordsContaining() { - findWordsContainingService.entry = internalSelectedEntry.value - findWordsContainingService.restart() + coroutineScope.launch { + internalIsFindingWordsContaining.value = true + internalWordsContaining.setAll( + dictionary + .findWordsContaining(internalSelectedEntry.value.toDomain()) + .map { it.toFx() } + ) + internalIsFindingWordsContaining.value = false + } } fun findCharacters() { - findCharacterService.entry = internalSelectedEntry.value - findCharacterService.restart() + coroutineScope.launch { + internalIsFindingCharacters.value = true + internalCharacters.setAll( + dictionary + .findCharactersOf(internalSelectedEntry.value.toDomain()) + .map { it.toFx() } + ) + internalIsFindingCharacters.value = false + } } fun findSentences() { - findSentencesService.entry = internalSelectedEntry.value - findSentencesService.restart() + coroutineScope.launch { + internalIsFindingSentences.value = true + internalSentences.setAll( + dictionary + .findExampleSentencesFor(internalSelectedEntry.value.toDomain()) + .map { it.toFx() } + ) + internalIsFindingSentences.value = false + } } fun selectEntry(entry: DictionaryEntryFx) { diff --git a/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt b/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt index a9a5c18..ed60b62 100644 --- a/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt +++ b/src/main/kotlin/com/marvinelsen/willow/WillowApplication.kt @@ -7,11 +7,6 @@ import com.marvinelsen.willow.ui.controllers.MainController import com.marvinelsen.willow.ui.controllers.MenuController import com.marvinelsen.willow.ui.controllers.SearchController import com.marvinelsen.willow.ui.controllers.SearchResultsController -import com.marvinelsen.willow.ui.services.FindCharacterService -import com.marvinelsen.willow.ui.services.FindSentencesService -import com.marvinelsen.willow.ui.services.FindWordsBeginningService -import com.marvinelsen.willow.ui.services.FindWordsContainingService -import com.marvinelsen.willow.ui.services.SearchService import javafx.application.Application import javafx.fxml.FXMLLoader import javafx.scene.Scene @@ -45,17 +40,8 @@ class WillowApplication : Application() { autoCommit = false } val dictionary = SqliteDictionary(connection) - val searchService = SearchService(dictionary) - val findWordsBeginningService = FindWordsBeginningService(dictionary) - val findWordsContainingService = FindWordsContainingService(dictionary) - val findCharacterService = FindCharacterService(dictionary) - val findSentenceService = FindSentencesService(dictionary) val model = Model( - searchService, - findWordsBeginningService, - findWordsContainingService, - findCharacterService, - findSentenceService + dictionary, ) val config = Config() config.load() diff --git a/src/main/kotlin/com/marvinelsen/willow/domain/Dictionary.kt b/src/main/kotlin/com/marvinelsen/willow/domain/Dictionary.kt index 16342cd..f56dc5c 100644 --- a/src/main/kotlin/com/marvinelsen/willow/domain/Dictionary.kt +++ b/src/main/kotlin/com/marvinelsen/willow/domain/Dictionary.kt @@ -1,12 +1,12 @@ package com.marvinelsen.willow.domain interface Dictionary { - fun search(query: String, searchMode: SearchMode): List + suspend fun search(query: String, searchMode: SearchMode): List - fun findWordsBeginningWith(entry: DictionaryEntry): List - fun findWordsContaining(entry: DictionaryEntry): List + suspend fun findWordsBeginningWith(entry: DictionaryEntry): List + suspend fun findWordsContaining(entry: DictionaryEntry): List - fun findCharactersOf(entry: DictionaryEntry): List + suspend fun findCharactersOf(entry: DictionaryEntry): List - fun findExampleSentencesFor(entry: DictionaryEntry): List + suspend fun findExampleSentencesFor(entry: DictionaryEntry): List } diff --git a/src/main/kotlin/com/marvinelsen/willow/domain/SqliteDictionary.kt b/src/main/kotlin/com/marvinelsen/willow/domain/SqliteDictionary.kt index 5c3f97b..c115e37 100644 --- a/src/main/kotlin/com/marvinelsen/willow/domain/SqliteDictionary.kt +++ b/src/main/kotlin/com/marvinelsen/willow/domain/SqliteDictionary.kt @@ -8,14 +8,18 @@ import com.github.houbb.segment.support.segment.impl.Segments import com.github.houbb.segment.support.segment.mode.impl.SegmentModes import com.github.houbb.segment.support.segment.result.impl.SegmentResultHandlers import com.github.houbb.segment.support.tagging.pos.tag.impl.SegmentPosTaggings +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import java.sql.Connection -import java.sql.PreparedStatement import java.sql.ResultSet class SqliteDictionary(private val connection: Connection) : Dictionary { private val whitespaceRegex = """\s+""".toRegex() + private val dispatcher = Dispatchers.IO + .limitedParallelism(1) + private val segmentBs = SegmentBs.newInstance() .segment(Segments.defaults()) .segmentData(SegmentPhraseDatas.define()) @@ -24,41 +28,29 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { .posTagging(SegmentPosTaggings.simple()) .posData(SegmentPosDatas.define()) - private val searchSimplifiedPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val searchSimplifiedSql = """ SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry WHERE simplified GLOB ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - private val searchTraditionalPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val searchTraditionalSql = """ SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry WHERE traditional GLOB ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - private val searchPinyinPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val searchPinyinSql = """ SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry WHERE searchable_pinyin GLOB ? OR searchable_pinyin_with_tone_numbers GLOB ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - private val searchSegmentsRawSql = """ + private val searchSegmentsSql = """ WITH cte(id, segment) AS (VALUES ?) SELECT entry.traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry INNER JOIN cte @@ -66,29 +58,21 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { ORDER BY cte.id """.trimIndent() - private val findWordsBeginningPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val findWordsBeginningSql = """ SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry WHERE traditional GLOB ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - private val findWordsContainingPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val findWordsContainingSql = """ SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry WHERE traditional LIKE ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - private val findCharactersRawSql = """ + private val findCharactersSql = """ WITH cte(id, character, pinyin) AS (VALUES ?) SELECT traditional, simplified, pinyin_with_tone_marks, pinyin_with_tone_numbers, zhuyin, cedict_definitions, cross_straits_definitions, moe_definitions FROM entry INNER JOIN cte @@ -97,48 +81,50 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { ORDER BY cte.id """.trimIndent() - private val findExampleSentencesPreparedStatement: PreparedStatement by lazy { - connection.prepareStatement( - """ + private val findExampleSentenceSql = """ SELECT traditional, simplified FROM sentence WHERE traditional LIKE ? ORDER BY character_count ASC - """.trimIndent() - ) - } + """.trimIndent() - override fun search(query: String, searchMode: SearchMode) = when (searchMode) { - SearchMode.SIMPLIFIED -> searchSimplified(query) - SearchMode.TRADITIONAL -> searchTraditional(query) - SearchMode.PINYIN -> searchPinyin(query) - SearchMode.SEGMENTS -> searchSegmentsOf(query) - } - - private fun searchSimplified(simplified: String): List { - searchSimplifiedPreparedStatement.setString(1, "$simplified*") - - return searchSimplifiedPreparedStatement.executeQuery().use { - it.toListOfDictionaryEntries() + override suspend fun search(query: String, searchMode: SearchMode) = withContext(dispatcher) { + when (searchMode) { + SearchMode.SIMPLIFIED -> searchSimplified(query) + SearchMode.TRADITIONAL -> searchTraditional(query) + SearchMode.PINYIN -> searchPinyin(query) + SearchMode.SEGMENTS -> searchSegmentsOf(query) } } - private fun searchTraditional(traditional: String): List { - searchTraditionalPreparedStatement.setString(1, "$traditional*") + private fun searchSimplified(simplified: String) = + connection.prepareStatement(searchSimplifiedSql).use { preparedStatement -> + preparedStatement.setString(1, "$simplified*") - return searchTraditionalPreparedStatement.executeQuery().use { - it.toListOfDictionaryEntries() + preparedStatement.executeQuery().use { + it.toListOfDictionaryEntries() + } + } + + private fun searchTraditional(traditional: String) = + connection.prepareStatement(searchTraditionalSql).use { preparedStatement -> + preparedStatement.setString(1, "$traditional*") + + preparedStatement.executeQuery().use { + it.toListOfDictionaryEntries() + } } - } private fun searchPinyin(pinyin: String): List { val sanitizedPinyin = pinyin.lowercase().replace(whitespaceRegex, "") - searchPinyinPreparedStatement.setString(1, "$sanitizedPinyin*") - searchPinyinPreparedStatement.setString(2, "$sanitizedPinyin*") + return connection.prepareStatement(searchPinyinSql).use { preparedStatement -> + preparedStatement.setString(1, "$sanitizedPinyin*") + preparedStatement.setString(2, "$sanitizedPinyin*") - return searchPinyinPreparedStatement.executeQuery().use { resultSet -> - resultSet.toListOfDictionaryEntries() + preparedStatement.executeQuery().use { resultSet -> + resultSet.toListOfDictionaryEntries() + } } } @@ -149,7 +135,7 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { .mapIndexed { index, segment -> "($index, '$segment')" } .joinToString(",") - val query = searchSegmentsRawSql.replace("?", segmentsListString) + val query = searchSegmentsSql.replace("?", segmentsListString) return connection.createStatement().use { statement -> statement.executeQuery(query).use { resultSet -> @@ -158,23 +144,27 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { } } - override fun findWordsContaining(entry: DictionaryEntry): List { - findWordsContainingPreparedStatement.setString(1, "_%${entry.traditional}%") + override suspend fun findWordsContaining(entry: DictionaryEntry) = withContext(dispatcher) { + connection.prepareStatement(findWordsContainingSql).use { preparedStatement -> + preparedStatement.setString(1, "_%${entry.traditional}%") - return findWordsContainingPreparedStatement.executeQuery().use { resultSet -> - resultSet.toListOfDictionaryEntries() + preparedStatement.executeQuery().use { resultSet -> + resultSet.toListOfDictionaryEntries() + } } } - override fun findWordsBeginningWith(entry: DictionaryEntry): List { - findWordsBeginningPreparedStatement.setString(1, "${entry.traditional}?*") + override suspend fun findWordsBeginningWith(entry: DictionaryEntry) = withContext(dispatcher) { + connection.prepareStatement(findWordsBeginningSql).use { preparedStatement -> + preparedStatement.setString(1, "${entry.traditional}?*") - return findWordsBeginningPreparedStatement.executeQuery().use { resultSet -> - resultSet.toListOfDictionaryEntries() + preparedStatement.executeQuery().use { resultSet -> + resultSet.toListOfDictionaryEntries() + } } } - override fun findCharactersOf(entry: DictionaryEntry): List { + override suspend fun findCharactersOf(entry: DictionaryEntry) = withContext(dispatcher) { val pinyinSyllablesWithToneNumbers = entry.pinyinWithToneNumbers .lowercase() .split(" ") @@ -192,20 +182,22 @@ class SqliteDictionary(private val connection: Connection) : Dictionary { .mapIndexed { index, s -> "($index, '${s.first}', '${s.second}')" } .joinToString(",") - val query = findCharactersRawSql.replace("?", queryInput) + val query = findCharactersSql.replace("?", queryInput) - return connection.createStatement().use { statement -> + connection.createStatement().use { statement -> statement.executeQuery(query).use { resultSet -> resultSet.toListOfDictionaryEntries() } } } - override fun findExampleSentencesFor(entry: DictionaryEntry): List { - findExampleSentencesPreparedStatement.setString(1, "_%${entry.traditional}%") + override suspend fun findExampleSentencesFor(entry: DictionaryEntry) = withContext(dispatcher) { + connection.prepareStatement(findExampleSentenceSql).use { preparedStatement -> + preparedStatement.setString(1, "_%${entry.traditional}%") - return findExampleSentencesPreparedStatement.executeQuery().use { resultSet -> - resultSet.toListOfSentences() + preparedStatement.executeQuery().use { resultSet -> + resultSet.toListOfSentences() + } } } } diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindCharacterService.kt b/src/main/kotlin/com/marvinelsen/willow/ui/services/FindCharacterService.kt deleted file mode 100644 index bfde52b..0000000 --- a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindCharacterService.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.marvinelsen.willow.ui.services - -import com.marvinelsen.willow.domain.Dictionary -import com.marvinelsen.willow.ui.DictionaryEntryFx -import com.marvinelsen.willow.ui.toDomain -import com.marvinelsen.willow.ui.toFx -import com.marvinelsen.willow.ui.util.task -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import javafx.concurrent.Service - -class FindCharacterService(private val dictionary: Dictionary) : Service>() { - lateinit var entry: DictionaryEntryFx - - override fun createTask() = task { - if (!this::entry.isInitialized) error("Entry is not initialized") - - FXCollections.observableList(dictionary.findCharactersOf(entry.toDomain()).map { it.toFx() }) - } -} diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindSentencesService.kt b/src/main/kotlin/com/marvinelsen/willow/ui/services/FindSentencesService.kt deleted file mode 100644 index e2f7812..0000000 --- a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindSentencesService.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.marvinelsen.willow.ui.services - -import com.marvinelsen.willow.domain.Dictionary -import com.marvinelsen.willow.ui.DictionaryEntryFx -import com.marvinelsen.willow.ui.SentenceFx -import com.marvinelsen.willow.ui.toDomain -import com.marvinelsen.willow.ui.toFx -import com.marvinelsen.willow.ui.util.task -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import javafx.concurrent.Service - -class FindSentencesService(private val dictionary: Dictionary) : Service>() { - lateinit var entry: DictionaryEntryFx - - override fun createTask() = task { - if (!this::entry.isInitialized) error("Entry is not initialized") - - FXCollections.observableList(dictionary.findExampleSentencesFor(entry.toDomain()).map { it.toFx() }) - } -} diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsBeginningService.kt b/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsBeginningService.kt deleted file mode 100644 index 0f25f23..0000000 --- a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsBeginningService.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.marvinelsen.willow.ui.services - -import com.marvinelsen.willow.domain.Dictionary -import com.marvinelsen.willow.ui.DictionaryEntryFx -import com.marvinelsen.willow.ui.toDomain -import com.marvinelsen.willow.ui.toFx -import com.marvinelsen.willow.ui.util.task -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import javafx.concurrent.Service - -class FindWordsBeginningService(private val dictionary: Dictionary) : Service>() { - lateinit var entry: DictionaryEntryFx - - override fun createTask() = task { - if (!this::entry.isInitialized) error("Entry is not initialized") - - FXCollections.observableList(dictionary.findWordsBeginningWith(entry.toDomain()).map { it.toFx() }) - } -} diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsContainingService.kt b/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsContainingService.kt deleted file mode 100644 index a80e8fe..0000000 --- a/src/main/kotlin/com/marvinelsen/willow/ui/services/FindWordsContainingService.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.marvinelsen.willow.ui.services - -import com.marvinelsen.willow.domain.Dictionary -import com.marvinelsen.willow.ui.DictionaryEntryFx -import com.marvinelsen.willow.ui.toDomain -import com.marvinelsen.willow.ui.toFx -import com.marvinelsen.willow.ui.util.task -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import javafx.concurrent.Service - -class FindWordsContainingService(private val dictionary: Dictionary) : Service>() { - lateinit var entry: DictionaryEntryFx - - override fun createTask() = task { - if (!this::entry.isInitialized) error("Entry is not initialized") - - FXCollections.observableList(dictionary.findWordsContaining(entry.toDomain()).map { it.toFx() }) - } -} diff --git a/src/main/kotlin/com/marvinelsen/willow/ui/services/SearchService.kt b/src/main/kotlin/com/marvinelsen/willow/ui/services/SearchService.kt deleted file mode 100644 index dbc3166..0000000 --- a/src/main/kotlin/com/marvinelsen/willow/ui/services/SearchService.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.marvinelsen.willow.ui.services - -import com.marvinelsen.willow.domain.Dictionary -import com.marvinelsen.willow.domain.SearchMode -import com.marvinelsen.willow.ui.DictionaryEntryFx -import com.marvinelsen.willow.ui.toFx -import com.marvinelsen.willow.ui.util.task -import javafx.collections.FXCollections -import javafx.collections.ObservableList -import javafx.concurrent.Service - -class SearchService(private val dictionary: Dictionary) : Service>() { - lateinit var searchQuery: String - lateinit var searchMode: SearchMode - - override fun createTask() = task { - if (!this::searchQuery.isInitialized) error("Search query is not initialized") - if (!this::searchMode.isInitialized) error("Search mode is not initialized") - - FXCollections.observableList(dictionary.search(searchQuery, searchMode).map { it.toFx() }) - } -}