OSDN Git Service

オランダ語プラグイン追加。
[wordring-tm/wordring-tm.git] / proxy / tmsocket.cpp
index 178e694..eee02eb 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "settings.h"
 #include "html.h"
-#include "htmltag.h"
+#include "tmtext.h"
 
 #include <QMutex>
 #include <QMutexLocker>
@@ -42,312 +42,6 @@ TM::HtmlData::pointer TM::HtmlData::create(HtmlNode node_, int begin_, int tail_
        return pointer(new HtmlData(node_, begin_, tail_));
 }
 
-// TextConverter --------------------------------------------------------------
-
-/*!
- * \brief 引数として与えられたHtmlの範囲から構造化テキストを作成します。
- */
-Text::pointer TM::TextConverter::to_text(HtmlRange range)
-{
-       m_state = 0;
-       Text::pointer result = Text::create();
-       int begin = 0;
-
-       for(HtmlNode const &node : range)
-       {
-               if(node.type() != HtmlNode::Text) continue;
-               Text::pointer p = stuff_text(result, node.value());
-               if(!p) continue;
-               int num = p->string().size();
-               HtmlData::pointer hd = HtmlData::create(node, begin, begin + num - 1);
-               p->set_data(hd);
-               begin += num;
-       }
-       return result;
-}
-
-/*!
- * \brief 引数として与えられたノードから構造化テキストを作成します。
- *
- * 連続する空白文字を一つにまとめます。
- * 無効なノードが与えられた場合、空の構造化テキストを返します。
- */
-Text::pointer TM::TextConverter::stuff_text(Text::pointer parent, QString const &string)
-{
-       QString outstring;
-       for(QChar const &ch : string)
-       {
-               if(is_ignorable_white_space(ch)) continue;
-               if(is_white_space(ch)) outstring.append(' ');
-               else outstring.append(ch);
-       }
-       if(outstring.isEmpty()) return Text::pointer();
-
-       Text::pointer result = Text::create(parent, outstring);
-       parent->append(result);
-       return result;
-}
-
-/*!
- * \brief 連続する空白文字を検出します。
- *
- * 最初の空白文字、あるいは空白文字以外は、falseを返します。
- * 連続する空白文字の二番目以降に対してのみtrueを返します。
- */
-bool TM::TextConverter::is_ignorable_white_space(const QChar &ch)
-{
-       if(!is_white_space(ch))
-       {
-               m_state = 0;
-               return false;
-       }
-       if(m_state == 0)
-       {
-               m_state = 1;
-               return false;
-       }
-       return true;
-}
-
-/*!
- * \brief 引数として与えられたchが空白文字の場合true、それ以外の場合falseを返します。
- */
-bool TM::TextConverter::is_white_space(QChar const &ch)
-{
-       switch(ch.unicode())
-       {
-       case 0x9:
-       case 0xA:
-       case 0xC:
-       case 0xD:
-       case 0x20:
-       case 0x200B:
-               return true;
-       }
-       return false;
-}
-
-// HtmlConverter --------------------------------------------------------------
-
-
-void TM::HtmlConverter::append(QString string)
-{
-       HtmlText::pointer text = HtmlText::create(HtmlText::weak_pointer());
-       text->set_value(string);
-       m_nodes.append(text);
-}
-
-void TM::HtmlConverter::append(HtmlNode::pointer node, QString string)
-{
-       QList<HtmlNode::pointer> left, right;
-       for(HtmlNode hn = node->parent(); hn; hn = hn.parent())
-       {
-               if(hn.tname() == "body") break;
-               left.prepend(hn.lself());
-               right.append(hn.ltail());
-       }
-
-       m_nodes.append(left);
-       append(string);
-       m_nodes.append(right);
-}
-
-QString TM::HtmlConverter::to_string()
-{
-       QString result;
-
-       adjust();
-       for(Html::pointer node : m_nodes) result += node->to_string();
-
-       return result;
-}
-
-void TM::HtmlConverter::adjust()
-{
-       int adjusted = 0;
-       do
-       {
-               adjusted = 0;
-               for(int i = 0; i < m_nodes.size() - 1; i++)
-               {
-                       HtmlNode::pointer p1 = m_nodes.at(i);
-                       if(p1->type() != Html::Element || p1->place() != Html::Close) continue;
-                       HtmlNode::pointer p2 = m_nodes.at(i + 1);
-                       if(p2->type() != Html::Element || p2->place() != Html::Open) continue;
-
-                       if(p1->lbegin() == p2->lbegin())
-                       {
-                               m_nodes.removeAt(i);
-                               m_nodes.removeAt(i);
-                               ++adjusted;
-                       }
-               }
-       }
-       while(adjusted);
-}
-
-// TextSegment ----------------------------------------------------------------
-
-TM::TextSegment::TextSegment(
-               Service *service, int scode, int segment_id, QString source)
-       : m_segment_id(segment_id)
-       , m_document(source.toUtf8())
-{
-       HtmlNode body = m_document.first("html").first("body");
-       TextConverter tc;
-       m_text = tc.to_text(HtmlRange(body, body));
-
-       Text::pointer sentences = service->divide_into_sentences(scode, m_text->to_string());
-       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 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)
-       {
-               result = service->crc32(code, sentence->source_sentence()->to_string());
-               sentence->set_crc32(result);
-       }
-       return result;
-}
-
-int TM::TextSegment::size() const { return m_sentences.size(); }
-
-TM::TextSentence::pointer TM::TextSegment::at(int index)
-{
-       assert(0 <= index && index < m_sentences.size());
-       return m_sentences.at(index);
-}
-
-TM::TextSegment::iterator TM::TextSegment::begin() { return m_sentences.begin(); }
-
-TM::TextSegment::iterator TM::TextSegment::end() { return m_sentences.end(); }
-
-HtmlNode::pointer TM::TextSegment::find_html_node_by_offset(int offset)
-{
-       HtmlNode::pointer result;
-
-       for(Text::pointer p = m_text->begin(); p; p = p->next())
-       {
-               UserData::pointer ud = p->data();
-               assert(ud->type() == HtmlData::Type);
-               HtmlData *hd = static_cast<HtmlData*>(ud.get());
-               if(hd->begin() <= offset && offset <= hd->tail())
-               {
-                       result = hd->node().lself();
-                       break;
-               }
-       }
-       return result;
-}
-
-/*!
- * \brief セグメント全体をHTML文字列に変換します。
- */
-QString TM::TextSegment::to_html()
-{
-       QString result;
-       for(TextSentence::pointer sentence : m_sentences)
-       {
-               if(!sentence->target_sentence())
-                       result += to_html_from_source(sentence);
-               else result += to_html_from_target(sentence);
-               result += "\r\n";
-       }
-       return result;
-}
-
-/*!
- * \brief 原文をHTMLに変換します。
- *
- * 訳文のついていない文のために在ります。
- */
-QString TM::TextSegment::to_html_from_source(TextSentence::pointer sentence)
-{
-       HtmlConverter hc;
-
-       UserData *ud = sentence->source_sentence()->data().get();
-       assert(ud->type() == RangeData::Type);
-       RangeData *rd = static_cast<RangeData*>(ud);
-       int sentence_offset = rd->begin();
-
-       Text::pointer source_sentence = sentence->source_sentence();
-       if(!source_sentence) return "";
-
-       for(Text::pointer word = source_sentence->begin(); word; word = word->next())
-       {
-               QString string = word->to_string();
-               UserData *ud = word->data().get();
-               assert(ud->type() == RangeData::Type);
-               RangeData *rd = static_cast<RangeData*>(ud);
-               int word_offset = sentence_offset + rd->begin();
-               HtmlNode::pointer node = find_html_node_by_offset(word_offset);
-               if(node) hc.append(node, string);
-               else hc.append(string);
-       }
-       return hc.to_string();
-}
-
-/*!
- * \brief 訳文をHTMLに変換します。
- */
-QString TM::TextSegment::to_html_from_target(TextSentence::pointer sentence)
-{
-       HtmlConverter hc;
-
-       WordLinker* linker = sentence->linker();
-       UserData *ud = sentence->source_sentence()->data().get();
-       assert(ud->type() == RangeData::Type);
-       RangeData *rd = static_cast<RangeData*>(ud);
-       int sentence_offset = rd->begin();
-
-       Text::pointer target_sentence = sentence->target_sentence();
-       if(!target_sentence) return "";
-
-       for(Text::pointer word = target_sentence->begin(); word; word = word->next())
-       {
-               QString string = word->to_string();
-               WordLink::pointer link = linker->find(WordLink::Target, word);
-               if(!link) hc.append(string);
-               else
-               {
-                       UserData::pointer ud = link->sources()->at(0)->data();
-                       assert(ud->type() == RangeData::Type);
-                       RangeData *rd = static_cast<RangeData*>(ud.get());
-                       int word_offset = sentence_offset + rd->begin();
-                       HtmlNode::pointer node = find_html_node_by_offset(word_offset);
-                       if(node) hc.append(node, string);
-                       else hc.append(string);
-               }
-       }
-       return hc.to_string();
-}
-
-TM::TextSegment::pointer TM::TextSegment::create(
-               Service *service, int scode, int segment_id, QString source)
-{
-       return pointer(new TextSegment(service, scode, segment_id, source));
-}
-
 // SocketConnection -----------------------------------------------------------
 
 TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
