return pointer(new WordDatabase(settings, name));
}
-// sentence_data_type ---------------------------------------------------------
-
-TM::sentence_data_type::sentence_data_type()
- : sentence_id(0)
- , source_id(0)
- , crc(0)
- , previous_crc(0)
- , next_crc(0)
- , user_id(0)
- , time(0)
-{
-
-}
-
-TM::sentence_data_type::pointer TM::sentence_data_type::create()
-{
- return pointer(new sentence_data_type());
-}
-
// SentenceDatabase -----------------------------------------------------------
TM::SentenceDatabase::SentenceDatabase(Settings *settings, int site_id, QString name)
// Database -------------------------------------------------------------------
-TM::Database::Database(Settings *settings)
+TM::Database::Database(Settings *settings, Service *service)
: QObject(0)
, m_settings(settings)
+ , m_service(service)
{
qRegisterMetaType<sentence_data_type::pointer>();
}
}
void TM::Database::find_sentence(quint32 site_id, int scode, int tcode,
- QString sstring, sentence_callback callback)
+ int segment_id, int index, sentence_data_type::pointer source,
+ SocketConnection::pointer socket)
{
- QByteArray json;
+ sentence_data_type::pointer result;
SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
assert(sdb);
-
SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
assert(tdb);
- int source_id = sdb->find_sentence_id(sstring);
- if(!source_id)
- {
- //tdb->
- }
+ quint32 source_id = sdb->find_sentence_id_with_context(
+ source->sentence, source->previous_crc, source->next_crc);
+
+ if(source_id) result = tdb->find_sentence_by_source_id(source_id);
+
+ QMetaObject::invokeMethod(
+ m_service, "sentence_found",
+ Qt::QueuedConnection,
+ Q_ARG(qint32, segment_id),
+ Q_ARG(qint32, index),
+ Q_ARG(sentence_data_type::pointer, result),
+ Q_ARG(SocketConnection::pointer, socket));
+
}
void TM::Database::insert_sentence(quint32 site_id,
#ifndef TMDATABASE_H
#define TMDATABASE_H
+#include "tmsocket.h"
+
#include <QObject>
+#include <QPointer>
#include <QSqlDatabase>
#include <QSqlQuery>
int m_cache_limit;
};
-class sentence_data_type
-{
-public:
- typedef std::shared_ptr<sentence_data_type> pointer;
-
- sentence_data_type();
-
- static pointer create();
-
- quint32 sentence_id; /*!< DBによって自動的に付与される文ID */
- quint32 source_id; /*!< 原文は0固定、訳文は原文のsentence_id */
- QString sentence; /*!< 文本体の文字列 */
- QByteArray json; /*!< 原文は空、訳文はリンク情報のJSON */
- quint32 crc; /*!< 検索に使う文のCRC */
- quint32 previous_crc;/*!< 原文は文脈一致に使う前方文のCRC、訳文は0固定 */
- quint32 next_crc; /*!< 原文は文脈一致に使う後方文のCRC、訳文は0固定 */
- quint32 user_id; /*!< レコードを更新したユーザーのID */
- quint64 time; /*!< 更新時刻 */
-};
-
class SentenceDatabase : public DatabaseBase
{
public:
typedef std::function<void(sentence_data_type)> sentence_callback;
public:
- Database(Settings *settings);
+ Database(Settings *settings, Service *service);
~Database();
signals:
quint32 find_site_id(QString host_name);
- void find_sentence(quint32 site_id, int scode, int tcode, QString sstring,
- sentence_callback callback);
+ void find_sentence(
+ quint32 site_id, int scode, int tcode,
+ int segment_id, int index, sentence_data_type::pointer source,
+ SocketConnection::pointer socket);
void insert_sentence(quint32 site_id,
quint32 scode, sentence_data_type::pointer source,
quint32 tcode, sentence_data_type::pointer target);
private:
Settings *m_settings;
+ Service *m_service;
QMap<int, QString> m_language_map; /*!< 言語コード、言語名 */
};
Q_DECLARE_METATYPE(sentence_data_type::pointer)
-//Q_DECLARE_METATYPE(quint32)
} // namespace TM
*/
void TM::Editor::do_link_mode_leave(SourcePanel *panel)
{
- qDebug() << m_current_source_panel->linker()->to_json();
assert(panel);
panel->commit_link();
//panel->clear_highlight();
, m_settings(settings)
, m_mutex(QMutex::Recursive)
, m_database_thread(new QThread(this))
- , m_database(new Database(settings))
+ , m_database(new Database(settings, this))
{
setup_crc_table();
* \param site_id 翻訳対象サイトを表すID。
* \param scode 原文の言語コード。
* \param tcode 訳文の言語コード。
- * \param ssentence 原文の構造化テキスト。
- * \param 結果を返すコールバック関数。
+ * \param sentence 原文。
*/
-void TM::Service::find_sentence(quint32 site_id, int scode, int tcode,
- Text::pointer ssentence, Database::sentence_callback callback)
+void TM::Service::find_sentence(
+ quint32 site_id, int scode, int tcode,
+ int segment_id, int index, TextSentence::pointer sentence,
+ SocketConnection::pointer socket)
{
- assert(m_languages.contains(scode));
- Language *slanguage = m_languages[scode];
+ sentence_data_type::pointer source_sentence_data =
+ stuff_sentence_data(scode, sentence->source_sentence());
- QString sstring = ssentence->to_string();
- assert(!sstring.isEmpty());
- sstring = slanguage->normalize(sstring);
- assert(!sstring.isEmpty());
+ source_sentence_data->crc = sentence->crc();
+ source_sentence_data->previous_crc = sentence->previous_crc();
+ source_sentence_data->next_crc = sentence->next_crc();
QMetaObject::invokeMethod(
m_database, "find_sentence",
Q_ARG(quint32, site_id),
Q_ARG(qint32, scode),
Q_ARG(qint32, tcode),
- Q_ARG(QString, sstring),
- Q_ARG(Database::sentence_callback, callback));
+ Q_ARG(qint32, segment_id),
+ Q_ARG(qint32, index),
+ Q_ARG(sentence_data_type::pointer, source_sentence_data),
+ Q_ARG(SocketConnection::pointer, socket));
}
/*!
* \param sentence 挿入する原文、訳文の対。
*/
void TM::Service::insert_sentence(quint32 site_id, int scode, int tcode,
- TextSentence::pointer sentence, quint32 previous_crc, quint32 next_crc)
+ TextSentence::pointer sentence)
{
sentence_data_type::pointer source_sentence_data =
stuff_sentence_data(scode, sentence->source_sentence());
- source_sentence_data->previous_crc = previous_crc;
- source_sentence_data->next_crc = next_crc;
+ source_sentence_data->crc = sentence->crc();
+ source_sentence_data->previous_crc = sentence->previous_crc();
+ source_sentence_data->next_crc = sentence->next_crc();
sentence_data_type::pointer target_sentence_data =
stuff_sentence_data(tcode, sentence->target_sentence());
return result;
}
+void TM::Service::sentence_found(
+ int segment_id, int index, sentence_data_type::pointer result,
+ SocketConnection::pointer socket)
+{
+ if(socket.isNull()) return;
+
+ socket->sentence_found(segment_id, index, result);
+}
#include "language.h"
#include "tmtext.h"
#include "tmdatabase.h"
+#include "tmsocket.h"
#include <QObject>
#include <QString>
quint32 find_site_id(QString host_name);
void insert_sentence(quint32 site_id, int scode, int tcode,
- TextSentence::pointer sentence, quint32 previous_crc, quint32 next_crc);
+ TextSentence::pointer sentence);
- void find_sentence(quint32 site_id, int scode, int tcode,
- Text::pointer ssentence, Database::sentence_callback callback);
+ void find_sentence(
+ quint32 site_id, int scode, int tcode,
+ int segment_id, int index, TextSentence::pointer sentence,
+ SocketConnection::pointer socket);
private:
sentence_data_type::pointer stuff_sentence_data(int code, Text::pointer text);
public:
void languageLoaded(int code, QString name, QIcon icon);
public slots:
+ void sentence_found(
+ int segment_id, int index, sentence_data_type::pointer result,
+ SocketConnection::pointer socket);
public:
QMutex m_mutex;
m_text = tc.to_text(HtmlRange(body, body));
Text::pointer sentences = service->divide_into_sentences(scode, m_text->to_string());
+ int i = 0;
+ int previous_crc = 0;
for(Text::pointer s = sentences->begin(); s; s = s->next()) // s: sentence
{
Text::pointer words = service->divide_into_words(scode, s);
- m_sentences.append(TextSentence::create(segment_id, words));
+ int crc = service->crc32(scode, words->to_string());
+ TextSentence::pointer sentence =
+ TextSentence::create(segment_id, i++, crc, words);
+ sentence->set_previous_crc(previous_crc);
+ m_sentences.append(sentence);
+ previous_crc = crc;
}
-}
-
-int TM::TextSegment::segment_id() const { return m_segment_id; }
-
-/*!
- * \brief 引数で指定されたインデックスの文について、CRC32を返します。
- * \param service サービスへのポインタ。
- * \param code 言語コード。
- * \param index 文のインデックス。
- * \return CRC32。
- *
- * このメンバは、不正なインデックスを指定した場合、0を返します。
- */
-quint32 TM::TextSegment::crc32(Service *service, int code, int index)
-{
- if(index < 0 || size() <= index) return 0;
- TextSentence::pointer sentence = at(index);
- quint32 result = sentence->crc32();
- if(!result)
+ int next_crc = 0;
+ for(int i = m_sentences.size() - 1; 0 <= i; i--)
{
- result = service->crc32(code, sentence->source_sentence()->to_string());
- sentence->set_crc32(result);
+ TextSentence::pointer sentence = m_sentences.at(i);
+ sentence->set_next_crc(next_crc);
+ next_crc = sentence->crc();
}
- return result;
}
+int TM::TextSegment::segment_id() const { return m_segment_id; }
+
int TM::TextSegment::size() const { return m_sentences.size(); }
TM::TextSentence::pointer TM::TextSegment::at(int index)
, m_source_language_code(0)
, m_target_language_code(0)
{
+ qRegisterMetaType<pointer>();
+
connect(socket, SIGNAL(textMessageReceived(QString const&)),
this, SLOT(onTextMessageReceived(QString const&)));
connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
int scode = m_editor_widget->source_language();
int tcode = m_editor_widget->target_language();
- // ソースのCRC
- quint32 previous_crc = segment->crc32(m_service, scode, index - 1);
- quint32 next_crc = segment->crc32(m_service, scode, index + 1);
-
// データベースへ登録。
- m_service->insert_sentence(m_site_id, scode, tcode, sentence, previous_crc, next_crc);
+ m_service->insert_sentence(m_site_id, scode, tcode, sentence);
// ブラウザへ反映。
set_segment(segment_id, segment->to_html());
}
+void TM::SocketConnection::sentence_found(
+ int segment_id, int index, sentence_data_type::pointer result)
+{
+ if(!result) return;
+
+ segment_map_iterator it = m_segments.find(segment_id);
+ assert(it != m_segments.end());
+ TextSegment::pointer segment = it.value();
+
+ Text::pointer sentences = m_service->divide_into_sentences(
+ m_target_language_code, result->sentence);
+ Text::pointer words = m_service->divide_into_words(
+ m_target_language_code, sentences->begin());
+ segment->at(index)->set_target_sentence(words);
+
+ segment->at(index)->set_json(result->json);
+
+ set_segment(segment_id, segment->to_html());
+}
+
void TM::SocketConnection::changeEditMode(bool edit_mode)
{
set_edit_mode(edit_mode);
m_service, m_source_language_code, segment_id, html);
m_segments.insert(segment_id, segment);
-
// センテンス完全一致訳文の検索。
+ for(TextSentence::pointer sentence : *segment)
+ {
+ m_service->find_sentence(
+ m_site_id, m_source_language_code, m_target_language_code,
+ segment_id, sentence->index(),
+ sentence, pointer(this));
+ }
//m_service->find_sentence(m_site_id,)
// セグメントの挿
#include "tmtext.h"
#include <QObject>
+#include <QPointer>
#include <QString>
#include <QList>
public:
int segment_id() const;
- quint32 crc32(Service *service, int code, int index);
int size() const;
TextSentence::pointer at(int index);
Q_OBJECT
public:
+ typedef QPointer<SocketConnection> pointer;
typedef QMap<int, TextSegment::pointer> segment_map_type;
typedef segment_map_type::iterator segment_map_iterator;
void save_sentence(int segment_id, int index);
void do_sentence_loaded();
+ void sentence_found(
+ int segment_id, int index, sentence_data_type::pointer result);
+
signals:
void editCmd(int id, QString html);
EditorWidget *m_editor_widget;
};
+Q_DECLARE_METATYPE(SocketConnection::pointer)
+
} // namespace TM
#endif // TMSOCKET_H
#include "debug.h"
+// sentence_data_type ---------------------------------------------------------
+
+TM::sentence_data_type::sentence_data_type()
+ : sentence_id(0)
+ , source_id(0)
+ , crc(0)
+ , previous_crc(0)
+ , next_crc(0)
+ , user_id(0)
+ , time(0)
+{
+
+}
+
+TM::sentence_data_type::pointer TM::sentence_data_type::create()
+{
+ return pointer(new sentence_data_type());
+}
+
// WordLink -------------------------------------------------------------------
TM::WordLink::WordLink() { }
TM::WordLink::storage_type* TM::WordLink::targets() { return &m_targets; }
-QJsonArray TM::WordLink::to_json() const
+QJsonArray TM::WordLink::to_json_array() const
{
QJsonArray sources, targets;
for(Text::pointer const &p : m_sources)
return result;
}
-void TM::WordLink::set_json(QJsonArray json)
-{
- for(QJsonValue jo : json);
-}
-
QString TM::WordLink::debug_dump() const
{
QString result;
TM::WordLinker::iterator TM::WordLinker::end() { return m_links.end(); }
-QByteArray TM::WordLinker::to_json() const
+QJsonArray TM::WordLinker::to_json_array() const
{
- QJsonArray ja;
- if(m_current_link && m_current_link->is_valid()) ja.append(m_current_link->to_json());
- for(WordLink::pointer const &p : m_links) ja.append(p->to_json());
+ QJsonArray result;
- QByteArray result;
- if(!ja.isEmpty())
+ if(m_current_link && m_current_link->is_valid())
{
- QJsonDocument jdoc(ja);
- result = jdoc.toJson(QJsonDocument::Compact);
+ QJsonArray link = m_current_link->to_json_array();
+ if(!link.isEmpty()) result.append(link);
}
- return result;
-}
-void TM::WordLinker::set_json(QByteArray json)
-{
- //m_links.append();
+ for(WordLink::pointer const &p : m_links)
+ {
+ QJsonArray link = p->to_json_array();
+ if(!link.isEmpty()) result.append(link);
+ }
+
+ return result;
}
QString TM::WordLinker::debug_dump() const
// TextSentence ---------------------------------------------------------------
-TM::TextSentence::TextSentence(int segment_id, Text::pointer source_sentence)
+TM::TextSentence::TextSentence(int segment_id, int index, quint32 crc, Text::pointer source_sentence)
: m_segment_id(segment_id)
+ , m_index(index)
, m_source_sentence(source_sentence)
- , m_crc32(0)
+ , m_crc(crc)
+ , m_previous_crc(0)
+ , m_next_crc(0)
, m_source_id(0)
, m_target_id(0)
, m_loaded(false)
QByteArray TM::TextSentence::to_json()
{
- return m_linker.to_json();
+ QByteArray result;
+
+ QJsonArray ja = m_linker.to_json_array();
+ if(!ja.isEmpty())
+ {
+ QJsonDocument jdocument;
+ QJsonObject jobject;
+ jobject["LINK"] = ja;
+ jdocument.setObject(jobject);
+ result = jdocument.toJson(QJsonDocument::Compact);
+ }
+
+ return result;
+}
+
+void TM::TextSentence::set_json(QByteArray json)
+{
+ if(json.isEmpty()) return;
+ QJsonDocument jdocument = QJsonDocument::fromJson(json);
+ if(jdocument.isEmpty()) return;
+ assert(jdocument.isObject());
+ if(!jdocument.isObject()) return;
+
+ QJsonObject jobject = jdocument.object();
+ if(jobject.isEmpty()) return;
+ if(jobject.contains("LINK")) set_json_links(jobject);
}
-void TM::TextSentence::set_json(QJsonArray json)
+/*!
+ * \brief センテンス全体の複数の単語リンクを処理します。
+ */
+void TM::TextSentence::set_json_links(QJsonObject const &jobject)
{
- m_linker.clear();
- for(QJsonValue jv : json)
+ QJsonValue jvalue = jobject["LINK"];
+ assert(jvalue.isArray());
+ if(!jvalue.isArray()) return;
+
+ QJsonArray jarray = jvalue.toArray();
+ for(QJsonValue jv : jarray)
{
- QJsonArray ja = jv.toArray();
- //for()
+ assert(jv.isArray());
+ if(!jv.isArray()) return;
+
+ QJsonArray link = jv.toArray();
+ assert(link.size() == 2);
+ if(link.size() != 2) return;
+ set_json_link(link);
}
}
-quint32 TM::TextSentence::crc32() const { return m_crc32; }
+/*!
+ * \brief 単一の単語リンクを処理します。
+ */
+void TM::TextSentence::set_json_link(QJsonArray const &link)
+{
+ QJsonValue svalue = link.at(0);
+ assert(svalue.isArray());
+ if(!svalue.isArray()) return;
+
+ QJsonValue tvalue = link.at(1);
+ assert(tvalue.isArray());
+ if(!tvalue.isArray()) return;
+
+ QList<QPair<int,int> > sranges = stuff_json_link(svalue.toArray());
+ QList<QPair<int,int> > tranges = stuff_json_link(tvalue.toArray());
+
+ assert(!sranges.isEmpty());
+ if(sranges.isEmpty()) return;
+ assert(!tranges.isEmpty());
+ if(tranges.isEmpty()) return;
+
+ m_linker.start();
+
+ for(QPair<int,int> const &range : sranges)
+ {
+ QList<Text::pointer> words = find_words_by_range(
+ m_source_sentence, range.first, range.second);
+ assert(!words.isEmpty());
+ if(words.isEmpty()) continue;
+
+ for(Text::pointer word : words)
+ m_linker.append(WordLink::Source, word);
+ }
+
+ for(QPair<int,int> const &range : tranges)
+ {
+ QList<Text::pointer> words = find_words_by_range(
+ m_target_sentence, range.first, range.second);
+ assert(!words.isEmpty());
+ if(words.isEmpty()) continue;
+
+ for(Text::pointer word : words)
+ m_linker.append(WordLink::Target, word);
+ }
+
+ m_linker.commit();
+}
+
+/*!
+ * \brief JSON配列から、単語の範囲をリスト化して返します。
+ *
+ * 不正な入力に対しては、空のリストを返します。
+ */
+QList<QPair<int,int> > TM::TextSentence::stuff_json_link(
+ QJsonArray const &ranges)
+{
+ QList<QPair<int,int>> result;
+
+ assert(!ranges.isEmpty());
+ if(ranges.isEmpty()) return result;
+ assert(!(ranges.size() % 2));
+ if(ranges.size() % 2) return result;
+
+ int first;
+ int state = 0;
+ for(QJsonValue jv : ranges)
+ {
+ assert(jv.isDouble());
+ if(!jv.isDouble())
+ {
+ result.clear();
+ break;
+ }
+ int n = jv.toInt();
+ if(state == 0)
+ {
+ first = n;
+ state = 1;
+ }
+ else
+ {
+ result.append(qMakePair(first, n));
+ state = 0;
+ }
+ }
+ return result;
+}
+
+/*!
+ * \brief 引数として与えられた文字範囲にマッチする単語をリスト化して返します。
+ * \param sentence 検索する構造化テキスト。
+ * \param first 最初の文字位置。
+ * \param tail 最後の文字位置。
+ *
+ * マッチする単語が無い場合、空のリストを返します。
+ */
+QList<Text::pointer> TM::TextSentence::find_words_by_range(
+ Text::pointer sentence, int first, int tail)
+{
+ QList<Text::pointer> result;
+
+ for(Text::pointer word = sentence->begin(); word; word = word->next())
+ {
+ UserData::pointer ud = word->data();
+ assert(ud->type() == RangeData::Type);
+ RangeData *rd = static_cast<RangeData*>(ud.get());
+ if(first <= rd->begin() && rd->tail() <= tail) result.append(word);
+ }
+
+ return result;
+}
+
+int TM::TextSentence::segment_id() const { return m_segment_id; }
+
+int TM::TextSentence::index() const { return m_index; }
+
+quint32 TM::TextSentence::crc() const { return m_crc; }
+
+quint32 TM::TextSentence::previous_crc() const { return m_previous_crc; }
+
+quint32 TM::TextSentence::next_crc() const { return m_next_crc; }
+
+void TM::TextSentence::set_previous_crc(quint32 crc) { m_previous_crc = crc; }
-void TM::TextSentence::set_crc32(quint32 crc32) { m_crc32 = crc32; }
+void TM::TextSentence::set_next_crc(quint32 crc) { m_next_crc = crc; }
quint32 TM::TextSentence::source_id() const { return m_source_id; }
}
TM::TextSentence::pointer TM::TextSentence::create(
- int segment_id, Text::pointer source_sentence)
+ int segment_id, int index, quint32 crc, Text::pointer source_sentence)
{
- return pointer(new TextSentence(segment_id, source_sentence));
+ return pointer(new TextSentence(segment_id, index, crc, source_sentence));
}
namespace TM
{
+class sentence_data_type
+{
+public:
+ typedef std::shared_ptr<sentence_data_type> pointer;
+
+ sentence_data_type();
+
+ static pointer create();
+
+ quint32 sentence_id; /*!< DBによって自動的に付与される文ID */
+ quint32 source_id; /*!< 原文は0固定、訳文は原文のsentence_id */
+ QString sentence; /*!< 文本体の文字列 */
+ QByteArray json; /*!< 原文は空、訳文はリンク情報のJSON */
+ quint32 crc; /*!< 検索に使う文のCRC */
+ quint32 previous_crc;/*!< 原文は文脈一致に使う前方文のCRC、訳文は0固定 */
+ quint32 next_crc; /*!< 原文は文脈一致に使う後方文のCRC、訳文は0固定 */
+ quint32 user_id; /*!< レコードを更新したユーザーのID */
+ quint64 time; /*!< 更新時刻 */
+};
+
/*!
* \brief 単語間のリンクを表現するクラスです。
*
storage_type* sources();
storage_type* targets();
- QJsonArray to_json() const;
- void set_json(QJsonArray json);
+ QJsonArray to_json_array() const;
QString debug_dump() const;
iterator begin();
iterator end();
- QByteArray to_json() const;
- void set_json(QByteArray json);
+ QJsonArray to_json_array() const;
QString debug_dump() const;
typedef std::shared_ptr<TextSentence> pointer;
private:
- TextSentence(int segment_id, Text::pointer source_sentence);
+ TextSentence(int segment_id, int index, quint32 crc, Text::pointer source_sentence);
public:
Text::pointer source_sentence();
Text::pointer target_sentence();
WordLinker* linker();
QByteArray to_json();
- void set_json(QJsonArray json);
+ void set_json(QByteArray json);
+private:
+ void set_json_links(QJsonObject const &jobject);
+ void set_json_link(QJsonArray const &link_array);
+ QList<QPair<int, int> > stuff_json_link(QJsonArray const &ranges);
- quint32 crc32() const;
- void set_crc32(quint32 crc32);
+ QList<Text::pointer> find_words_by_range(Text::pointer sentence, int first, int tail);
+public:
+ int segment_id() const;
+ int index() const;
+
+ quint32 crc() const;
+ quint32 previous_crc() const;
+ quint32 next_crc() const;
+ void set_previous_crc(quint32 crc);
+ void set_next_crc(quint32 crc);
quint32 source_id() const;
void set_source_id(quint32 source_id);
QString debug_dump() const;
- static pointer create(int segment_id, Text::pointer source_sentence);
+ static pointer create(int segment_id, int index,
+ quint32 crc, Text::pointer source_sentence);
private:
Text::pointer m_source_sentence;
WordLinker m_linker;
int m_segment_id;
+ int m_index;
+
+ quint32 m_crc;
+ quint32 m_previous_crc;
+ quint32 m_next_crc;
- quint32 m_crc32;
quint32 m_source_id;
quint32 m_target_id;