diff options
| author | Peng Wu <alexepico@gmail.com> | 2022-01-14 10:55:58 +0800 |
|---|---|---|
| committer | Peng Wu <alexepico@gmail.com> | 2022-01-14 11:00:05 +0800 |
| commit | 787875a45559952ff65aa8ea608659aedf41f138 (patch) | |
| tree | d2590b1063d414130275b25c9810abfa1234fbee /src | |
| parent | 24f0a6add6e92ba1ebf4e480250c0f0ffc2f427c (diff) | |
| download | ibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.tar.gz ibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.tar.xz ibus-libpinyin-787875a45559952ff65aa8ea608659aedf41f138.zip | |
Write PYEnglishDatabase.cc
Diffstat (limited to 'src')
| -rw-r--r-- | src/PYEnglishDatabase.cc | 351 | ||||
| -rw-r--r-- | src/PYEnglishDatabase.h | 8 | ||||
| -rw-r--r-- | src/PYEnglishEditor.cc | 325 |
3 files changed, 359 insertions, 325 deletions
diff --git a/src/PYEnglishDatabase.cc b/src/PYEnglishDatabase.cc new file mode 100644 index 0000000..308305a --- /dev/null +++ b/src/PYEnglishDatabase.cc @@ -0,0 +1,351 @@ +/* vim:set et ts=4 sts=4: + * + * ibus-libpinyin - Intelligent Pinyin engine based on libpinyin for IBus + * + * Copyright (c) 2021 Peng Wu <alexepico@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "PYEnglishDatabase.h" + +namespace PY{ + +#define DB_BACKUP_TIMEOUT (60) + +EnglishDatabase::EnglishDatabase(){ + m_sqlite = NULL; + m_sql = ""; + m_user_db = ""; + m_timeout_id = 0; + m_timer = g_timer_new (); +} + +EnglishDatabase::~EnglishDatabase(){ + g_timer_destroy (m_timer); + if (m_timeout_id != 0) { + saveUserDB (); + g_source_remove (m_timeout_id); + } + + if (m_sqlite){ + sqlite3_close (m_sqlite); + m_sqlite = NULL; + } + m_sql = ""; + m_user_db = NULL; +} + +gboolean +EnglishDatabase::isDatabaseExisted(const char *filename) { + gboolean result = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + if (!result) + return FALSE; + + sqlite3 *tmp_db = NULL; + if (sqlite3_open_v2 (filename, &tmp_db, + SQLITE_OPEN_READONLY, NULL) != SQLITE_OK){ + return FALSE; + } + + /* Check the desc table */ + sqlite3_stmt *stmt = NULL; + const char *tail = NULL; + m_sql = "SELECT value FROM desc WHERE name = 'version';"; + result = sqlite3_prepare_v2 (tmp_db, m_sql.c_str(), -1, &stmt, &tail); + if (result != SQLITE_OK) + return FALSE; + + result = sqlite3_step (stmt); + if (result != SQLITE_ROW) + return FALSE; + + result = sqlite3_column_type (stmt, 0); + if (result != SQLITE_TEXT) + return FALSE; + + const char *version = (const char *) sqlite3_column_text (stmt, 0); + if (strcmp("1.2.0", version ) != 0) + return FALSE; + + result = sqlite3_finalize (stmt); + g_assert (result == SQLITE_OK); + sqlite3_close (tmp_db); + return TRUE; +} + +gboolean +EnglishDatabase::createDatabase(const char *filename) { + /* unlink the old database. */ + gboolean retval = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + if (retval) { + int result = g_unlink (filename); + if (result == -1) + return FALSE; + } + + char *dirname = g_path_get_dirname (filename); + g_mkdir_with_parents (dirname, 0700); + g_free (dirname); + + sqlite3 *tmp_db = NULL; + if (sqlite3_open_v2 (filename, &tmp_db, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { + return FALSE; + } + + /* Create DESCription table */ + m_sql = "BEGIN TRANSACTION;\n"; + m_sql << "CREATE TABLE IF NOT EXISTS desc (name TEXT PRIMARY KEY, value TEXT);\n"; + m_sql << "INSERT OR IGNORE INTO desc VALUES ('version', '1.2.0');"; + m_sql << "COMMIT;\n"; + + if (!executeSQL (tmp_db)) { + sqlite3_close (tmp_db); + return FALSE; + } + + /* Create Schema */ + m_sql = "CREATE TABLE IF NOT EXISTS english (" + "word TEXT NOT NULL PRIMARY KEY," + "freq FLOAT NOT NULL DEFAULT(0)" + ");"; + if (!executeSQL (tmp_db)) { + sqlite3_close (tmp_db); + return FALSE; + } + return TRUE; +} + +gboolean +EnglishDatabase::openDatabase(const char *system_db, const char *user_db){ + if (!isDatabaseExisted (system_db)) + return FALSE; + if (!isDatabaseExisted (user_db)) { + gboolean result = createDatabase (user_db); + if (!result) + return FALSE; + } + /* cache the user db name. */ + m_user_db = user_db; + + /* do database attach here. :) */ + if (sqlite3_open_v2 (system_db, &m_sqlite, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { + m_sqlite = NULL; + return FALSE; + } + +#if 0 + m_sql.printf (SQL_ATTACH_DB, user_db); + if (!executeSQL (m_sqlite)) { + sqlite3_close (m_sqlite); + m_sqlite = NULL; + return FALSE; + } + return TRUE; +#endif + return loadUserDB(); +} + +/* List the words in freq order. */ +gboolean +EnglishDatabase::listWords(const char *prefix, std::vector<std::string> & words){ + sqlite3_stmt *stmt = NULL; + const char *tail = NULL; + words.clear (); + + /* list words */ + const char *SQL_DB_LIST = + "SELECT word FROM ( " + "SELECT * FROM english UNION ALL SELECT * FROM userdb.english) " + " WHERE word LIKE \"%s%\" GROUP BY word ORDER BY SUM(freq) DESC;"; + m_sql.printf (SQL_DB_LIST, prefix); + int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail); + if (result != SQLITE_OK) + return FALSE; + + result = sqlite3_step (stmt); + while (result == SQLITE_ROW){ + /* get the words. */ + result = sqlite3_column_type (stmt, 0); + if (result != SQLITE_TEXT) + return FALSE; + + const char *word = (const char *)sqlite3_column_text (stmt, 0); + words.push_back (word); + result = sqlite3_step (stmt); + } + + sqlite3_finalize (stmt); + if (result != SQLITE_DONE) + return FALSE; + return TRUE; +} + +/* Get the freq of user sqlite db. */ +gboolean +EnglishDatabase::getWordInfo(const char *word, float & freq){ + sqlite3_stmt *stmt = NULL; + const char *tail = NULL; + /* get word info. */ + const char *SQL_DB_SELECT = + "SELECT freq FROM userdb.english WHERE word = \"%s\";"; + m_sql.printf (SQL_DB_SELECT, word); + int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail); + g_assert (result == SQLITE_OK); + result = sqlite3_step (stmt); + if (result != SQLITE_ROW) + return FALSE; + result = sqlite3_column_type (stmt, 0); + if (result != SQLITE_FLOAT) + return FALSE; + freq = sqlite3_column_double (stmt, 0); + result = sqlite3_finalize (stmt); + g_assert (result == SQLITE_OK); + return TRUE; +} + +/* Update the freq with delta value. */ +gboolean +EnglishDatabase::updateWord(const char *word, float freq){ + const char *SQL_DB_UPDATE = + "UPDATE userdb.english SET freq = \"%f\" WHERE word = \"%s\";"; + m_sql.printf (SQL_DB_UPDATE, freq, word); + gboolean retval = executeSQL (m_sqlite); + modified (); + return retval; +} + +/* Insert the word into user db with the initial freq. */ +gboolean +EnglishDatabase::insertWord(const char *word, float freq){ + const char *SQL_DB_INSERT = + "INSERT INTO userdb.english (word, freq) VALUES (\"%s\", \"%f\");"; + m_sql.printf (SQL_DB_INSERT, word, freq); + gboolean retval = executeSQL (m_sqlite); + modified (); + return retval; +} + +gboolean +EnglishDatabase::executeSQL(sqlite3 *sqlite){ + gchar *errmsg = NULL; + if (sqlite3_exec (sqlite, m_sql.c_str (), NULL, NULL, &errmsg) + != SQLITE_OK) { + g_warning ("%s: %s", errmsg, m_sql.c_str()); + sqlite3_free (errmsg); + return FALSE; + } + m_sql.clear (); + return TRUE; +} + +gboolean +EnglishDatabase::loadUserDB (void){ + sqlite3 *userdb = NULL; + /* Attach user database */ + do { + const char *SQL_ATTACH_DB = + "ATTACH DATABASE ':memory:' AS userdb;"; + m_sql.printf (SQL_ATTACH_DB); + if (!executeSQL (m_sqlite)) + break; + + /* Note: user db is always created by openDatabase. */ + if (sqlite3_open_v2 ( m_user_db, &userdb, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) + break; + + sqlite3_backup *backup = sqlite3_backup_init (m_sqlite, "userdb", userdb, "main"); + + if (backup) { + sqlite3_backup_step (backup, -1); + sqlite3_backup_finish (backup); + } + + sqlite3_close (userdb); + return TRUE; + } while (0); + + if (userdb) + sqlite3_close (userdb); + return FALSE; +} + +gboolean +EnglishDatabase::saveUserDB (void){ + sqlite3 *userdb = NULL; + String tmpfile = String(m_user_db) + "-tmp"; + do { + /* remove tmpfile if it exist */ + g_unlink(tmpfile); + + if (sqlite3_open_v2 (tmpfile, &userdb, + SQLITE_OPEN_READWRITE | + SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) + break; + + sqlite3_backup *backup = sqlite3_backup_init (userdb, "main", m_sqlite, "userdb"); + + if (backup == NULL) + break; + + sqlite3_backup_step (backup, -1); + sqlite3_backup_finish (backup); + sqlite3_close (userdb); + + g_rename(tmpfile, m_user_db); + return TRUE; + } while (0); + + if (userdb) + sqlite3_close (userdb); + g_unlink (tmpfile); + return FALSE; +} + +void +EnglishDatabase::modified (void){ + /* Restart the timer */ + g_timer_start (m_timer); + + if (m_timeout_id != 0) + return; + + m_timeout_id = g_timeout_add_seconds (DB_BACKUP_TIMEOUT, + EnglishDatabase::timeoutCallback, + static_cast<gpointer> (this)); +} + +static gboolean +EnglishDatabase::timeoutCallback (gpointer data){ + EnglishDatabase *self = static_cast<EnglishDatabase *> (data); + + /* Get elapsed time since last modification of database. */ + guint elapsed = (guint) g_timer_elapsed (self->m_timer, NULL); + + if (elapsed >= DB_BACKUP_TIMEOUT && + self->saveUserDB ()) { + self->m_timeout_id = 0; + return FALSE; + } + + return TRUE; +} + +}; diff --git a/src/PYEnglishDatabase.h b/src/PYEnglishDatabase.h index d5b832a..0cfbc3d 100644 --- a/src/PYEnglishDatabase.h +++ b/src/PYEnglishDatabase.h @@ -40,6 +40,14 @@ private: gboolean saveUserDB (void); void modified (void); static gboolean timeoutCallback (gpointer data); + +private: + sqlite3 *m_sqlite; + String m_sql; + const char *m_user_db; + + guint m_timeout_id; + GTimer *m_timer; }; #endif diff --git a/src/PYEnglishEditor.cc b/src/PYEnglishEditor.cc index f5e3123..0b2677f 100644 --- a/src/PYEnglishEditor.cc +++ b/src/PYEnglishEditor.cc @@ -34,331 +34,6 @@ namespace PY { -#define DB_BACKUP_TIMEOUT (60) - -class EnglishDatabase{ -public: - EnglishDatabase(){ - m_sqlite = NULL; - m_sql = ""; - m_user_db = ""; - m_timeout_id = 0; - m_timer = g_timer_new (); - } - - ~EnglishDatabase(){ - g_timer_destroy (m_timer); - if (m_timeout_id != 0) { - saveUserDB (); - g_source_remove (m_timeout_id); - } - - if (m_sqlite){ - sqlite3_close (m_sqlite); - m_sqlite = NULL; - } - m_sql = ""; - m_user_db = NULL; - } - - gboolean isDatabaseExisted(const char *filename) { - gboolean result = g_file_test (filename, G_FILE_TEST_IS_REGULAR); - if (!result) - return FALSE; - - sqlite3 *tmp_db = NULL; - if (sqlite3_open_v2 (filename, &tmp_db, - SQLITE_OPEN_READONLY, NULL) != SQLITE_OK){ - return FALSE; - } - - /* Check the desc table */ - sqlite3_stmt *stmt = NULL; - const char *tail = NULL; - m_sql = "SELECT value FROM desc WHERE name = 'version';"; - result = sqlite3_prepare_v2 (tmp_db, m_sql.c_str(), -1, &stmt, &tail); - if (result != SQLITE_OK) - return FALSE; - - result = sqlite3_step (stmt); - if (result != SQLITE_ROW) - return FALSE; - - result = sqlite3_column_type (stmt, 0); - if (result != SQLITE_TEXT) - return FALSE; - - const char *version = (const char *) sqlite3_column_text (stmt, 0); - if (strcmp("1.2.0", version ) != 0) - return FALSE; - - result = sqlite3_finalize (stmt); - g_assert (result == SQLITE_OK); - sqlite3_close (tmp_db); - return TRUE; - } - - gboolean createDatabase(const char *filename) { - /* unlink the old database. */ - gboolean retval = g_file_test (filename, G_FILE_TEST_IS_REGULAR); - if (retval) { - int result = g_unlink (filename); - if (result == -1) - return FALSE; - } - - char *dirname = g_path_get_dirname (filename); - g_mkdir_with_parents (dirname, 0700); - g_free (dirname); - - sqlite3 *tmp_db = NULL; - if (sqlite3_open_v2 (filename, &tmp_db, - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { - return FALSE; - } - - /* Create DESCription table */ - m_sql = "BEGIN TRANSACTION;\n"; - m_sql << "CREATE TABLE IF NOT EXISTS desc (name TEXT PRIMARY KEY, value TEXT);\n"; - m_sql << "INSERT OR IGNORE INTO desc VALUES ('version', '1.2.0');"; - m_sql << "COMMIT;\n"; - - if (!executeSQL (tmp_db)) { - sqlite3_close (tmp_db); - return FALSE; - } - - /* Create Schema */ - m_sql = "CREATE TABLE IF NOT EXISTS english (" - "word TEXT NOT NULL PRIMARY KEY," - "freq FLOAT NOT NULL DEFAULT(0)" - ");"; - if (!executeSQL (tmp_db)) { - sqlite3_close (tmp_db); - return FALSE; - } - return TRUE; - } - - gboolean openDatabase(const char *system_db, const char *user_db){ - if (!isDatabaseExisted (system_db)) - return FALSE; - if (!isDatabaseExisted (user_db)) { - gboolean result = createDatabase (user_db); - if (!result) - return FALSE; - } - /* cache the user db name. */ - m_user_db = user_db; - - /* do database attach here. :) */ - if (sqlite3_open_v2 (system_db, &m_sqlite, - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { - m_sqlite = NULL; - return FALSE; - } - -#if 0 - m_sql.printf (SQL_ATTACH_DB, user_db); - if (!executeSQL (m_sqlite)) { - sqlite3_close (m_sqlite); - m_sqlite = NULL; - return FALSE; - } - return TRUE; -#endif - return loadUserDB(); - } - - /* List the words in freq order. */ - gboolean listWords(const char *prefix, std::vector<std::string> & words){ - sqlite3_stmt *stmt = NULL; - const char *tail = NULL; - words.clear (); - - /* list words */ - const char *SQL_DB_LIST = - "SELECT word FROM ( " - "SELECT * FROM english UNION ALL SELECT * FROM userdb.english) " - " WHERE word LIKE \"%s%\" GROUP BY word ORDER BY SUM(freq) DESC;"; - m_sql.printf (SQL_DB_LIST, prefix); - int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail); - if (result != SQLITE_OK) - return FALSE; - - result = sqlite3_step (stmt); - while (result == SQLITE_ROW){ - /* get the words. */ - result = sqlite3_column_type (stmt, 0); - if (result != SQLITE_TEXT) - return FALSE; - - const char *word = (const char *)sqlite3_column_text (stmt, 0); - words.push_back (word); - result = sqlite3_step (stmt); - } - - sqlite3_finalize (stmt); - if (result != SQLITE_DONE) - return FALSE; - return TRUE; - } - - /* Get the freq of user sqlite db. */ - gboolean getWordInfo(const char *word, float & freq){ - sqlite3_stmt *stmt = NULL; - const char *tail = NULL; - /* get word info. */ - const char *SQL_DB_SELECT = - "SELECT freq FROM userdb.english WHERE word = \"%s\";"; - m_sql.printf (SQL_DB_SELECT, word); - int result = sqlite3_prepare_v2 (m_sqlite, m_sql.c_str(), -1, &stmt, &tail); - g_assert (result == SQLITE_OK); - result = sqlite3_step (stmt); - if (result != SQLITE_ROW) - return FALSE; - result = sqlite3_column_type (stmt, 0); - if (result != SQLITE_FLOAT) - return FALSE; - freq = sqlite3_column_double (stmt, 0); - result = sqlite3_finalize (stmt); - g_assert (result == SQLITE_OK); - return TRUE; - } - - /* Update the freq with delta value. */ - gboolean updateWord(const char *word, float freq){ - const char *SQL_DB_UPDATE = - "UPDATE userdb.english SET freq = \"%f\" WHERE word = \"%s\";"; - m_sql.printf (SQL_DB_UPDATE, freq, word); - gboolean retval = executeSQL (m_sqlite); - modified (); - return retval; - } - - /* Insert the word into user db with the initial freq. */ - gboolean insertWord(const char *word, float freq){ - const char *SQL_DB_INSERT = - "INSERT INTO userdb.english (word, freq) VALUES (\"%s\", \"%f\");"; - m_sql.printf (SQL_DB_INSERT, word, freq); - gboolean retval = executeSQL (m_sqlite); - modified (); - return retval; - } - -private: - gboolean executeSQL(sqlite3 *sqlite){ - gchar *errmsg = NULL; - if (sqlite3_exec (sqlite, m_sql.c_str (), NULL, NULL, &errmsg) - != SQLITE_OK) { - g_warning ("%s: %s", errmsg, m_sql.c_str()); - sqlite3_free (errmsg); - return FALSE; - } - m_sql.clear (); - return TRUE; - } - - gboolean loadUserDB (void){ - sqlite3 *userdb = NULL; - /* Attach user database */ - do { - const char *SQL_ATTACH_DB = - "ATTACH DATABASE ':memory:' AS userdb;"; - m_sql.printf (SQL_ATTACH_DB); - if (!executeSQL (m_sqlite)) - break; - - /* Note: user db is always created by openDatabase. */ - if (sqlite3_open_v2 ( m_user_db, &userdb, - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) - break; - - sqlite3_backup *backup = sqlite3_backup_init (m_sqlite, "userdb", userdb, "main"); - - if (backup) { - sqlite3_backup_step (backup, -1); - sqlite3_backup_finish (backup); - } - - sqlite3_close (userdb); - return TRUE; - } while (0); - - if (userdb) - sqlite3_close (userdb); - return FALSE; - } - - gboolean saveUserDB (void){ - sqlite3 *userdb = NULL; - String tmpfile = String(m_user_db) + "-tmp"; - do { - /* remove tmpfile if it exist */ - g_unlink(tmpfile); - - if (sqlite3_open_v2 (tmpfile, &userdb, - SQLITE_OPEN_READWRITE | - SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) - break; - - sqlite3_backup *backup = sqlite3_backup_init (userdb, "main", m_sqlite, "userdb"); - - if (backup == NULL) - break; - - sqlite3_backup_step (backup, -1); - sqlite3_backup_finish (backup); - sqlite3_close (userdb); - - g_rename(tmpfile, m_user_db); - return TRUE; - } while (0); - - if (userdb) - sqlite3_close (userdb); - g_unlink (tmpfile); - return FALSE; - } - - void modified (void){ - /* Restart the timer */ - g_timer_start (m_timer); - - if (m_timeout_id != 0) - return; - - m_timeout_id = g_timeout_add_seconds (DB_BACKUP_TIMEOUT, - EnglishDatabase::timeoutCallback, - static_cast<gpointer> (this)); - } - - static gboolean timeoutCallback (gpointer data){ - EnglishDatabase *self = static_cast<EnglishDatabase *> (data); - - /* Get elapsed time since last modification of database. */ - guint elapsed = (guint) g_timer_elapsed (self->m_timer, NULL); - - if (elapsed >= DB_BACKUP_TIMEOUT && - self->saveUserDB ()) { - self->m_timeout_id = 0; - return FALSE; - } - - return TRUE; - } - - sqlite3 *m_sqlite; - String m_sql; - const char *m_user_db; - - guint m_timeout_id; - GTimer *m_timer; -}; - EnglishEditor::EnglishEditor (PinyinProperties & props, Config &config) : Editor (props, config), m_train_factor (0.1) { |