@@ -359,9 +53,11 @@ TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
        , m_editor_widget(editor_widget)
        , m_site_id(0)
        , m_edit_mode(false)
-       , m_source_language_code(0)
+       , m_scode(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&)),
@@ -396,6 +92,8 @@ void TM::SocketConnection::send_message(QJsonObject const &json)
  * \brief データベースへセンテンスの登録とブラウザへの反映を行います。
  * \param segment_id セグメントのID。
  * \param index センテンスのインデックス。
+ *
+ * エディタから呼び出されます。
  */
 void TM::SocketConnection::save_sentence(int segment_id, int index)
 {
@@ -415,22 +113,107 @@ void TM::SocketConnection::save_sentence(int segment_id, int index)
        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(segment_id, index,
+                                               m_url.host(), scode, tcode, sentence, pointer(this));
+
+       // ブラウザへ反映。
+       set_segment(segment_id, segment->to_html());
+}
+
+/*!
+ * \brief データベースへセンテンスの消去とブラウザへの反映を行います。
+ * \param segment_id セグメントのID。
+ * \param index センテンスのインデックス。
+ *
+ * エディタから呼び出されます。
+ * 呼び出されたとき、訳文はありません。
+ * そこで、clear系がサービスに依頼する時は、原文を引数として渡します。
+ */
+void TM::SocketConnection::remove_sentence(int segment_id, int index)
+{
+       assert(m_site_id);
+
+       // セグメントの検索。
+       segment_map_iterator it = m_segments.find(segment_id);
+       assert(it != m_segments.end());
+       TextSegment::pointer segment = it.value();
+
+       // センテンスの検索。
+       TextSentence::pointer sentence = segment->at(index);
+       assert(sentence->source_sentence());
+       // 訳文の消去。
+       sentence->set_target_sentence(Text::pointer());
+
+       // 言語コード。
+       int scode = m_editor_widget->source_language();
+       int tcode = m_editor_widget->target_language();
+
+       // データベースとサーバへ反映。
+       m_service->remove_sentence(m_url.host(), scode, tcode, sentence);
 
        // ブラウザへ反映。
        set_segment(segment_id, segment->to_html());
 }
 
