11package fr.berliat.hskwidget.data.store
22
33import android.content.Context
4+ import android.database.Cursor
45import android.util.Log
5- import com.opencsv.CSVReader
66import fr.berliat.hskwidget.data.model.ChineseWord
77import java.util.Locale
88
9- class ChineseWordsStore private constructor(val context : Context ) {
10- private val fullDict = mutableListOf<ChineseWord >()
119
12- init {
13- Log .i(" ChineseWordsStore" , " A new dictionary is being loaded" )
14- ChineseWord .HSK_Level .values().forEach {
15- addFromCSVResource(it, getHSKFile(it))
16- }
10+ class ChineseWordsStore private constructor(val context : Context ) {
11+ private val dbHelper = ChineseWordsDBHelper (context)
12+ private val database = dbHelper.readableDatabase
13+
14+ private val projection = arrayOf(
15+ ChineseWordsDBHelper .SIMPLIFIED ,
16+ ChineseWordsDBHelper .PINYINS ,
17+ ChineseWordsDBHelper .HSK ,
18+ ChineseWordsDBHelper .DEFINITION_EN
19+ )
20+
21+ fun getOnlyHSKLevels (levels : Set <ChineseWord .HSK_Level >): Array <ChineseWord > {
22+ return _getOnlyHSKLevels (levels, arrayListOf (), " " , " " )
1723 }
1824
19- fun getOnlyHSKLevels (levels : Set <ChineseWord .HSK_Level >) : List <ChineseWord > {
20- return fullDict.filter {
21- levels.contains(it.HSK )
25+ private fun _getOnlyHSKLevels (
26+ levels : Set <ChineseWord .HSK_Level >, bannedWord : ArrayList <ChineseWord >,
27+ orderBy : String , limit : String
28+ ): Array <ChineseWord > {
29+ // Filter results WHERE "title" = 'My Title'
30+ val sel =
31+ " ${ChineseWordsDBHelper .HSK } IN (" + levels.map { it.level }.joinToString() + " ) " +
32+ " AND ${ChineseWordsDBHelper .SIMPLIFIED } NOT IN (?)"
33+
34+ val cursor = database.query(
35+ ChineseWordsDBHelper .TABLE_NAME , // The table to query
36+ projection, // The array of columns to return (pass null to get all)
37+ sel, // The columns for the WHERE clause
38+ bannedWord.map { it.simplified }.toTypedArray(), // The values for the WHERE clause
39+ null , // don't group the rows
40+ null , // don't filter by row groups
41+ orderBy,
42+ limit
43+ )
44+
45+ val dict = mutableSetOf<ChineseWord >()
46+ with (cursor) {
47+ while (moveToNext()) {
48+ dict.add(cursorToWord(cursor))
49+ }
2250 }
51+ cursor.close()
52+
53+ return dict.toTypedArray()
2354 }
2455
2556 fun getRandomWord (
2657 levels : Set <ChineseWord .HSK_Level >,
2758 bannedWords : ArrayList <ChineseWord >
2859 ) : ChineseWord ? {
29- val dict = getOnlyHSKLevels (levels)
30- if (dict.isEmpty() || dict.size == bannedWords.size ) return null
60+ val dict = _getOnlyHSKLevels (levels, bannedWords, " RANDOM() " , " 1 " )
61+ if (dict.isEmpty()) return null
3162
32- var word : ChineseWord
63+ var word: ChineseWord
3364 do {
3465 word = dict.random()
3566 } while (bannedWords.contains(word))
@@ -39,47 +70,60 @@ class ChineseWordsStore private constructor(val context: Context) {
3970 }
4071
4172 fun findWordFromSimplified (simplifiedWord : String? ): ChineseWord ? {
42- val word = fullDict.filter {
43- it.simplified == simplifiedWord
44- }
45-
46- if (word.isEmpty())
73+ // Filter results WHERE "title" = 'My Title'
74+ val simpSel = " ${ChineseWordsDBHelper .SIMPLIFIED } IN (?)"
75+
76+ val cursor = database.query(
77+ ChineseWordsDBHelper .TABLE_NAME , // The table to query
78+ projection, // The array of columns to return (pass null to get all)
79+ simpSel, // The columns for the WHERE clause
80+ arrayOf(simplifiedWord), // The values for the WHERE clause
81+ null , // don't group the rows
82+ null , // don't filter by row groups
83+ " "
84+ )
85+
86+ if (! cursor.moveToNext())
4787 return null
4888
49- return word.first()
89+ val word = cursorToWord(cursor)
90+ cursor.close()
91+
92+ return word
5093 }
5194
52- private fun addFromCSVResource (hsk : ChineseWord .HSK_Level , reader : CSVReader ) {
53- var nextLine: Array <String >?
54- nextLine = reader.readNext()
55- while (nextLine != null ) {
56- fullDict.add(
57- ChineseWord (
58- nextLine[0 ],
59- " " ,
60- mapOf (Locale .ENGLISH to nextLine[2 ]),
61- hsk,
62- ChineseWord .Pinyins (nextLine[1 ])
95+ private fun cursorToWord (cursor : Cursor ): ChineseWord {
96+ with (ChineseWordsDBHelper ) {
97+ return ChineseWord (
98+ cursor.getString(cursor.getColumnIndexOrThrow(SIMPLIFIED )),
99+ " " ,
100+ mapOf (
101+ Locale .ENGLISH to cursor.getString(
102+ cursor.getColumnIndexOrThrow(DEFINITION_EN )
103+ )
104+ ),
105+ ChineseWord .HSK_Level .from(
106+ cursor.getInt(
107+ cursor.getColumnIndexOrThrow(HSK )
108+ )
109+ ),
110+ ChineseWord .Pinyins (
111+ cursor.getString(
112+ cursor.getColumnIndexOrThrow(PINYINS )
113+ )
63114 )
64115 )
65-
66- nextLine = reader.readNext()
67116 }
68117 }
69118
70- private fun getHSKFile (hskLevel : ChineseWord .HSK_Level ) : CSVReader {
71- // ToDo: optimize into a database to avoid loading all just to pull a random word
72- return CSVReader (context.assets.open(" hsk_csk/hsk${hskLevel.level} .csv" ).reader())
73- }
74-
75119 companion object {
76- // @Todo: monitor for possible memory leak
120+ // Todo: Monitor for memory leak.
121+ @Volatile
77122 private var instance: ChineseWordsStore ? = null
78123
79- fun getInstance (context : Context ) : ChineseWordsStore {
80- if (instance == null ) instance = ChineseWordsStore (context)
81-
82- return instance!!
83- }
124+ fun getInstance (context : Context ) =
125+ instance ? : synchronized(this ) {
126+ instance ? : ChineseWordsStore (context).also { instance = it }
127+ }
84128 }
85129}
0 commit comments