+/*!
+ * \brief TM::SocketConnection::sentence_found
+ * \param segment_id
+ * \param index
+ * \param result データベースが返す訳文。
+ */
+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();
+
+       segment->at(index)->set_source_id(result->source_id);
+       if(100 <= result->quality) // 品質が100以上の場合、セグメントとブラウザを更新する。
+       {
+               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()); // ブラウザに送信
+       }
+       else // 品質が100未満の場合、候補として登録する。
+       {
+               segment->at(index)->append(result);
+       }
+}
+
+void TM::SocketConnection::sentence_inserted(int segment_id, int index,
+                                               quint32 source_id, quint32 target_id)
+{
+       segment_map_iterator it = m_segments.find(segment_id);
+       assert(it != m_segments.end());
+       TextSegment::pointer segment = it.value();
+
+       assert(source_id);
+       TextSentence::pointer sentence = segment->at(index);
+       sentence->set_source_id(source_id);
+       sentence->set_target_id(target_id);
+}
+
 void TM::SocketConnection::changeEditMode(bool edit_mode)
 {
        set_edit_mode(edit_mode);
 }
 
+void TM::SocketConnection::changeLanguage()
+{
+       QJsonObject json;
+       json["cmd"] = "reload";
+       send_message(json);
+}
+
 /*!
  * \brief ブラウザに編集モードを反映します。
  *
@@ -469,6 +252,7 @@ void TM::SocketConnection::set_segment(int segment_id, QString html)
 void TM::SocketConnection::do_edit_segment(QJsonObject const &json)
 {
        assert(json.contains("segment_id"));
+       if(!json.contains("segment_id")) return;
        int segment_id = json["segment_id"].toString().toInt();
 
        segment_map_iterator it = m_segments.find(segment_id);
@@ -505,7 +289,7 @@ void TM::SocketConnection::do_load(QJsonObject const &json)
 
        set_edit_mode(m_editor_widget->edit_mode());
 
-       m_source_language_code = m_editor_widget->source_language();
+       m_scode = m_editor_widget->source_language();
        m_target_language_code = m_editor_widget->target_language();
        m_url = QUrl(json["url"].toString());
        m_site_id = m_service->find_site_id(m_url.host());
@@ -528,18 +312,17 @@ void TM::SocketConnection::do_load_segment(QJsonObject const &json)
 
        // セグメントの挿入。
        TextSegment::pointer segment = TextSegment::create(
-               m_service, m_source_language_code, segment_id, html);
+               m_service, m_scode, segment_id, html);
        m_segments.insert(segment_id, segment);
 
-
        // センテンス完全一致訳文の検索。
-       //m_service->find_sentence(m_site_id,)
-
-       // セグメントの挿
-       //QJsonObject json;
-       //json["cmd"] = "set_edit_mode";
-       //json["edit_mode"] = edit_mode;
-       //send_message(json);
+       for(TextSentence::pointer sentence : *segment)
+       {
+               m_service->find_sentence(
+                               segment_id, sentence->index(),
+                               m_url.host(), m_scode, m_target_language_code,
+                               sentence, pointer(this));
+       }
 }
 
 void TM::SocketConnection::onTextMessageReceived(QString const &message)
@@ -574,6 +357,7 @@ TM::SocketServer::SocketServer(Settings *settings, Service* service,
        quint16 port = m_settings->value("SocketServer/port").toUInt();
        bool result = m_server->listen(QHostAddress::LocalHost, port);
        if(!result) result = m_server->listen(QHostAddress::LocalHost, 0);
+       assert(result);
        if(!result) qFatal("An error occured in SocketServer().");
 }