line-height: 1.3em;
}
+form {
+ margin: 0 auto;
+ padding: 0.5em;
+ background-color: #999966;
+ font-size: 1.3em;
+}
+#u {
+ width: 60%;
+}
+
a { color: #3b7960; }
a:visited { color: #3b7960; }
a:hover { color: #c85179; }
</head>
<body>
-
<h1>Wordring 翻訳メモリー</h1>
-<h2>以下のフォームにURLを入力して移動できます</h2>
+<noscript>動作にはJavascriptが必要です。</noscript>
+
+<h2>以下のフォームにURLを入力して読みたいサイトへ移動できます</h2>
<form name="form">
-Enter URL:<input type="text" name="u"><input type="submit" value="Enter">
+URL:<input type="text" name="u" id="u"><input type="submit" value="Enter">
</form>
+<p>
+訳文を書き込むと、原文と訳文の対がパソコンとws://wordring.net/の両方に保存されます。<br>
+ws://wordring.net/に保存された対訳は皆で共有されるので、書き込んだ訳文はパブリック・ドメインでお願いします。
+</p>
+<p>
+詳しくは<a href="http://wordring.net/#usage">使い方</a>を見てください。
+</p>
<h2>以下のサイトで試してみてください</h2>
<ul>
<p>
リンクをクリックして移動してください。<br>
いずれのサイトも文書をオープンソース・ライセンスで公開しています。
-(※いずれのサイトもサイトのデザインはオープンソースでありませんが、このプログラムは文章以外を共有しません。)
</p>
<h2>エラーが出る場合</h2>
+
<p>
-ほとんどの場合、ブラウザがWebSocketに対応していないことが原因です。<br>
+翻訳メモリーとウェブ・ブラウザ間の接続は、HTTPに加えてWebSocketを使います。<br>
主要なウェブ・ブラウザの最新版は、WebSocketに対応しています。
-最新版にアップデートしてください。
+最新版にアップデートしてください。<br>
+WebSocketで通信するため、JavascriptがONである必要があります。
+</p>
+<p>
+古いブラウザでは、動かないことや見た目が崩れる場合があります。
+</p>
+<p>
+翻訳メモリーは、ws://wordring.net/とWebSocketによる通信を行います。<br>
+ファイアウォールがある場合、適切に設定してください。
</p>
<p>
解決できない問題は、
<h2>その他</h2>
<p>
-現在、機能検討用のアルファ版の状態です。<br>
-正式に決まるまで、交換される訳文はパブリック・ドメインでお願いします。<br>
-訳文に各々がライセンスを付けるべきなのか私もよくわかっていません。<br>
-その他の問題も含めて、<a href="https://osdn.jp/projects/wordring-tm/forums/32597/">OSDNのフォーラム</a>で
-話し合っていただければと思います。
+現在、仕様検討用のアルファ版の状態です。<br>
+追加したい機能など、<a href="https://osdn.jp/projects/wordring-tm/forums/32597/">OSDNのフォーラム</a>で話し合ってください。
</p>
<h2>Wordring 翻訳メモリーのサイト</h2>
m_candidate_dock = new QDockWidget(tr("candidate"), this);
m_candidate_dock->setObjectName("CandidateDock");
//m_candidate_dock->setFeatures(QDockWidget::DockWidgetMovable);
- m_candidate_widget = new TM::CandidateWidget(this);
+ m_candidate_widget = new TM::CandidateWidget(settings, service, this);
m_candidate_dock->setWidget(m_candidate_widget);
addDockWidget(Qt::RightDockWidgetArea, m_candidate_dock, Qt::Vertical);
tmtext.cpp \
tmdatabase.cpp \
tmcandidatewidget.cpp \
- tmeditorwidget.cpp
+ tmeditorwidget.cpp \
+ tmcandidate.cpp
HEADERS += mainwindow.h \
tmhttp.h \
tmtext.h \
tmdatabase.h \
tmcandidatewidget.h \
- tmeditorwidget.h
+ tmeditorwidget.h \
+ tmcandidate.h
DEFINES += PROXYSRCDIR=\\\"$$PWD\\\"
#INCLUDEPATH += $$PWD/../language
--- /dev/null
+#include "tmcandidate.h"
+
--- /dev/null
+#ifndef TMCANDIDATE_H
+#define TMCANDIDATE_H
+
+
+class tmcandidate
+{
+public:
+ tmcandidate();
+
+signals:
+
+public slots:
+};
+
+#endif // TMCANDIDATE_H
-#include "tmcandidatewidget.h"
-#include "textwidget.h"
+
+#include "settings.h"
+#include "tmservice.h"
+#include "tmcandidatewidget.h"
+
#include <QBoxLayout>
-#include <QTextEdit>
+#include <QLabel>
-TM::CandidateWidget::CandidateWidget(QWidget *parent)
- : QWidget(parent)
+TM::CandidateWidget::CandidateWidget(Settings *settings, Service *service, QWidget *parent)
+ : QListWidget(parent)
{
+ addItem("<b>test\r\n</b>\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
+ addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
+ addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
+ addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
+ addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
+ /*
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+ setWidgetResizable(false);
+ //new CandidateArea(this);
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setSpacing(4);
vlayout->setContentsMargins(0, 0, 0, 0);
+ vlayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
- m_text_widget = new TextWidget(this);
- vlayout->addWidget(m_text_widget);
+ QLabel *l = new QLabel("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na", this);
+ l->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ l->setFrameStyle(QFrame::Panel);
+ //l->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
+ vlayout->addWidget(l);
+ QLabel *l2 = new QLabel("<b><a href=\"#\">test</a></b>", this);
+ l2->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ vlayout->addWidget(l2);
+ */
}
+
+TM::CandidateArea::CandidateArea(QWidget *parent)
+ : QWidget(parent)
+{
+}
#define TMCANDIDATEWIDGET_H
#include <QWidget>
-#include <QTextEdit>
+#include <QScrollArea>
+#include <QListWidget>
-class TextWidget;
+class Settings;
namespace TM
{
-class CandidateWidget : public QWidget
+class Service;
+
+class CandidateWidget : public QListWidget
{
Q_OBJECT
public:
- explicit CandidateWidget(QWidget *parent = 0);
+ CandidateWidget(Settings *settings, Service *service, QWidget *parent = 0);
signals:
public slots:
private:
- TextWidget *m_text_widget;
+ ;
+};
+
+class CandidateArea : public QWidget
+{
+ Q_OBJECT
+public:
+ CandidateArea(QWidget *parent);
};
} // namespace TM
return m_find_sentence_id_with_context.value(0).toUInt();
}
-TM::sentence_data_type::pointer
+TM::sentence_data_type
TM::SentenceDatabase::find_sentence_by_source_id(int source_id)
{
assert(source_id);
- sentence_data_type::pointer result;
+ sentence_data_type result;
m_find_sentence_by_source_id.bindValue(0, source_id);
exec(m_find_sentence_by_source_id, Q_FUNC_INFO);
return stuff_value(&m_find_sentence_by_source_id);
}
-void TM::SentenceDatabase::insert(sentence_data_type::pointer sentence_data)
+void TM::SentenceDatabase::insert(sentence_data_type const &sentence_data)
{
assert(m_database.isValid());
assert(m_database.isOpen());
- m_insert_sentence.bindValue(0, sentence_data->source_id);
- m_insert_sentence.bindValue(1, sentence_data->sentence);
- m_insert_sentence.bindValue(2, sentence_data->json);
- m_insert_sentence.bindValue(3, sentence_data->crc);
- m_insert_sentence.bindValue(4, sentence_data->previous_crc);
- m_insert_sentence.bindValue(5, sentence_data->next_crc);
- m_insert_sentence.bindValue(6, sentence_data->user_id);
- m_insert_sentence.bindValue(7, sentence_data->time);
+ m_insert_sentence.bindValue(0, sentence_data.source_id);
+ m_insert_sentence.bindValue(1, sentence_data.sentence);
+ m_insert_sentence.bindValue(2, sentence_data.json);
+ m_insert_sentence.bindValue(3, sentence_data.crc);
+ m_insert_sentence.bindValue(4, sentence_data.previous_crc);
+ m_insert_sentence.bindValue(5, sentence_data.next_crc);
+ m_insert_sentence.bindValue(6, sentence_data.user_id);
+ m_insert_sentence.bindValue(7, sentence_data.time);
exec(m_insert_sentence, Q_FUNC_INFO);
}
-void TM::SentenceDatabase::update(sentence_data_type::pointer sentence_data)
+void TM::SentenceDatabase::update(sentence_data_type const &sentence_data)
{
assert(m_database.isValid());
assert(m_database.isOpen());
- m_update_sentence.bindValue(0, sentence_data->source_id);
- m_update_sentence.bindValue(1, sentence_data->sentence);
- m_update_sentence.bindValue(2, sentence_data->json);
- m_update_sentence.bindValue(3, sentence_data->crc);
- m_update_sentence.bindValue(4, sentence_data->previous_crc);
- m_update_sentence.bindValue(5, sentence_data->next_crc);
- m_update_sentence.bindValue(6, sentence_data->user_id);
- m_update_sentence.bindValue(7, sentence_data->time);
- m_update_sentence.bindValue(8, sentence_data->sentence_id);
+ m_update_sentence.bindValue(0, sentence_data.source_id);
+ m_update_sentence.bindValue(1, sentence_data.sentence);
+ m_update_sentence.bindValue(2, sentence_data.json);
+ m_update_sentence.bindValue(3, sentence_data.crc);
+ m_update_sentence.bindValue(4, sentence_data.previous_crc);
+ m_update_sentence.bindValue(5, sentence_data.next_crc);
+ m_update_sentence.bindValue(6, sentence_data.user_id);
+ m_update_sentence.bindValue(7, sentence_data.time);
+ m_update_sentence.bindValue(8, sentence_data.sentence_id);
exec(m_update_sentence, Q_FUNC_INFO);
}
exec(m_delete_sentence, Q_FUNC_INFO);
}
-TM::sentence_data_type::pointer
+TM::sentence_data_type
TM::SentenceDatabase::stuff_value(QSqlQuery *query)
{
assert(query->isValid());
- sentence_data_type::pointer result = sentence_data_type::create();
-
- result->sentence_id = query->value(0).toUInt();
- result->source_id = query->value(1).toUInt();
- result->sentence = query->value(2).toString();
- result->json = query->value(3).toByteArray();
- result->crc = query->value(4).toUInt();
- result->previous_crc = query->value(5).toUInt();
- result->next_crc = query->value(6).toUInt();
- result->user_id = query->value(7).toUInt();
- result->time = query->value(8).toUInt();
+ sentence_data_type result;
+
+ result.sentence_id = query->value(0).toUInt();
+ result.source_id = query->value(1).toUInt();
+ result.sentence = query->value(2).toString();
+ result.json = query->value(3).toByteArray();
+ result.crc = query->value(4).toUInt();
+ result.previous_crc = query->value(5).toUInt();
+ result.next_crc = query->value(6).toUInt();
+ result.user_id = query->value(7).toUInt();
+ result.time = query->value(8).toUInt();
return result;
}
}
void TM::IndexDatabase::insert_index(WordDatabase::pointer word_database,
- sentence_data_type::pointer source, quint32 target_id)
+ sentence_data_type const &sdata, quint32 target_id)
{
- QString const &string = source->sentence;
- for(QPair<int,int> const & range: source->words)
+ for(QString word : sdata.words)
{
- QString word = string.mid(
- range.first, range.second - range.first + 1);
if(word.size() <= 2) continue;
int word_id = 0;
, m_settings(settings)
, m_service(service)
{
- qRegisterMetaType<sentence_data_type::pointer>();
}
TM::Database::~Database()
/*!
* \brief センテンスを検索して結果をサービスに返します。
- * \param site_name
- * \param scode
- * \param tcode
- * \param source
- * \param token
*/
-void TM::Database::find_sentence(QString site_name, int scode, int tcode,
- sentence_data_type::pointer source, int token)
+void TM::Database::find_sentence(sentence_data_type source, TextSentence::weak_pointer token)
{
- sentence_data_type::pointer result;
+ sentence_data_type result;
- quint32 site_id = find_site_id(site_name);
+ quint32 site_id = find_site_id(source.site);
assert(site_id);
- SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
+ SentenceDatabase::pointer sdb = find_sentence_database(site_id, source.scode);
assert(sdb);
- SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
+ SentenceDatabase::pointer tdb = find_sentence_database(site_id, source.tcode);
assert(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);
- if(result) result->quality = 101;
- }
+ source.sentence, source.previous_crc, source.next_crc);
+ if(source_id) result = tdb->find_sentence_by_source_id(source_id);
+
// 文脈込で発見できない場合、文脈無視で検索を試みる。
- if(!result)
+ if(!result.sentence_id)
{
- source_id = sdb->find_sentence_id(source->sentence);
+ source_id = sdb->find_sentence_id(source.sentence);
if(source_id) result = tdb->find_sentence_by_source_id(source_id);
- if(result)
- {
- result->quality = 100;
- result->source_id = source_id;
- }
}
- if(result) // 検索結果を返す。
+ if(result.sentence_id) // 検索結果を返す。
{
QMetaObject::invokeMethod(
m_service, "sentence_found",
Qt::QueuedConnection,
- Q_ARG(sentence_data_type::pointer, result),
- Q_ARG(qint32, token));
+ Q_ARG(sentence_data_type, result),
+ Q_ARG(TextSentence::weak_pointer, token));
}
else // 検索できなかったことを通知し、サービスはサーバに問い合わせる。
{
QMetaObject::invokeMethod(
m_service, "sentence_not_found",
Qt::QueuedConnection,
- Q_ARG(QString, site_name),
- Q_ARG(qint32, scode),
- Q_ARG(qint32, tcode),
- Q_ARG(QString, source->sentence),
- Q_ARG(qint32, token));
+ Q_ARG(TextSentence::weak_pointer, token));
}
}
-void TM::Database::insert_sentence(
- QString site_name, quint32 scode, quint32 tcode,
- sentence_data_type::pointer source,
- sentence_data_type::pointer target, qint32 token)
+void TM::Database::insert_sentence(sentence_data_type source,
+ sentence_data_type target,
+ TextSentence::weak_pointer token)
{
// 入力の検証。
- assert(!site_name.isEmpty());
- assert(scode);
- assert(tcode);
- assert(source->source_id == 0);
- assert(source->json.isEmpty());
- assert(target->previous_crc == 0);
- assert(target->next_crc == 0);
-
- quint32 site_id = find_site_id(site_name);
+ assert(!target.site.isEmpty());
+ assert(target.scode);
+ assert(target.tcode);
+ assert(source.source_id == 0);
+ assert(source.json.isEmpty());
+ assert(target.previous_crc == 0);
+ assert(target.next_crc == 0);
+
+ quint32 site_id = find_site_id(target.site);
assert(site_id);
if(!site_id) return;
// 原文の更新。
- SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
+ SentenceDatabase::pointer sdb =
+ find_sentence_database(site_id, target.scode);
assert(sdb);
// 原文IDの検索、無い場合登録。
// 現在、前後の文脈が一致する場合のみ、IDを検索できたこととしている。
// 文脈が一致しない場合、新たにIDを付与する。
- quint32 source_id = source->sentence_id;
+ quint32 source_id = source.sentence_id;
if(!source_id) source_id = sdb->find_sentence_id_with_context(
- source->sentence, source->previous_crc, source->next_crc);
+ source.sentence, source.previous_crc, source.next_crc);
if(!source_id)
{
- source->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ source.time = QDateTime::currentDateTime().toMSecsSinceEpoch();
sdb->insert(source);
source_id = sdb->find_sentence_id_with_context(
- source->sentence, source->previous_crc, source->next_crc);
+ source.sentence, source.previous_crc, source.next_crc);
}
assert(source_id);
// 訳文の更新。
- SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
+ SentenceDatabase::pointer tdb =
+ find_sentence_database(site_id, target.tcode);
assert(tdb);
- target->source_id = source_id;
- target->time = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ target.source_id = source_id;
+ target.time = QDateTime::currentDateTime().toMSecsSinceEpoch();
// 原文IDに対応する訳文を検索する。
// 現在、原文IDに対して登録できる訳文は一つのみ。
- sentence_data_type::pointer target_sentence =
+ sentence_data_type target_sentence =
tdb->find_sentence_by_source_id(source_id);
- if(target_sentence) // 訳文があった場合、上書きして更新する。
+ if(target_sentence.sentence_id) // 訳文があった場合、上書きして更新する。
{
- target->sentence_id = target_sentence->sentence_id;
+ target.sentence_id = target_sentence.sentence_id;
tdb->update(target);
}
else
// 挿入後、検索することで訳文IDを取得する。
target_sentence = tdb->find_sentence_by_source_id(source_id);
}
- assert(target_sentence);
- quint32 target_id = target_sentence->sentence_id;
+ assert(target_sentence.sentence_id);
+ quint32 target_id = target_sentence.sentence_id;
assert(target_id); // 挿入あるいは更新しているので、IDがある。
// 単語とセンテンスの索引を付ける。
// 単語の登録。
IndexDatabase::pointer index_database =
- find_index_database(site_id, scode, tcode);
+ find_index_database(site_id, target.scode, target.tcode);
assert(index_database);
- WordDatabase::pointer word_database = find_word_database(scode);
+ WordDatabase::pointer word_database = find_word_database(target.scode);
assert(word_database);
index_database->insert_index(word_database, source, target_id);
-
- // サービスに原文IDを返す。
- QMetaObject::invokeMethod(
- m_service, "sentence_inserted",
- Qt::QueuedConnection,
- Q_ARG(quint32, source_id),
- Q_ARG(quint32, target_id),
- Q_ARG(qint32, token));
}
/*!
* \brief データベースから引数として与えられた原文に対応する訳文を削除します。
- * \param site_name
- * \param scode
- * \param tcode
- * \param source
*/
-void TM::Database::remove_sentence(QString site_name, quint32 scode, quint32 tcode,
- sentence_data_type::pointer source)
+void TM::Database::remove_sentence(
+ sentence_data_type sdata, sentence_data_type tdata)
{
- qDebug() << "TM::Database::remove_sentence()";
-
// 入力の検証。
- assert(!site_name.isEmpty());
- assert(scode);
- assert(tcode);
- assert(source->source_id == 0);
- assert(source->json.isEmpty());
+ assert(!sdata.site.isEmpty());
+ assert(sdata.scode);
+ assert(sdata.tcode);
+ assert(sdata.source_id == 0);
+ assert(sdata.json.isEmpty());
- quint32 site_id = find_site_id(site_name);
+ quint32 site_id = find_site_id(sdata.site);
assert(site_id);
if(!site_id) return;
// 原文IDの検索。
- //SentenceDatabase::pointer sdb = find_sentence_database(site_id, scode);
- //assert(sdb);
-
- // 原文IDの検索、無い場合、訳文を引き出せないので諦めるしかない。
- quint32 source_id = source->sentence_id;
- //if(!source_id) source_id = sdb->find_sentence_ids(source->sentence);
+ quint32 source_id = sdata.sentence_id;
+ if(!source_id)
+ {
+ SentenceDatabase::pointer sdb =
+ find_sentence_database(site_id, sdata.scode);
+ assert(sdb);
+ source_id = sdb->find_sentence_id_with_context(
+ sdata.sentence, sdata.previous_crc, sdata.next_crc);
+ }
if(!source_id) qDebug() << "Database::remove_sentence(): Do not find source_id";
if(!source_id) return;
// 訳文の消去。
- SentenceDatabase::pointer tdb = find_sentence_database(site_id, tcode);
+ SentenceDatabase::pointer tdb =
+ find_sentence_database(site_id, sdata.tcode);
assert(tdb);
tdb->remove(source_id);
bool find_sentence_by_crc(
quint32 crc, QString *tsentence, QJsonArray *json = nullptr);
- sentence_data_type::pointer find_sentence_by_source_id(int source_id);
+ sentence_data_type find_sentence_by_source_id(int source_id);
- void insert(sentence_data_type::pointer sentence_data);
- void update(sentence_data_type::pointer sentence_data);
+ void insert(sentence_data_type const &sentence_data);
+ void update(sentence_data_type const &sentence_data);
void remove(quint32 source_id);
private:
- sentence_data_type::pointer stuff_value(QSqlQuery *query);
+ sentence_data_type stuff_value(QSqlQuery *query);
public:
static pointer create(Settings *settings, int site_id, QString name);
public:
index_data_type::pointer find_index(quint32 word_id);
void insert_index(WordDatabase::pointer word_database,
- sentence_data_type::pointer source, quint32 target_id);
+ const sentence_data_type &sdata, quint32 target_id);
private:
void insert_index(index_data_type::pointer index_data);
void update_index(index_data_type::pointer index_data);
quint32 find_site_id(QString host_name);
- void find_sentence(QString site_name, int scode, int tcode,
- sentence_data_type::pointer source, int token);
- void insert_sentence(QString site_name, quint32 scode, quint32 tcode,
- sentence_data_type::pointer source,
- sentence_data_type::pointer target, qint32 token);
- void remove_sentence(QString site_name, quint32 scode, quint32 tcode,
- sentence_data_type::pointer source);
+ void find_sentence(sentence_data_type source,
+ TextSentence::weak_pointer token);
+ void insert_sentence(sentence_data_type source, sentence_data_type target,
+ TextSentence::weak_pointer token);
+ void remove_sentence(sentence_data_type sdata,
+ sentence_data_type tdata);
//void insert_index();
int m_index_cache_limit;
};
-Q_DECLARE_METATYPE(sentence_data_type::pointer)
-
} // namespace TM
#endif // TMDATABASE_H
*
* 結果としてデータベース、サーバ、ブラウザを更新します。
*/
-void TM::EditorWidget::save_sentence(int segment_id, int index)
+void TM::EditorWidget::save_sentence(TextSentence::pointer text_sentence)
{
- m_socket->save_sentence(segment_id, index);
+ m_socket->save_sentence(text_sentence);
}
/*!
*
* チケット #35438 訳文を元に戻せない 対応
*/
-void TM::EditorWidget::remove_sentence(int segment_id, int index)
+void TM::EditorWidget::remove_sentence(TextSentence::pointer text_sentence)
{
- m_socket->remove_sentence(segment_id, index);
+ m_socket->remove_sentence(text_sentence);
}
/*!
Text::pointer TM::SourcePanel::source_sentence()
{
- return m_text_sentence->source_sentence();
+ return m_text_sentence->ssentence();
}
Text::pointer TM::SourcePanel::target_sentence()
{
- return m_text_sentence->target_sentence();
+ return m_text_sentence->tsentence();
}
void TM::SourcePanel::set_target_sentence(Text::pointer sentence)
{
- m_text_sentence->set_target_sentence(sentence);
+ m_text_sentence->set_tsentence(sentence);
}
TM::TextSentence::pointer TM::SourcePanel::text_sentence()
set_link_mode(false);
parent_editor_widget()->set_link_mode_disabled(true);
- int index = panel->index(); // センテンスのインデックス。
+ TextSentence::pointer text_sentence = panel->text_sentence();
// チケット #35438 訳文を元に戻せない
bool need_clear_target = (!tp->is_text_saved()) && tp->is_text_dirty() && tp->is_empty();
if(need_clear_target)
{
- parent_editor_widget()->remove_sentence(m_segment_id, index);
+ parent_editor_widget()->remove_sentence(text_sentence);
}
else if(!tp->is_text_saved())
{
if(tp->is_text_dirty()) divide_target_sentence(panel);
if(!tp->is_empty())
- parent_editor_widget()->save_sentence(m_segment_id, index);
+ parent_editor_widget()->save_sentence(text_sentence);
tp->set_text_saved(true);
}
}
int target_language();
void set_segment(TextSegment::pointer segment);
- void save_sentence(int segment_id, int index);
- void remove_sentence(int segment_id, int index);
+ void save_sentence(TextSentence::pointer text_sentence);
+ void remove_sentence(TextSentence::pointer text_sentence);
signals:
void editModeChanged(bool mode_);
#include "settings.h"
#include "tmdatabase.h"
+#include "tmsocket.h"
+
#include <QMessageBox>
#include <QDir>
{
}
-bool TM::WordringConnection::find_sentence(
- QString site_name, int scode, int tcode, QString ssentence, qint32 token)
+bool TM::WordringConnection::find_sentence(sentence_data_type const &sdata)
{
+ if(m_request_limit < m_requests.size()) return false;
+
QJsonObject jobject;
jobject["cmd"] = "find";
- jobject["site"] = site_name;
- jobject["scode"] = scode;
- jobject["tcode"] = tcode;
- jobject["ssentence"] = ssentence;
- jobject["token"] = token;
+ jobject["site"] = sdata.site;
+ jobject["scode"] = (int)sdata.scode;
+ jobject["tcode"] = (int)sdata.tcode;
+
+ jobject["ssentence"] = sdata.sentence;
+ jobject["previous_crc"] = (int)sdata.previous_crc;
+ jobject["next_crc"] = (int)sdata.next_crc;
+
+ jobject["token"] = 1;
QJsonDocument jdocument(jobject);
+ assert(jdocument.isObject());
QByteArray json = jdocument.toJson();
m_requests.enqueue(json);
}
bool TM::WordringConnection::insert_sentence(
- QString site_name, int scode, int tcode,
- QString ssentence, QString tsentence, QByteArray word_link)
+ sentence_data_type const &sdata,
+ sentence_data_type const & tdata)
{
+ if(m_request_limit < m_requests.size()) return false;
+
+ QString host = sdata.site;
+ qint32 scode = sdata.scode;
+ qint32 tcode = sdata.tcode;
+
+ QString sstring = sdata.sentence;
+ QString tstring = tdata.sentence;
+ QByteArray jdata = tdata.json;
+
QJsonObject jobject;
jobject["cmd"] = "insert";
- jobject["site"] = site_name;
+
+ jobject["site"] = host;
jobject["scode"] = scode;
jobject["tcode"] = tcode;
- jobject["ssentence"] = ssentence;
- jobject["tsentence"] = tsentence;
- if(!word_link.isEmpty())
+ jobject["ssentence"] = sstring;
+ jobject["previous_crc"] = (int)sdata.previous_crc;
+ jobject["next_crc"] = (int)sdata.next_crc;
+
+ jobject["tsentence"] = tstring;
+
+ if(!jdata.isEmpty())
{
QJsonParseError error;
- QJsonDocument jlink = QJsonDocument::fromJson(word_link, &error);
+ QJsonDocument jlink = QJsonDocument::fromJson(jdata, &error);
assert(error.error == QJsonParseError::NoError);
if(error.error != QJsonParseError::NoError) return false;
}
QJsonDocument jdocument(jobject);
+ assert(jdocument.isObject());
QByteArray json = jdocument.toJson(QJsonDocument::Compact);
m_requests.enqueue(json);
/*!
* \brief サーバから訳文を消去します。
- * \param site_name
- * \param scode
- * \param tcode
- * \param ssentence
- * \return
*/
bool TM::WordringConnection::remove_sentence(
- QString site_name, int scode, int tcode, QString ssentence)
+ sentence_data_type const &sdata,
+ sentence_data_type const &)
{
QJsonObject jobject;
jobject["cmd"] = "remove";
- jobject["site"] = site_name;
- jobject["scode"] = scode;
- jobject["tcode"] = tcode;
- jobject["ssentence"] = ssentence;
+
+ jobject["site"] = sdata.site;
+ jobject["scode"] = (int)sdata.scode;
+ jobject["tcode"] = (int)sdata.tcode;
+
+ jobject["ssentence"] = sdata.sentence;
+ jobject["previous_crc"] = (int)sdata.previous_crc;
+ jobject["next_crc"] = (int)sdata.next_crc;
QJsonDocument jdocument(jobject);
+ assert(jdocument.isObject());
QByteArray json = jdocument.toJson(QJsonDocument::Compact);
m_requests.enqueue(json);
void TM::WordringConnection::onDisconnected()
{
- m_service->do_wordring_disconnected();
if(m_socket) m_socket->deleteLater();
m_socket = nullptr;
}
QJsonParseError jerror;
QJsonDocument jdocument = QJsonDocument::fromJson(message.toUtf8(), &jerror);
assert(jerror.error == QJsonParseError::NoError);
- if(jerror.error != QJsonParseError::NoError) return;
+ if(jerror.error != QJsonParseError::NoError)
+ {
+ qDebug() << jerror.errorString();
+ return;
+ }
assert(jdocument.isObject());
if(!jdocument.isObject()) return;
QString cmd = json["cmd"].toString();
if(cmd == "found") sentence_found(json);
- else if(cmd == "not_found") sentence_not_found(json);
-
}
-void TM::WordringConnection::onBinaryMessageReceived(QByteArray const &message)
+void TM::WordringConnection::onBinaryMessageReceived(QByteArray const &)
{
assert(false);
}
{
if(m_request_limit < m_requests.size()) // 制限値を超えているか検査。
{
- m_request_limit = 0;
m_requests.clear();
}
- if(!m_request_limit) return; // 制限値が0の場合、送信できない。
-
if(!m_socket) open();
if(!m_socket->isValid()) return;
while(!m_requests.isEmpty())
{
- QByteArray & json = m_requests.head();
+ QByteArray json = m_requests.head();
m_socket->sendTextMessage(json);
m_requests.dequeue();
}
QString site = json["site"].toString();
int scode = json["scode"].toInt();
int tcode = json["tcode"].toInt();
+
QString ssentence = json["ssentence"].toString();
QString tsentence = json["tsentence"].toString();
QByteArray jdata;
quint32 user_id = json["user_id"].toInt();
qint32 token = json["token"].toInt();
+ quint32 sentence_id = 0;
+ quint32 source_id = 0;
+ quint32 crc = 0;
+ quint32 previous_crc = 0;
+ quint32 next_crc = 0;
+ quint64 time = 0;
+
assert(!site.isEmpty());
- assert(scode);
- assert(tcode);
+ assert(0 < scode && scode <= 0xFF);
+ assert(0 < tcode && tcode <= 0xFF);
assert(!ssentence.isEmpty());
assert(!tsentence.isEmpty());
assert(user_id);
if(tsentence.isEmpty()) return;
if(!user_id) return;
if(!token) return;
- m_service->sentence_found(
- site, scode, tcode, ssentence, tsentence, jdata, user_id, token);
-}
-void TM::WordringConnection::sentence_not_found(QJsonObject json)
-{
- qint32 token = json["token"].toInt();
- assert(token);
- if(!token) return;
- m_service->sentence_not_found(token);
+ sentence_data_type result;
+ result.site = site;
+ result.scode = scode;
+ result.tcode = tcode;
+ result.sentence_id = sentence_id;
+ result.source_id = source_id;
+ result.sentence = tsentence;
+ result.json = jdata;
+ result.crc = crc;
+ result.previous_crc = previous_crc;
+ result.next_crc = next_crc;
+ result.user_id = user_id;
+ result.time = time;
+
+ m_service->sentence_found(ssentence, result);
}
// Service --------------------------------------------------------------------
, m_database_thread(new QThread(this))
, m_database(new Database(settings, this))
, m_wordring(new WordringConnection(settings, this))
- , m_wordring_dispatcher_limit(4 * 1024)
{
+ qRegisterMetaType<sentence_data_type>();
+ qRegisterMetaType<TextSentence::weak_pointer>();
+
setup_crc_table();
m_database->moveToThread(m_database_thread);
m_database_thread->wait();
}
+void TM::Service::attach(SocketConnection *connection)
+{
+ m_connections.insert(connection);
+}
+
+void TM::Service::detach(SocketConnection *connection)
+{
+ m_connections.remove(connection);
+}
+
void TM::Service::setup_crc_table()
{
for (quint32 i = 0; i < 256; i++)
return m_languages[code]->divide_into_words(sentence);
}
+QString TM::Service::normalize(int code, QString string)
+{
+ assert(m_languages.contains(code));
+ return m_languages[code]->normalize(string);
+}
+
/*!
* \brief 引数として与えられたサイトに対応するIDを返します。
*/
-quint32 TM::Service::find_site_id(QString host_name)
+quint32 TM::Service::find_site_id(QString host)
{
quint32 result;
QMetaObject::invokeMethod(
m_database, "find_site_id",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(quint32, result),
- Q_ARG(QString, host_name));
+ Q_ARG(QString, host));
return result;
}
* \param tcode 訳文の言語コード。
* \param sentence 原文。
*/
-void TM::Service::find_sentence(
- int segment_id, int index,
- QString site_name, int scode, int tcode,
- TextSentence::pointer sentence,
- SocketConnection::pointer socket)
+void TM::Service::find_sentence(TextSentence::pointer sentence)
{
- sentence_data_type::pointer source_sentence_data =
- stuff_sentence_data(scode, sentence->source_sentence());
-
- source_sentence_data->crc = sentence->crc();
- source_sentence_data->previous_crc = sentence->previous_crc();
- source_sentence_data->next_crc = sentence->next_crc();
-
- // データベースの検索。
- // ソケットのトークンを取得。
- dispatcher_data_type ddata;
- ddata.segment_id = segment_id;
- ddata.index = index;
- ddata.connection = socket;
-
- int token = m_database_dispatcher.set(ddata);
+ sentence_data_type sdata = sentence->ssentence_data();
+ // 原文を正規化。
+ sdata.sentence = normalize(sdata.scode, sdata.sentence);
// データベース呼び出し。
QMetaObject::invokeMethod(
m_database, "find_sentence",
Qt::QueuedConnection,
- Q_ARG(QString, site_name),
- Q_ARG(qint32, scode),
- Q_ARG(qint32, tcode),
- Q_ARG(sentence_data_type::pointer, source_sentence_data),
- Q_ARG(qint32, token));
+ Q_ARG(sentence_data_type, sdata),
+ Q_ARG(TextSentence::weak_pointer, sentence));
}
/*!
* \brief 原文と訳文の対をデータベースとサーバへ登録します。
- * \param site_id 翻訳対象サイトを表すID。
- * \param scode 原文の言語コード。
- * \param tcode 訳文の言語コード。
* \param sentence 挿入する原文、訳文の対。
*/
-void TM::Service::insert_sentence(int segment_id, int index,
- QString site_name, int scode, int tcode,
- TextSentence::pointer sentence,
- SocketConnection::pointer socket)
-
+void TM::Service::insert_sentence(TextSentence::pointer sentence)
{
- // 原文を埋める。
- sentence_data_type::pointer source_sentence_data =
- stuff_source_sentence_data(scode, sentence->source_sentence(),
- sentence->crc(), sentence->previous_crc(), sentence->next_crc());
+ sentence_data_type sdata = sentence->ssentence_data();
+ sentence_data_type tdata = sentence->tsentence_data();
- // 訳文を埋める。
- sentence_data_type::pointer target_sentence_data =
- stuff_sentence_data(tcode, sentence->target_sentence());
- target_sentence_data->json = sentence->to_json();
+ // 原文を正規化。
+ sdata.sentence = normalize(sdata.scode, sdata.sentence);
// サーバへ登録(文のポインタを共有するため、非同期で動くデータベースへは、後から登録する)。
- if( m_wordring_dispatcher.size() < m_wordring_dispatcher_limit)
- {
- m_wordring->insert_sentence(site_name, scode, tcode,
- source_sentence_data->sentence, target_sentence_data->sentence,
- target_sentence_data->json);
- }
-
- // ソケットのトークンを取得。
- dispatcher_data_type ddata;
- ddata.segment_id = segment_id;
- ddata.index = index;
- ddata.connection = socket;
-
- int token = m_database_dispatcher.set(ddata);
+ m_wordring->insert_sentence(sdata, tdata);
// データベースへ登録。
QMetaObject::invokeMethod(
m_database, "insert_sentence",
Qt::QueuedConnection,
- Q_ARG(QString, site_name),
- Q_ARG(quint32, scode),
- Q_ARG(quint32, tcode),
- Q_ARG(sentence_data_type::pointer, source_sentence_data),
- Q_ARG(sentence_data_type::pointer, target_sentence_data),
- Q_ARG(qint32, token));
+ Q_ARG(sentence_data_type, sdata),
+ Q_ARG(sentence_data_type, tdata),
+ Q_ARG(TextSentence::weak_pointer, sentence));
}
+
/*!
* \brief 訳文をデータベースとサーバから消去します。
- * \param site_id 翻訳対象サイトを表すID。
- * \param scode 原文の言語コード。
- * \param tcode 訳文の言語コード。
- * \param sentence 消去する訳文。
*/
-void TM::Service::remove_sentence(QString site_name, int scode, int tcode,
- TextSentence::pointer sentence)
+void TM::Service::remove_sentence(TextSentence::pointer sentence)
{
- qDebug() << "TM::Service::remove_sentence()";
-
- // 原文を埋める。
- sentence_data_type::pointer source_sentence_data =
- stuff_source_sentence_data(scode, sentence->source_sentence(),
- sentence->crc(), sentence->previous_crc(), sentence->next_crc());
- quint32 ssentence_id = sentence->source_id();
- assert(ssentence_id);
- source_sentence_data->sentence_id = ssentence_id;
+ sentence_data_type sdata = sentence->ssentence_data();
+ sentence_data_type tdata = sentence->tsentence_data();
+ // 原文を正規化。
+ sdata.sentence = normalize(sdata.scode, sdata.sentence);
- // サーバへ登録(文のポインタを共有するため、非同期で動くデータベースへは、後から登録する)。
- if( m_wordring_dispatcher.size() < m_wordring_dispatcher_limit)
- {
- m_wordring->remove_sentence(site_name, scode, tcode,
- source_sentence_data->sentence);
- }
+ m_wordring->remove_sentence(sdata, tdata);
// データベースから消去。
QMetaObject::invokeMethod(
m_database, "remove_sentence",
Qt::QueuedConnection,
- Q_ARG(QString, site_name),
- Q_ARG(quint32, scode),
- Q_ARG(quint32, tcode),
- Q_ARG(sentence_data_type::pointer, source_sentence_data));
-}
-
-/*!
- * \brief ブラウザ側のデータ形式をデータベース形式に変換します。
- * \param code 言語コード。
- * \param text 構造化テキスト。
- */
-TM::sentence_data_type::pointer TM::Service::stuff_sentence_data(
- int code, Text::pointer text)
-{
- assert(m_languages.contains(code));
- Language *language = m_languages[code];
-
- QString string = text->to_string();
- assert(!string.isEmpty());
- string = language->normalize(string);
- assert(!string.isEmpty());
-
- sentence_data_type::pointer result = sentence_data_type::create();
-
- result->sentence = string;
-
- return result;
-}
-
-/*!
- * \brief ブラウザ側の原文データ形式をデータベース原文形式に変換します。
- * \param code 言語コード。
- * \param text 構造化テキスト。
- */
-TM::sentence_data_type::pointer TM::Service::stuff_source_sentence_data(
- int code, Text::pointer text, quint32 crc, quint32 previous_crc, quint32 next_crc)
-{
- // 原文を埋める。
- sentence_data_type::pointer result = stuff_sentence_data(code, text);
- result->crc = crc;
- result->previous_crc = previous_crc;
- result->next_crc = next_crc;
-
- // 原文の単語情報を埋める。
- assert(m_languages.contains(code));
- Language *language = m_languages[code];
- Text::pointer sentences = language->divide_into_sentences(result->sentence);
- assert(sentences->size() == 1);
- Text::pointer words = language->divide_into_words(sentences->begin());
- for(Text::pointer word = words->begin(); word; word = word->next())
- {
- UserData::pointer ud = word->data();
- assert(ud->type() == RangeData::Type);
- RangeData *rd = static_cast<RangeData*>(ud.get());
-
- result->words.append(qMakePair(rd->begin(), rd->tail()));
- }
-
- return result;
+ Q_ARG(sentence_data_type, sdata),
+ Q_ARG(sentence_data_type, tdata));
}
/*!
* \brief データベースからの検索結果。
* \param result 検索結果。
* \param token connectionを示すトークン。
+ *
+ * 検索に使った文字列は、tokenから取れる。
*/
-void TM::Service::sentence_found(
- sentence_data_type::pointer result, qint32 token)
+void TM::Service::sentence_found(sentence_data_type result,
+ TextSentence::weak_pointer token)
{
- dispatcher_data_type ddata = m_database_dispatcher.get(token);
- if(ddata.connection.isNull()) return;
- // SocketConnectionに返答する。
- ddata.connection->sentence_found(ddata.segment_id, ddata.index, result);
+ if(token.expired()) return;
+
+ TextSentence::pointer sentence = token.lock();
+ // ブラウザに結果を送る。
+ TextSegmentList::pointer segments = sentence->segment_list();
+ assert(segments);
+ SocketConnection* connection = segments->connection();
+ assert(connection);
+ connection->sentence_found(result, sentence);
}
// データベースからの検索結果が無かった場合、サーバから検索する。
-void TM::Service::sentence_not_found(QString site_name, qint32 scode, qint32 tcode,
- QString ssentence, qint32 token)
+void TM::Service::sentence_not_found(TextSentence::weak_pointer token)
{
- dispatcher_data_type ddata = m_database_dispatcher.get(token);
- if(ddata.connection.isNull()) return;
+ if(token.expired()) return;
// サーバから検索。
- if(m_wordring_dispatcher.size() < m_wordring_dispatcher_limit)
- {
- int wordring_token = m_wordring_dispatcher.set(ddata);
- m_wordring->find_sentence(site_name, scode, tcode, ssentence, wordring_token);
- }
+ TextSentence::pointer sentence = token.lock();
+ sentence_data_type sdata = sentence->ssentence_data();
+ sdata.sentence = normalize(sdata.scode, sdata.sentence);
+
+ m_wordring->find_sentence(sdata);
}
+
/*!
* \brief データベースからの挿入結果通知。
* \param source_id
* \param target_id
* \param token
*/
-void TM::Service::sentence_inserted(
- quint32 source_id, quint32 target_id, qint32 token)
+void TM::Service::sentence_inserted(quint32 source_id, quint32 target_id,
+ TextSentence::weak_pointer token)
{
+ if(token.expired()) return;
+
+ TextSentence::pointer sentence = token.lock();
+ sentence->set_source_id(source_id);
+ sentence->set_target_id(target_id);
// ブラウザへ通知。
- dispatcher_data_type ddata = m_wordring_dispatcher.get(token);
- if(ddata.connection)
- {
- ddata.connection->sentence_inserted(
- ddata.segment_id, ddata.index, source_id, target_id);
- qDebug() << "Service::sentence_inserted()";
- }
+ sentence->segment_list()->connection()->sentence_inserted(sentence);
}
/*!
* \brief サーバからの検索結果通知。
- * \param site サイト名。
- * \param scode 原文文字コード。
- * \param tcode 訳文文字コード。
- * \param ssentence 原文。
- * \param tsentence 訳文。
- * \param json 付加データ。
- * \param user_id 書き込み者のユーザーID。
- * \param token SocketConnectionを示すトークン。
*/
-void TM::Service::sentence_found(QString site, qint32 scode, qint32 tcode,
- QString ssentence, QString tsentence, QByteArray json,
- quint32 user_id, qint32 token)
+void TM::Service::sentence_found(
+ QString sstring, sentence_data_type const &result)
{
+ // 原文を正規化。
+ sstring = normalize(result.scode, sstring);
+
+ // ブラウザへ通知。
+ for(SocketConnection *connection : m_connections)
+ {
+ TextSegmentList::pointer segments = connection->segment_list();
+ QList<TextSentence::pointer> i = segments->find_sentences(sstring);
+ for(TextSentence::pointer j : i) connection->sentence_found(result, j);
+ }
+
// 原文を埋める。
- sentence_data_type::pointer sdata = sentence_data_type::create();
+ sentence_data_type source;
+ source.sentence = sstring;
// 原文の単語情報を埋める。
- assert(m_languages.contains(scode));
- Language *language = m_languages[scode];
- Text::pointer sentences = language->divide_into_sentences(ssentence);
+ assert(m_languages.contains(result.scode));
+ Language *language = m_languages[result.scode];
+ Text::pointer sentences = language->divide_into_sentences(sstring);
assert(sentences->size() == 1);
Text::pointer words = language->divide_into_words(sentences->begin());
for(Text::pointer word = words->begin(); word; word = word->next())
- {
- UserData::pointer ud = word->data();
- assert(ud->type() == RangeData::Type);
- RangeData *rd = static_cast<RangeData*>(ud.get());
-
- sdata->words.append(qMakePair(rd->begin(), rd->tail()));
- }
- sdata->sentence = ssentence;
-
- // 訳文を埋める。
- sentence_data_type::pointer tdata = sentence_data_type::create();
- tdata->sentence = tsentence;
- tdata->json = json;
- tdata->user_id = user_id;
+ source.words.append(word->to_string());
- // ブラウザへ通知。
- dispatcher_data_type ddata = m_wordring_dispatcher.get(token);
- if(ddata.connection)
- {
- tdata->quality = 100;
- ddata.connection->sentence_found(ddata.segment_id, ddata.index, tdata);
- }
-
- // データベースへ登録(文のポインタを共有するため、非同期で動くデータベースへは、後から登録する)。
+ // データベースへ保存。
QMetaObject::invokeMethod(
m_database, "insert_sentence",
Qt::QueuedConnection,
- Q_ARG(QString, site),
- Q_ARG(quint32, scode),
- Q_ARG(quint32, tcode),
- Q_ARG(sentence_data_type::pointer, sdata),
- Q_ARG(sentence_data_type::pointer, tdata),
- Q_ARG(qint32, token));
+ Q_ARG(sentence_data_type, source),
+ Q_ARG(sentence_data_type, result),
+ Q_ARG(TextSentence::weak_pointer, TextSentence::pointer()));
}
-void TM::Service::sentence_not_found(quint32 token)
-{
- m_wordring_dispatcher.get(token);
-}
-
-void TM::Service::do_wordring_disconnected()
-{
- qDebug() << "Service::do_wordring_disconnected()"
- << "dispatcer count=" << m_wordring_dispatcher.size();
- m_wordring_dispatcher.clear();
-}
#include "language.h"
#include "tmtext.h"
#include "tmdatabase.h"
-#include "tmsocket.h"
#include <QObject>
#include <QString>
{
class Service;
-
-template <typename T>
-class Dispatcher
-{
-public:
- Dispatcher() : m_token_count(0) { }
-
- int size() const { return m_map.size(); }
-
- void clear() { m_map.clear(); }
-
- int set(T p)
- {
- int token = 0;
- if(m_tokens.isEmpty()) token = ++m_token_count;
- else token = m_tokens.dequeue();
-
- m_map.insert(token, p);
- return token;
- }
-
- T get(int token)
- {
- T result;
- QMap<int, T>::iterator it = m_map.find(token);
-
- if(it != m_map.end())
- {
- result = it.value();
- m_tokens.append(it.key());
- m_map.erase(it);
- }
- return result;
- }
-
-private:
- QMap<int, T> m_map;
- QQueue<int> m_tokens;
- int m_token_count;
-};
+class SocketConnection;
class WordringConnection : public QObject
{
public:
WordringConnection(Settings *settings, Service *service);
- bool find_sentence(QString site_name, int scode, int tcode,
- QString ssentence, qint32 token);
+ bool find_sentence(sentence_data_type const &sdata);
- bool insert_sentence(QString site_name, int scode, int tcode,
- QString ssentence, QString tsentence, QByteArray word_link);
- bool remove_sentence(QString site_name, int scode, int tcode,
- QString ssentence);
+ bool insert_sentence(sentence_data_type const &sdata,
+ sentence_data_type const &tdata);
+ bool remove_sentence(
+ sentence_data_type const &sdata,
+ sentence_data_type const &tdata);
public slots:
void onConnected();
Service(Settings *settings, QObject *parent = 0);
~Service();
+ // SocketConnection
+ void attach(SocketConnection *connection);
+ void detach(SocketConnection *connection);
+
// CRC32
void setup_crc_table();
quint32 crc32(QString const &string);
Text::pointer divide_into_sentences(int code, QString string);
Text::pointer divide_into_words(int code, Text::pointer sentence);
+ QString normalize(int code, QString string);
+
// データベース
- quint32 find_site_id(QString host_name);
-
- void insert_sentence(int segment_id, int index,
- QString site_name, int scode, int tcode,
- TextSentence::pointer sentence, SocketConnection::pointer socket);
- void remove_sentence(QString site_name, int scode, int tcode,
- TextSentence::pointer sentence);
-
- void find_sentence(int segment_id, int index,
- QString site_name, int scode, int tcode,
- TextSentence::pointer sentence,
- SocketConnection::pointer socket);
+ quint32 find_site_id(QString host);
+
+ void insert_sentence(TextSentence::pointer sentence);
+ void remove_sentence(TextSentence::pointer sentence);
+
+ void find_sentence(TextSentence::pointer sentence);
private:
- sentence_data_type::pointer stuff_sentence_data(int code, Text::pointer text);
- sentence_data_type::pointer stuff_source_sentence_data(
- int code, Text::pointer text, quint32 crc, quint32 previous_crc,
- quint32 next_crc);
public:
signals:
/*!
public slots:
// データベースから
- void sentence_found(sentence_data_type::pointer result, qint32 token);
- void sentence_not_found(QString site_name, qint32 scode, qint32 tcode,
- QString ssentence, qint32 token);
- void sentence_inserted(quint32 source_id, quint32 target_id, qint32 token);
+ void sentence_found(sentence_data_type result, TextSentence::weak_pointer token);
+ void sentence_not_found(TextSentence::weak_pointer token);
+ void sentence_inserted(quint32 source_id, quint32 target_id, TextSentence::weak_pointer token);
public:
// サーバから
- void sentence_found(QString site, qint32 scode, qint32 tcode,
- QString ssentence, QString tsentence, QByteArray json,
- quint32 user_id, qint32 token);
- void sentence_not_found(quint32 token);
-
- void do_wordring_disconnected();
+ void sentence_found(QString sstring, sentence_data_type const &result);
private:
Settings *m_settings;
- quint32 m_crc_table[256];
QMap<int, Language*> m_languages;
QThread *m_database_thread;
Database *m_database;
- Dispatcher<dispatcher_data_type> m_database_dispatcher;
WordringConnection *m_wordring;
- Dispatcher<dispatcher_data_type> m_wordring_dispatcher;
- int m_wordring_dispatcher_limit;
+
+ QSet<SocketConnection*> m_connections;
+ quint32 m_crc_table[256];
};
+Q_DECLARE_METATYPE(sentence_data_type)
+Q_DECLARE_METATYPE(TextSentence::weak_pointer)
+
} // namespace TM
#endif // TMSERVICE_H
HtmlNode TM::HtmlData::node() { return m_node; }
+UserData::pointer TM::HtmlData::clone()
+{
+ return pointer(new HtmlData(m_node, begin(), tail()));
+}
+
QString TM::HtmlData::debug_dump() const
{
QString result;
, m_service(service)
, m_mutex(QMutex::Recursive)
, m_editor_widget(editor_widget)
- , m_site_id(0)
, m_edit_mode(false)
- , m_scode(0)
- , m_target_language_code(0)
+ , m_segments(TextSegmentList::create(service, this))
{
qRegisterMetaType<pointer>();
+ m_service->attach(this);
+
connect(socket, SIGNAL(textMessageReceived(QString const&)),
this, SLOT(onTextMessageReceived(QString const&)));
connect(socket, SIGNAL(binaryMessageReceived(QByteArray const&)),
TM::SocketConnection::~SocketConnection()
{
m_editor_widget->detach(this);
+ m_service->detach(this);
+}
+
+TM::TextSegmentList::pointer TM::SocketConnection::segment_list()
+{
+ return m_segments;
+}
+
+/*!
+ * \brief サーバからの文検索の結果、呼び出されます。
+ */
+void TM::SocketConnection::insert_sentence(
+ QString sstring, QString tstring,
+ quint32 previous_crc, quint32 next_crc, QByteArray json)
+{
+ QList<TextSentence::pointer> sentences =
+ m_segments->find_sentences(sstring);
+
+ for(TextSentence::pointer s : sentences)
+ {
+ TextSegment::pointer current = m_segments->current();
+ if(s->parent() == current) continue;
+
+ s->set_tsentence(previous_crc, next_crc, tstring, json);
+ m_editor_widget->set_segment(s->parent());
+ }
}
QWebSocket* TM::SocketConnection::socket()
/*!
* \brief データベースへセンテンスの登録とブラウザへの反映を行います。
- * \param segment_id セグメントのID。
- * \param index センテンスのインデックス。
*
* エディタから呼び出されます。
*/
-void TM::SocketConnection::save_sentence(int segment_id, int index)
+void TM::SocketConnection::save_sentence(TextSentence::pointer sentence)
{
- assert(m_site_id);
+ assert(sentence->ssentence());
+ if(!sentence->tsentence()) return;
- // セグメントの検索。
- segment_map_iterator it = m_segments.find(segment_id);
- assert(it != m_segments.end());
- TextSegment::pointer segment = it.value();
+ // データベースへ登録。
+ m_service->insert_sentence(sentence);
- // センテンスの検索。
- TextSentence::pointer sentence = segment->at(index);
- assert(sentence->source_sentence());
- if(!sentence->target_sentence()) return;
+ // 一致する文を検索。
+ QString sstring = sentence->ssentence()->to_string();
+ sstring = m_service->normalize(sentence->scode(), sstring);
+ QList<TextSentence::pointer> sentences =
+ m_segments->find_sentences(sstring);
- // 言語コード。
- int scode = m_editor_widget->source_language();
- int tcode = m_editor_widget->target_language();
+ // センテンスへ登録。
+ QString tstring = sentence->tsentence()->to_string();
+ QByteArray json = sentence->to_json();
- // データベースへ登録。
- m_service->insert_sentence(segment_id, index,
- m_url.host(), scode, tcode, sentence, pointer(this));
+ quint32 previous_crc = 0;
+ quint32 next_crc = 0;
+ if(sentence->previous()) previous_crc = sentence->previous()->crc();
+ if(sentence->next()) next_crc = sentence->next()->crc();
+
+ for(TextSentence::pointer s : sentences)
+ s->set_tsentence(previous_crc, next_crc, tstring, json);
// ブラウザへ反映。
- set_segment(segment_id, segment->to_html());
+ sentences.append(sentence);
+ for(TextSentence::pointer s : sentences)
+ {
+ TextSegment::pointer segment = s->parent();
+ set_segment(segment->segment_id(), segment->to_html());
+ }
}
/*!
*
* エディタから呼び出されます。
* 呼び出されたとき、訳文はありません。
- * そこで、clear系がサービスに依頼する時は、原文を引数として渡します。
+ * そこで、remove系がサービスに依頼する時は、原文を引数として渡します。
*/
-void TM::SocketConnection::remove_sentence(int segment_id, int index)
+void TM::SocketConnection::remove_sentence(TextSentence::pointer sentence)
{
- assert(m_site_id);
-
- // セグメントの検索。
- segment_map_iterator it = m_segments.find(segment_id);
- assert(it != m_segments.end());
- TextSegment::pointer segment = it.value();
+ assert(sentence->ssentence());
- // センテンスの検索。
- 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();
-
+ sentence->set_tsentence(Text::pointer());
// データベースとサーバへ反映。
- m_service->remove_sentence(m_url.host(), scode, tcode, sentence);
+ m_service->remove_sentence(sentence);
// ブラウザへ反映。
- set_segment(segment_id, segment->to_html());
+ TextSegment::pointer segment = sentence->parent();
+ set_segment(segment->segment_id(), segment->to_html());
}
/*!
- * \brief TM::SocketConnection::sentence_found
- * \param segment_id
- * \param index
- * \param result データベースが返す訳文。
+ * \brief データベース、サーバ検索の結果、呼び出されます。
+ * \param data データベースが返す訳文。
+ *
+ * サーバの検索結果の場合、訳文IDは、まだ付いていない。
*/
void TM::SocketConnection::sentence_found(
- int segment_id, int index, sentence_data_type::pointer result)
+ sentence_data_type result,
+ TextSentence::pointer sentence)
{
- if(!result) return; // 検索がヒットしなかった場合、何もしない。
+ assert(sentence);
- segment_map_iterator it = m_segments.find(segment_id);
- assert(it != m_segments.end());
- TextSegment::pointer segment = it.value();
+ sentence->set_tsentence(result.previous_crc, result.next_crc,
+ result.sentence, result.json);
- 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);
- }
+ // ブラウザに送信
+ TextSegment::pointer segment = sentence->parent();
+ int segment_id = segment->segment_id();
+
+ set_segment(segment_id, segment->to_html());
}
-void TM::SocketConnection::sentence_inserted(int segment_id, int index,
- quint32 source_id, quint32 target_id)
+void TM::SocketConnection::sentence_inserted(TextSentence::pointer)
{
- 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)
if(!json.contains("segment_id")) return;
int segment_id = json["segment_id"].toString().toInt();
- segment_map_iterator it = m_segments.find(segment_id);
- assert(it != m_segments.end());
- TextSegment::pointer segment = it.value();
+ TextSegment::pointer segment = m_segments->at(segment_id);
+ assert(segment);
+ if(!segment) return;
- if(segment != m_current_segment)
- {
- m_current_segment = segment;
- m_editor_widget->set_segment(m_current_segment);
- }
+ // 編集中のセグメントに変更があった場合のみ、エディタにセット。
+ if(m_segments->set_current(segment)) m_editor_widget->set_segment(segment);
}
/*!
m_editor_widget->attach(this);
m_editor_widget->set_edit_mode(m_edit_mode);
- if(m_current_segment) m_editor_widget->set_segment(m_current_segment);
+ if(m_segments->current()) m_editor_widget->set_segment(m_segments->current());
}
void TM::SocketConnection::do_blur(QJsonObject const &)
set_edit_mode(m_editor_widget->edit_mode());
- 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());
+ m_segments->set_scode(m_editor_widget->source_language());
+ m_segments->set_tcode(m_editor_widget->target_language());
+ qDebug() << QUrl(json["url"].toString());
+ m_segments->set_url(QUrl(json["url"].toString()));
}
/*!
{
assert(json.contains("segment_id"));
int segment_id = json["segment_id"].toString().toInt();
- assert(!m_segments.contains(segment_id));
+ assert(!m_segments->contains(segment_id));
assert(json.contains("html"));
QString html = json["html"].toString();
// セグメントの挿入。
- TextSegment::pointer segment = TextSegment::create(
- m_service, m_scode, segment_id, html);
- m_segments.insert(segment_id, segment);
+ TextSegment::pointer segment = m_segments->append(segment_id, html);
// センテンス完全一致訳文の検索。
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));
- }
+ m_service->find_sentence(sentence);
}
void TM::SocketConnection::onTextMessageReceived(QString const &message)
EditorWidget *editor_widget, QWebSocket *socket);
virtual ~SocketConnection();
+ //
+ TextSegmentList::pointer segment_list();
+ void insert_sentence(QString sstring, QString tstring,
+ quint32 previous_crc, quint32 next_crc,
+ QByteArray json = QByteArray());
+
+ // ブラウザとの通信。
QWebSocket* socket();
void send_message(QString const &message);
void send_message(QJsonObject const &json);
void set_edit_mode(bool edit_mode);
- void save_sentence(int segment_id, int index);
- void remove_sentence(int segment_id, int index);
+ void save_sentence(TextSentence::pointer sentence);
+ void remove_sentence(TextSentence::pointer sentence);
+
+ void sentence_found(sentence_data_type result,
+ TextSentence::pointer sentence);
+ void sentence_inserted(TextSentence::pointer sentence);
- void sentence_found(
- int segment_id, int index, sentence_data_type::pointer result);
- void sentence_inserted(int segment_id, int index,
- quint32 source_id, quint32 target_id);
+ QUrl url() const;
signals:
- //void editCmd(int id, QString html);
public slots:
void changeEditMode(bool edit_mode);
Service *m_service;
QMutex m_mutex;
- QUrl m_url; /*!< 読み込んでいるウェブページのURL */
- int m_site_id;
-
bool m_edit_mode;
- int m_scode;
- int m_target_language_code;
EditorWidget *m_editor_widget;
- QMap<int, TextSegment::pointer> m_segments;
- TextSegment::pointer m_current_segment;
+ TextSegmentList::pointer m_segments;
};
class SocketServer : public QObject
// sentence_data_type ---------------------------------------------------------
TM::sentence_data_type::sentence_data_type()
- : sentence_id(0)
+ : scode(0)
+ , tcode(0)
+ , sentence_id(0)
, source_id(0)
, crc(0)
, previous_crc(0)
, next_crc(0)
, user_id(0)
, time(0)
-
- , quality(0)
-{
-}
-
-bool TM::sentence_data_type::operator < (sentence_data_type const &rhs)
-{
- return quality < rhs.quality;
-}
-
-bool TM::sentence_data_type::operator == (sentence_data_type const &rhs)
-{
- return sentence == rhs.sentence;
-}
-
-TM::sentence_data_type::pointer TM::sentence_data_type::create()
{
- return pointer(new sentence_data_type());
}
// WordLink -------------------------------------------------------------------
// TextSentence ---------------------------------------------------------------
-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_crc(crc)
- , m_previous_crc(0)
- , m_next_crc(0)
+TM::TextSentence::TextSentence(
+ int index, Text::pointer ssentence, parent_pointer parent)
+ : m_index(index)
+ , m_source_sentence(ssentence)
, m_source_id(0)
+ , m_target_id(0)
+ , m_quality(0)
+ , m_parent(parent)
+{
+}
+
+Text::pointer TM::TextSentence::ssentence() { return m_source_sentence; }
+
+Text::pointer TM::TextSentence::tsentence() { return m_target_sentence; }
+
+void TM::TextSentence::set_tsentence(Text::pointer tsentence)
+{
+ m_target_sentence = tsentence;
+}
+
+void TM::TextSentence::set_tsentence(QString tsentence, QByteArray json)
{
+ Service *service = segment_list()->service();
+ int code = tcode();
+ Text::pointer sentences = service->divide_into_sentences(code, tsentence);
+ assert(sentences->size() == 1);
+ Text::pointer words = service->divide_into_words(code, sentences->begin());
+
+ m_target_sentence = words;
+ if(!json.isEmpty()) set_json(json);
+
+ m_quality = 100;
+}
+
+/*!
+ * \brief 引数として与えられた訳文の品質が上回る場合のみ、訳文をセットします。
+ * \param previous_crc 対訳の前方CRC
+ * \param next_crc 対訳の後方CRC
+ */
+void TM::TextSentence::set_tsentence(quint32 previous_crc_, quint32 next_crc_,
+ QString tstring, QByteArray json)
+{
+ // 訳文の品質を評価する。
+ int quality = 100;
+ if(previous_crc_ == previous_crc() && next_crc_ == next_crc()) quality = 101;
+
+ // 訳文をセット。
+ if(m_quality <= quality)
+ {
+ set_tsentence(tstring, json);
+ m_quality = quality;
+ }
}
-Text::pointer TM::TextSentence::source_sentence() { return m_source_sentence; }
+TM::sentence_data_type TM::TextSentence::ssentence_data()
+{
+ sentence_data_type result;
+
+ QString sstring = ssentence()->to_string();
+
+ result.site = host();
+ result.scode = scode();
+ result.tcode = tcode();
+
+ result.sentence_id = source_id();
+ result.source_id = 0;
+ result.sentence = sstring;
+ result.json = QByteArray();
+ result.crc = crc();
+ result.previous_crc = previous_crc();
+ result.next_crc = next_crc();
+ result.user_id = 0;
+ result.time = 0;
+
+ Service *service = segment_list()->service();
+ // 原文の単語情報を埋める。
+ Text::pointer sentences = service->divide_into_sentences(scode(), sstring);
+ assert(sentences->size() == 1);
+ Text::pointer words = service->divide_into_words(scode(), sentences->begin());
+ for(Text::pointer word = words->begin(); word; word = word->next())
+ {
+ QString w = service->normalize(scode(), word->to_string());
+ result.words.append(w);
+ }
-Text::pointer TM::TextSentence::target_sentence() { return m_target_sentence; }
+ return result;
+}
-void TM::TextSentence::set_target_sentence(Text::pointer target_sentence)
+TM::sentence_data_type TM::TextSentence::tsentence_data()
{
- m_target_sentence = target_sentence;
+ sentence_data_type result;
+ if(!tsentence()) return result;
+
+ QString tstring = tsentence()->to_string();
+
+ result.site = host();
+ result.scode = scode();
+ result.tcode = tcode();
+
+ result.sentence_id = target_id();
+ result.source_id = source_id();
+ result.sentence = tstring;
+ result.json = to_json();
+ result.crc = crc();
+ result.previous_crc = 0;
+ result.next_crc = 0;
+ result.user_id = 0;
+ result.time = 0;
+
+ return result;
}
TM::WordLinker* TM::TextSentence::linker() { return &m_linker; }
return result;
}
-int TM::TextSentence::segment_id() const { return m_segment_id; }
+/*!
+ * \brief ホスト名を返します。
+ */
+QString TM::TextSentence::host()
+{
+ QString result;
+ TextSegmentList::pointer segments = segment_list();
+ assert(segments);
+ if(segments) result = segments->host();
+ return result;
+}
+
+/*!
+ * \brief 原文の言語コードを返します。
+ */
+int TM::TextSentence::scode()
+{
+ int result = 0;
+ TextSegmentList::pointer segments = segment_list();
+ assert(segments);
+ if(segments) result = segments->scode();
+ return result;
+}
+
+/*!
+ * \brief 訳文の言語コードを返します。
+ */
+int TM::TextSentence::tcode()
+{
+ int result = 0;
+ TextSegmentList::pointer segments = segment_list();
+ assert(segments);
+ if(segments) result = segments->tcode();
+ return result;
+}
+
+int TM::TextSentence::segment_id()
+{
+ assert(parent());
+ return parent()->segment_id();
+}
int TM::TextSentence::index() const { return m_index; }
-quint32 TM::TextSentence::crc() const { return m_crc; }
+TM::TextSegmentList::pointer TM::TextSentence::segment_list()
+{
+ TextSegmentList::pointer result;
+ parent_pointer segment = parent();
+ if(segment) result = segment->parent();
+ return result;
+}
+
+TM::TextSegment::pointer TM::TextSentence::parent()
+{
+ TextSegment::pointer result;
+ if(!m_parent.expired()) result = m_parent.lock();
+ return result;
+}
+
+TM::TextSentence::pointer TM::TextSentence::previous()
+{
+ assert(0 <= m_index);
-quint32 TM::TextSentence::previous_crc() const { return m_previous_crc; }
+ pointer result;
+ TextSegment::pointer parent;
+ if(!m_parent.expired()) parent = m_parent.lock();
-quint32 TM::TextSentence::next_crc() const { return m_next_crc; }
+ if(m_index && parent) result = parent->at(m_index - 1);
-void TM::TextSentence::set_previous_crc(quint32 crc) { m_previous_crc = crc; }
+ return result;
+}
+
+TM::TextSentence::pointer TM::TextSentence::next()
+{
+ assert(0 <= m_index);
+ pointer result;
-void TM::TextSentence::set_next_crc(quint32 crc) { m_next_crc = crc; }
+ TextSegment::pointer parent;
+ if(!m_parent.expired()) parent = m_parent.lock();
+ if(parent && m_index < parent->size()) result = parent->at(m_index + 1);
+
+ return result;
+}
quint32 TM::TextSentence::source_id() const { return m_source_id; }
}
/*!
+ * \brief 原文のCRCを返します。
+ */
+quint32 TM::TextSentence::crc()
+{
+ Service *service = segment_list()->service();
+ return service->crc32(scode(), m_source_sentence->to_string());
+}
+
+quint32 TM::TextSentence::previous_crc()
+{
+ pointer previous_ = previous();
+ if(previous_) return previous_->crc();
+ return 0;
+}
+
+quint32 TM::TextSentence::next_crc()
+{
+ pointer next_ = next();
+ if(next_) return next_->crc();
+ return 0;
+}
+
+/*!
* \brief 訳語候補を追加します。
*/
-void TM::TextSentence::append(sentence_data_type::pointer sentence)
+void TM::TextSentence::append(sentence_data_type const &sentence)
{
m_candidates.append(sentence);
}
/*!
* \brief 訳語候補のリストを返します。
*/
-QList<TM::sentence_data_type::const_pointer> const& TM::TextSentence::candidates() const
+const QList<TM::sentence_data_type> &TM::TextSentence::candidates() const
{
return m_candidates;
}
}
TM::TextSentence::pointer TM::TextSentence::create(
- int segment_id, int index, quint32 crc, Text::pointer source_sentence)
+ int index, Text::pointer ssentence, parent_pointer parent)
{
- return pointer(new TextSentence(segment_id, index, crc, source_sentence));
+ assert(parent);
+ pointer result(new TextSentence(index, ssentence, parent));
+ result->m_self = result;
+ return result;
}
// TextSegment ----------------------------------------------------------------
-TM::TextSegment::TextSegment(
- Service *service, int scode, int segment_id, QString source)
- : m_segment_id(segment_id)
- , m_document(source.toUtf8())
+TM::TextSegment::TextSegment(int segment_id, parent_weak_pointer parent)
+ : m_parent(parent)
+ , m_segment_id(segment_id)
{
- 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());
- 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);
- 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 next_crc = 0;
- for(int i = m_sentences.size() - 1; 0 <= i; i--)
- {
- TextSentence::pointer sentence = m_sentences.at(i);
- sentence->set_next_crc(next_crc);
- next_crc = sentence->crc();
- }
+TM::TextSegment::parent_pointer TM::TextSegment::parent()
+{
+ parent_pointer result;
+ if(!m_parent.expired()) result = m_parent.lock();
+ return result;
}
int TM::TextSegment::segment_id() const { return m_segment_id; }
TM::TextSentence::pointer TM::TextSegment::at(int index)
{
- assert(0 <= index && index < m_sentences.size());
- return m_sentences.at(index);
+ if(0 <= index && index < m_sentences.size()) return m_sentences.at(index);
+ return TextSentence::pointer();
}
TM::TextSegment::iterator TM::TextSegment::begin() { return m_sentences.begin(); }
return result;
}
+void TM::TextSegment::set_html(QString html)
+{
+ m_document.set_content(html.toUtf8());
+ HtmlNode body = m_document.first("html").first("body");
+ TextConverter tc;
+ m_text = tc.to_text(HtmlRange(body, body));
+
+ assert(!m_parent.expired());
+ Service *service = m_parent.lock()->service();
+ int scode = m_parent.lock()->scode();
+
+ 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);
+ TextSentence::pointer sentence =
+ TextSentence::create(m_sentences.size(), words, m_self.lock());
+ m_sentences.append(sentence);
+ }
+}
+
/*!
* \brief セグメント全体をHTML文字列に変換します。
*/
QString result;
for(TextSentence::pointer sentence : m_sentences)
{
- if(!sentence->target_sentence())
+ if(!sentence->tsentence())
result += to_html_from_source(sentence);
else result += to_html_from_target(sentence);
result += "\r\n";
{
HtmlConverter hc;
- UserData *ud = sentence->source_sentence()->data().get();
+ UserData *ud = sentence->ssentence()->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();
+ Text::pointer source_sentence = sentence->ssentence();
if(!source_sentence) return "";
for(Text::pointer word = source_sentence->begin(); word; word = word->next())
HtmlConverter hc;
WordLinker* linker = sentence->linker();
- UserData *ud = sentence->source_sentence()->data().get();
+ UserData *ud = sentence->ssentence()->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();
+ Text::pointer target_sentence = sentence->tsentence();
if(!target_sentence) return "";
for(Text::pointer word = target_sentence->begin(); word; word = word->next())
}
TM::TextSegment::pointer TM::TextSegment::create(
- Service *service, int scode, int segment_id, QString source)
+ int segment_id, QString html, parent_weak_pointer parent)
{
- return pointer(new TextSegment(service, scode, segment_id, source));
+ pointer result(new TextSegment(segment_id, parent));
+ result->m_self = result;
+ result->set_html(html);
+ return result;
}
// TextSegmentList ------------------------------------------------------------
-TM::TextSegmentList::TextSegmentList(Service *service)
+TM::TextSegmentList::TextSegmentList(
+ Service *service, SocketConnection *connection)
: m_service(service)
+ , m_connection(connection)
+ , m_scode(0)
+ , m_tcode(0)
{
}
+bool TM::TextSegmentList::contains(int segment_id)
+{
+ return segment_id < m_segments.size();
+}
+
TM::TextSegment::pointer TM::TextSegmentList::at(int segment_id)
{
- return m_segments.at(segment_id);
+ QMap<int, TextSegment::pointer>::iterator it = m_segments.find(segment_id);
+ assert(it != m_segments.end());
+ if(it == m_segments.end()) return TextSegment::pointer();
+ return it.value();
}
-TM::TextSegment::pointer TM::TextSegmentList::append(int scode, QString html_source)
+TM::TextSegment::pointer TM::TextSegmentList::append(int segment_id, QString html_source)
{
- //TextSegment::create(m_service, )
- return TextSegment::pointer();
+ TextSegment::pointer segment =
+ TextSegment::create(segment_id, html_source, m_self);
+ // m_source_mapを埋める
+ for(int i = 0; i < segment->size(); i++)
+ {
+ TextSentence::pointer sentence = segment->at(i);
+ QString ssentence = m_service->normalize(sentence->scode(), sentence->ssentence()->to_string());
+ m_source_map.insertMulti(ssentence, sentence);
+ }
+ m_segments.insert(segment_id, segment);
+ return segment;
}
+int TM::TextSegmentList::size() const { return m_segments.size(); }
+QList<TM::TextSentence::pointer>
+TM::TextSegmentList::find_sentences(QString ssentence)
+{
+ return m_source_map.values(ssentence);
+}
+TM::TextSegment::pointer TM::TextSegmentList::current()
+{
+ return m_current_segment;
+}
+/*!
+ * \brief 引数として与えられたセグメントを編集中としてマークします。
+ * \return 既に編集中とマークされていた場合、falseを返す。
+ */
+bool TM::TextSegmentList::set_current(TextSegment::pointer segment)
+{
+ if(m_current_segment == segment) return false;
+
+ m_current_segment = segment;
+ return true;
+}
+
+TM::SocketConnection* TM::TextSegmentList::connection() { return m_connection; }
+
+int TM::TextSegmentList::scode() const { assert(m_scode); return m_scode; }
+
+int TM::TextSegmentList::tcode() const { assert(m_tcode); return m_tcode; }
+void TM::TextSegmentList::set_scode(int code) { m_scode = code; }
+void TM::TextSegmentList::set_tcode(int code) { m_tcode = code; }
+
+QUrl TM::TextSegmentList::url() const { assert(m_url.isValid()); return m_url; }
+
+void TM::TextSegmentList::set_url(QUrl url) { assert(url.isValid()); m_url = url; }
+
+QString TM::TextSegmentList::host() const { return m_url.host(); }
+
+TM::Service* TM::TextSegmentList::service() { return m_service; }
+
+TM::TextSegmentList::pointer TM::TextSegmentList::create(
+ Service *service, SocketConnection *connection)
+{
+ pointer result(new TextSegmentList(service, connection));
+ result->m_self = result;
+ return result;
+}
#include "html.h"
#include <QString>
+#include <QUrl>
#include <QList>
#include <QMap>
{
class Service;
+class SocketConnection;
+class TextSegment;
+class TextSegmentList;
/*!
* \brief データベースとの通信に使うデータ構造です。
class sentence_data_type
{
public:
- typedef std::shared_ptr<sentence_data_type> pointer;
- typedef std::shared_ptr<sentence_data_type const> const_pointer;
-
sentence_data_type();
- bool operator < (sentence_data_type const &rhs);
- bool operator == (sentence_data_type const &rhs);
-
- static pointer create();
+ // サイトデータベース用
+ QString site;
+ quint32 scode;
+ quint32 tcode;
+ // センテンスデータベース用。
quint32 sentence_id; /*!< DBによって自動的に付与される文ID */
quint32 source_id; /*!< 原文は0固定、訳文は原文のsentence_id */
QString sentence; /*!< 文本体の文字列 */
quint32 user_id; /*!< レコードを更新したユーザーのID */
quint64 time; /*!< 更新時刻 */
- quint32 quality; /*!< 訳文は訳語の品質、原文は0 */
- QList<QPair<int,int> > words; /*!< データベース登録時に、sentenceの単語の範囲を示すリスト */
+ // 索引データベース用。
+ /*!
+ * データベース登録時に、sentenceの単語を示すリスト。
+ * 原文にのみ必要。
+ */
+ QList<QString> words;
+};
+
+/*!
+ * \brief 候補文のためのデータ構造です。
+ */
+class candidate_data_type
+{
+
};
/*!
{
public:
typedef std::shared_ptr<TextSentence> pointer;
+ typedef std::weak_ptr<TextSentence> weak_pointer;
+
+ typedef std::shared_ptr<TextSegment> parent_pointer;
+ typedef std::weak_ptr<TextSegment> parent_weak_pointer;
+
+ typedef std::shared_ptr<TextSegmentList> segment_list_pointer;
private:
- TextSentence(int segment_id, int index, quint32 crc, Text::pointer source_sentence);
+ TextSentence(int index, Text::pointer ssentence, parent_pointer parent);
public:
- Text::pointer source_sentence();
- Text::pointer target_sentence();
- void set_target_sentence(Text::pointer target_sentence);
+ Text::pointer ssentence();
+ Text::pointer tsentence();
+ void set_tsentence(Text::pointer tsentence);
+ void set_tsentence(QString tsentence, QByteArray json = QByteArray());
+ void set_tsentence(quint32 previous_crc_, quint32 next_crc_,
+ QString tstring, QByteArray json = QByteArray());
+ sentence_data_type ssentence_data();
+ sentence_data_type tsentence_data();
WordLinker* linker();
QByteArray to_json();
QList<Text::pointer> find_words_by_range(Text::pointer sentence, int first, int tail);
public:
- int segment_id() const;
+ QString host();
+ int scode();
+ int tcode();
+
+ int segment_id();
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);
+ segment_list_pointer segment_list();
+
+ parent_pointer parent();
+ pointer previous();
+ pointer next();
quint32 source_id() const;
void set_source_id(quint32 source_id);
quint32 target_id() const;
void set_target_id(quint32 source_id);
- void append(sentence_data_type::pointer sentence);
- const QList<sentence_data_type::const_pointer> &candidates() const;
+ quint32 crc();
+ quint32 previous_crc();
+ quint32 next_crc();
+
+ quint32 user_id() { return 0; }
+
+ void append(sentence_data_type const &sentence);
+ QList<sentence_data_type> const & candidates() const;
bool is_loaded() const;
//void set_loaded(bool loaded);
QString debug_dump() const;
- static pointer create(int segment_id, int index,
- quint32 crc, Text::pointer source_sentence);
+ static pointer create(int index, Text::pointer ssentence, parent_pointer parent);
private:
Text::pointer m_source_sentence; // 原文
Text::pointer m_target_sentence; // 訳文
WordLinker m_linker; // 単語間のリンク
- int m_segment_id;
- int m_index;
+ quint32 m_source_id; /*!< 原文のID */
+ quint32 m_target_id; /*!< 訳文のID */
- quint32 m_crc;
- quint32 m_previous_crc;
- quint32 m_next_crc;
+ int m_quality; /*!< 訳文の品質 */
- quint32 m_source_id; // 原文のID
- quint32 m_target_id; // 訳文のID
+ QList<sentence_data_type> m_candidates; /*!< 訳文候補 */
- QList<sentence_data_type::const_pointer> m_candidates; /*!< 訳文候補 */
+ parent_weak_pointer m_parent;
+ int m_index; /*!< セグメント内における位置 */
+
+ weak_pointer m_self;
};
/*!
public:
int type() const;
HtmlNode node();
+ UserData::pointer clone();
QString debug_dump() const;
{
public:
typedef std::shared_ptr<TextSegment> pointer;
+ typedef std::weak_ptr<TextSegment> weak_pointer;
+
typedef QList<TextSentence::pointer> storage_type;
typedef storage_type::iterator iterator;
+ typedef std::shared_ptr<TextSegmentList> parent_pointer;
+ typedef std::weak_ptr<TextSegmentList> parent_weak_pointer;
+
private:
- TextSegment(Service *service, int scode, int segment_id, QString source);
+ TextSegment(int segment_id, parent_weak_pointer parent);
public:
+ parent_pointer parent();
int segment_id() const;
int size() const;
iterator begin();
iterator end();
+ void set_html(QString html);
QString to_html();
QString to_html_from_source(TextSentence::pointer sentence);
QString to_html_from_target(TextSentence::pointer sentence);
HtmlNode::pointer find_html_node_by_offset(int offset);
- static pointer create(
- Service *service, int scode, int segment_id, QString source);
+ static pointer create(int segment_id, QString html, parent_weak_pointer parent);
private:
// リスト用
// 編集用
storage_type m_sentences; /*!< 編集用センテンスのリスト。 */
+
+ // 親子。
+ parent_weak_pointer m_parent;
+ weak_pointer m_self;
};
/*!
* \brief 一つのHTMLページを表現するクラスです。
*
* オーナーはSocketConnectionです。
+ * 編集スレッド内では、このデータ構造を参照します。
*/
class TextSegmentList
{
public:
- TextSegmentList(Service *service);
+ typedef std::shared_ptr<TextSegmentList> pointer;
+ typedef std::weak_ptr<TextSegmentList> weak_pointer;
+private:
+ TextSegmentList(Service *service, SocketConnection *connection);
+
+public:
+ // セグメント・リスト。
+ bool contains(int segment_id);
TextSegment::pointer at(int segment_id);
- TextSegment::pointer append(int scode, QString html_source);
+ TextSegment::pointer append(int segment_id, QString html_source);
+ int size() const;
+
+ // 検索。
+ QList<TextSentence::pointer> find_sentences(QString ssentence);
+
+ // エディタ。
+ TextSegment::pointer current();
+ bool set_current(TextSegment::pointer segment);
+
+ SocketConnection* connection();
+
+ // 言語コード。
+ int scode() const;
+ int tcode() const;
+ void set_scode(int code);
+ void set_tcode(int code);
+
+ // URL
+ QUrl url() const;
+ void set_url(QUrl url);
+ QString host() const;
+
+ // 親子。
+ Service* service();
+
+ static pointer create(Service *service, SocketConnection *connection);
private:
Service *m_service;
- QList<TextSegment::pointer> m_segments;
- int m_current_segment; /*!< 編集中のセグメントを表す。 */
-
+ SocketConnection *m_connection;
int m_scode; /*!< 原文言語コード。*/
int m_tcode; /*!< 訳文言語コード。 */
+ QUrl m_url;
- /*! 原文IDと編集文のマルチ・マップ。 */
- QMap<int, TextSentence::pointer> m_source_id_map;
-};
+ QMap<int, TextSegment::pointer> m_segments; /*!< HTML内のdata-wordring-segmentの値とセグメントのマップ */
+ TextSegment::pointer m_current_segment; /*!< 編集中のセグメントを表す。 */
+ /*! 正規化原文と編集文のマルチ・マップ。 */
+ QMap<QString, TextSentence::pointer> m_source_map;
+
+ weak_pointer m_self;
+};
} // namespace TM
--- /dev/null
+examples/unidiff
+examples/unistrdiff
+examples/strdiff
+examples/strdiff_cp
+examples/strdiff3
+examples/intdiff
+examples/bdiff
+examples/intdiff3
+examples/patch
+examples/fpatch
+examples/st2ses
+test/strdiff3_test
+test/*/*/*_
+*.o
+*.orig
+*~
+.semantic.cache
+.DS_Store
+*.swp
+GPATH
+GRTAGS
+GSYMS
+GTAGS
+TAGS
+dtl_test
+.sconsign.dblite
+.sconf_temp
+config.log
+^\#
+\#$
+\.\#
--- /dev/null
+Tatsuhiko Kubo <cubicdaiya@gmail.com>
+Jan Weiß <jan@geheimwerk.de>
--- /dev/null
+In short, Diff Template Library is distributed under so called "BSD license",
+
+Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+2013-01-10 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add printSES function for custom Printer Thanks to Hu Shubin
+
+ * remove files related mercurial
+
+ * 1.18 released
+
+2012-11-18 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add version-string variable
+
+ * replace get_current_dir_name to getcwd(get_current_dir_name is not in Mountain Lion)
+
+ * fix small typo
+
+ * 1.17 released
+
+2012-10-26 Tatsuhiko Kubo <cubicdaiya@gmail.com>, Jan Weiß <jan@geheimwerk.de>
+
+ * Improving comments
+
+ * rewrite README with markdown-style
+
+ * support gtest1.6
+
+ * allow clients to handle if DTL swaps the arrays it is passed
+
+ * fix incorrect results for SES_COMMON when swapped
+
+ * add explicit initialization of ses
+
+ * implement option to force the order of sesElem objects in the ses
+
+ * fix problem -> issues/4(test succeeds when result-file does not exist)
+
+ * 1.16 released
+
+2011-11-10 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * remove unused variable
+
+ * 1.15 released
+
+2011-06-18 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * fix Issue #7 (isOnlyOneOperation returns incorrect value)
+
+ * 1.14 released
+
+2011-04-10 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add check for libraries with SCons
+
+ * add installer with SCons
+
+ * add sample of diff(bdiff)
+
+ * 1.13 released
+
+2011-01-23 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * forget to add template parameter of stream for print methods of printUnifiedFormat
+
+ * 1.12 released
+
+2011-01-01 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add template parameter of stream for print methods
+
+ * function of importing ses
+
+ * 1.11 released
+
+2010-11-23 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * use static_cast instead of C-style cast
+
+ * remove specifyConfliction
+
+ * 1.10 released
+
+2010-09-20 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * fix bug of specifyConfliction
+
+ * add parameter for Diff3's comparator
+
+ * fix the bug which uniPatch fails
+
+ * add debug option for examples
+
+ * divide test sources
+
+ * 1.09 released
+
+2010-08-29 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * fix the problem that comparator parameter is completely ignored.
+
+ * remove the warning 'comparison of unsigned expression >= 0 is always true' ( use -W )
+
+ * move directory 'src' to 'dtl'
+
+ * remove Makefile for examples and test
+
+ * 1.08 released
+
+2010-07-11 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * fix potential risk on 64bit environment(size of 'int' and size of 'size_t')
+
+ * add scons
+
+ * fix bug of specifyConfliction
+
+ * fix memory leak when onlyEditDistance is true
+
+ * change indent size from 2 to 4
+
+ * 1.07 released
+
+2010-05-01 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add specifyConfliction
+
+ * 1.06 released
+
+2010-04-13 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * constructors takes referenced parameter
+
+ * fix seg fault bug (http://code.google.com/p/dtl-cpp/issues/detail?id=2)
+
+ * 1.05 released
+
+2009-12-01 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add test programs with googletest
+
+ * add sample of diff(uintdiff)
+
+ * 1.04 released
+
+2009-10-02 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add function getLcsVec
+
+ * divide header files
+
+ * 1.03 released
+
+2009-09-08 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * rename editType to edit_t
+
+ * add print functions
+
+ * add functor of compare
+
+ * use 'using' declaration
+
+ * refactoring
+
+ * add ignore patterns for Mercurial
+
+ * 1.02 released.
+
+2009-08-08 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * append appropriate const keyword
+
+ * refactoring
+
+ * 1.01 released.
+
+2009-07-04 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * resolve problem memory leak occurs when default copy constructor set in motion.
+
+ * 1.00 released.
+
+2009-06-08 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * enhance readability
+
+ * change behavior when conflicted
+
+ * decliment parameter for patch function
+
+ * 0.07 released.
+
+2009-05-08 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add flag for recording only editdistance
+
+ * add sample of unidiff for string (unistrdiff)
+
+ * add unserious diff. ver 0.03 has this feture.
+
+ * fix bug for merge
+
+ * add function getUniHunks
+
+ * 0.06 released.
+
+2008-12-12 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add sample of diff3 (strdiff3, intdiff3)
+
+ * add diff3
+
+ * + -> - to - -> + with Unified Diff Format
+
+ * add function uniPatch. this function can patch with Unified Diff Format Data Structure
+
+ * dtl Import Unified Diff Format Data Structure from unidiff.cpp.
+
+ * fix bug. function check whether file exists(common.cpp)
+
+ * 0.05 released.
+
+2008-11-10 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * Improves accuracy of diff
+
+ * fix serious memory bug for patch
+
+ * changed type of argument in function fileExists(common.hpp, common.cpp)
+
+ * 0.04 released.
+
+2008-11-06 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * add erorr check for sample programs
+
+ * 0.03 released.
+
+2008-10-31 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * rename ChangLog to ChangeLog
+
+ * modifiy README
+
+ * output OK message on patch and fpatch
+
+ * 0.02 released.
+
+2008-10-30 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+
+ * 0.01 released.
+
--- /dev/null
+DTL
+===
+
+What is dtl?
+------------
+
+dtl is a diff template library written in C++.
+
+Copyright(C) 2013 Tatsuhiko Kubo <<cubicdaiya@gmail.com>>
+
+dtl can compare two sequences with arbitrary type, however the sequences must support the random access iterator.
+Please read the document at the following URL for details:
+
+<http://code.google.com/p/dtl-cpp/>
+
+
+License
+-------
+
+Please read the file COPYING or the document at the following URL concerning the license for dtl:
+
+<http://code.google.com/p/dtl-cpp/source/browse/COPYING>
+
+
+How to use dtl
+--------------
+
+To start using this library, all you need to do is include dtl.hpp.
+
+ #include "dtl/dtl.hpp"
+
+If you are interested in the details, please visit the following URL:
+
+<http://code.google.com/p/dtl-cpp/wiki/Tutorial>
--- /dev/null
+import os
+
+# install script
+
+prefix = ARGUMENTS.get('prefix', '/usr/local')
+headers = Glob('dtl/*.hpp')
+Alias('install', Install(os.path.join(prefix, 'dtl', 'include'), headers))
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_DIFF_H
+#define DTL_DIFF_H
+
+namespace dtl {
+
+ /**
+ * diff class template
+ * sequence must support random_access_iterator.
+ */
+ template <typename elem, typename sequence = vector< elem >, typename comparator = Compare< elem > >
+ class Diff
+ {
+ private :
+ dtl_typedefs(elem, sequence)
+ sequence A;
+ sequence B;
+ size_t M;
+ size_t N;
+ size_t delta;
+ size_t offset;
+ long long *fp;
+ long long editDistance;
+ Lcs< elem > lcs;
+ Ses< elem > ses;
+ editPath path;
+ editPathCordinates pathCordinates;
+ bool swapped;
+ bool huge;
+ bool trivial;
+ bool editDistanceOnly;
+ uniHunkVec uniHunks;
+ comparator cmp;
+ public :
+ Diff () {}
+
+ Diff (const sequence& a,
+ const sequence& b) : A(a), B(b), ses(false) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ bool deletesFirst) : A(a), B(b), ses(deletesFirst) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ const comparator& comp) : A(a), B(b), ses(false), cmp(comp) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ bool deleteFirst,
+ const comparator& comp) : A(a), B(b), ses(deleteFirst), cmp(comp) {
+ init();
+ }
+
+ ~Diff() {}
+
+ long long getEditDistance () const {
+ return editDistance;
+ }
+
+ Lcs< elem > getLcs () const {
+ return lcs;
+ }
+
+ elemVec getLcsVec () const {
+ return lcs.getSequence();
+ }
+
+ Ses< elem > getSes () const {
+ return ses;
+ }
+
+ uniHunkVec getUniHunks () const {
+ return uniHunks;
+ }
+
+ /* These should be deprecated */
+ bool isHuge () const {
+ return huge;
+ }
+
+ void onHuge () {
+ this->huge = true;
+ }
+
+ void offHuge () {
+ this->huge = false;
+ }
+
+ bool isUnserious () const {
+ return trivial;
+ }
+
+ void onUnserious () {
+ this->trivial = true;
+ }
+
+ void offUnserious () {
+ this->trivial = false;
+ }
+
+ void onOnlyEditDistance () {
+ this->editDistanceOnly = true;
+ }
+
+ /* These are the replacements for the above */
+ bool hugeEnabled () const {
+ return huge;
+ }
+
+ void enableHuge () {
+ this->huge = true;
+ }
+
+ void disableHuge () {
+ this->huge = false;
+ }
+
+ bool trivialEnabled () const {
+ return trivial;
+ }
+
+ void enableTrivial () const {
+ this->trivial = true;
+ }
+
+ void disableTrivial () {
+ this->trivial = false;
+ }
+
+ void editDistanceOnlyEnabled () {
+ this->editDistanceOnly = true;
+ }
+
+ /**
+ * patching with Unified Format Hunks
+ */
+ sequence uniPatch (const sequence& seq) {
+ elemList seqLst(seq.begin(), seq.end());
+ sesElemVec shunk;
+ sesElemVec_iter vsesIt;
+ elemList_iter lstIt = seqLst.begin();
+ long long inc_dec_total = 0;
+ long long gap = 1;
+ for (uniHunkVec_iter it=uniHunks.begin();it!=uniHunks.end();++it) {
+ joinSesVec(shunk, it->common[0]);
+ joinSesVec(shunk, it->change);
+ joinSesVec(shunk, it->common[1]);
+ it->a += inc_dec_total;
+ inc_dec_total += it->inc_dec_count;
+ for (long long i=0;i<it->a - gap;++i) {
+ ++lstIt;
+ }
+ gap = it->a + it->b + it->inc_dec_count;
+ vsesIt = shunk.begin();
+ while (vsesIt!=shunk.end()) {
+ switch (vsesIt->second.type) {
+ case SES_ADD :
+ seqLst.insert(lstIt, vsesIt->first);
+ break;
+ case SES_DELETE :
+ if (lstIt != seqLst.end()) {
+ lstIt = seqLst.erase(lstIt);
+ }
+ break;
+ case SES_COMMON :
+ if (lstIt != seqLst.end()) {
+ ++lstIt;
+ }
+ break;
+ default :
+ // no fall-through
+ break;
+ }
+ ++vsesIt;
+ }
+ shunk.clear();
+ }
+
+ sequence patchedSeq(seqLst.begin(), seqLst.end());
+ return patchedSeq;
+ }
+
+ /**
+ * patching with Shortest Edit Script (SES)
+ */
+ sequence patch (const sequence& seq) const {
+ sesElemVec sesSeq = ses.getSequence();
+ elemList seqLst(seq.begin(), seq.end());
+ elemList_iter lstIt = seqLst.begin();
+ for (sesElemVec_iter sesIt=sesSeq.begin();sesIt!=sesSeq.end();++sesIt) {
+ switch (sesIt->second.type) {
+ case SES_ADD :
+ seqLst.insert(lstIt, sesIt->first);
+ break;
+ case SES_DELETE :
+ lstIt = seqLst.erase(lstIt);
+ break;
+ case SES_COMMON :
+ ++lstIt;
+ break;
+ default :
+ // no through
+ break;
+ }
+ }
+ sequence patchedSeq(seqLst.begin(), seqLst.end());
+ return patchedSeq;
+ }
+
+ /**
+ * compose Longest Common Subsequence and Shortest Edit Script.
+ * The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
+ * described by Sun Wu, Udi Manber and Gene Myers
+ */
+ void compose() {
+
+ if (isHuge()) {
+ pathCordinates.reserve(MAX_CORDINATES_SIZE);
+ }
+
+ long long p = -1;
+ fp = new long long[M + N + 3];
+ fill(&fp[0], &fp[M + N + 3], -1);
+ path = editPath(M + N + 3);
+ fill(path.begin(), path.end(), -1);
+ ONP:
+ do {
+ ++p;
+ for (long long k=-p;k<=static_cast<long long>(delta)-1;++k) {
+ fp[k+offset] = snake(k, fp[k-1+offset]+1, fp[k+1+offset]);
+ }
+ for (long long k=static_cast<long long>(delta)+p;k>=static_cast<long long>(delta)+1;--k) {
+ fp[k+offset] = snake(k, fp[k-1+offset]+1, fp[k+1+offset]);
+ }
+ fp[delta+offset] = snake(static_cast<long long>(delta), fp[delta-1+offset]+1, fp[delta+1+offset]);
+ } while (fp[delta+offset] != static_cast<long long>(N) && pathCordinates.size() < MAX_CORDINATES_SIZE);
+
+ editDistance += static_cast<long long>(delta) + 2 * p;
+ long long r = path[delta+offset];
+ P cordinate;
+ editPathCordinates epc(0);
+
+ // recording edit distance only
+ if (editDistanceOnly) {
+ delete[] this->fp;
+ return;
+ }
+
+ while(r != -1) {
+ cordinate.x = pathCordinates[(size_t)r].x;
+ cordinate.y = pathCordinates[(size_t)r].y;
+ epc.push_back(cordinate);
+ r = pathCordinates[(size_t)r].k;
+ }
+
+ // record Longest Common Subsequence & Shortest Edit Script
+ if (!recordSequence(epc)) {
+ pathCordinates.resize(0);
+ epc.resize(0);
+ p = -1;
+ goto ONP;
+ }
+ delete[] this->fp;
+ }
+
+ /**
+ * print difference between A and B as an SES
+ */
+ template < typename stream >
+ void printSES (stream& out) const {
+ sesElemVec ses_v = ses.getSequence();
+ for_each(ses_v.begin(), ses_v.end(), ChangePrinter< sesElem, stream >(out));
+ }
+
+ void printSES (ostream& out = cout) const {
+ printSES< ostream >(out);
+ }
+
+ /**
+ * print differences given an SES
+ */
+ template < typename stream >
+ static void printSES (const Ses< elem >& s, stream& out) {
+ sesElemVec ses_v = s.getSequence();
+ for_each(ses_v.begin(), ses_v.end(), ChangePrinter< sesElem, stream >(out));
+ }
+
+ static void printSES (const Ses< elem >& s, ostream& out = cout) {
+ printSES< ostream >(s, out);
+ }
+
+ /**
+ * print difference between A and B as an SES with custom printer
+ */
+ template < typename stream, template < typename SEET, typename STRT > class PT >
+ void printSES (stream& out) const {
+ sesElemVec ses_v = ses.getSequence ();
+ for_each (ses_v.begin (), ses_v.end(), PT < sesElem, stream > (out));
+ }
+
+ /**
+ * print difference between A and B in the Unified Format
+ */
+ template < typename stream >
+ void printUnifiedFormat (stream& out) const {
+ for_each(uniHunks.begin(), uniHunks.end(), UniHunkPrinter< sesElem, stream >(out));
+ }
+
+ void printUnifiedFormat (ostream& out = cout) const {
+ printUnifiedFormat< ostream >(out);
+ }
+
+ /**
+ * print unified format difference with given unified format hunks
+ */
+ template < typename stream >
+ static void printUnifiedFormat (const uniHunkVec& hunks, stream& out) {
+ for_each(hunks.begin(), hunks.end(), UniHunkPrinter< sesElem >(out));
+ }
+
+ static void printUnifiedFormat (const uniHunkVec& hunks, ostream& out = cout) {
+ printUnifiedFormat< ostream >(hunks, out);
+ }
+
+ /**
+ * compose Unified Format Hunks from Shortest Edit Script
+ */
+ void composeUnifiedHunks () {
+ sesElemVec common[2];
+ sesElemVec change;
+ sesElemVec ses_v = ses.getSequence();
+ long long l_cnt = 1;
+ long long length = distance(ses_v.begin(), ses_v.end());
+ long long middle = 0;
+ bool isMiddle, isAfter;
+ elemInfo einfo;
+ long long a, b, c, d; // @@ -a,b +c,d @@
+ long long inc_dec_count = 0;
+ uniHunk< sesElem > hunk;
+ sesElemVec adds;
+ sesElemVec deletes;
+
+ isMiddle = isAfter = false;
+ a = b = c = d = 0;
+
+ for (sesElemVec_iter it=ses_v.begin();it!=ses_v.end();++it, ++l_cnt) {
+ einfo = it->second;
+ switch (einfo.type) {
+ case SES_ADD :
+ middle = 0;
+ ++inc_dec_count;
+ adds.push_back(*it);
+ if (!isMiddle) isMiddle = true;
+ if (isMiddle) ++d;
+ if (l_cnt >= length) {
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ isAfter = true;
+ }
+ break;
+ case SES_DELETE :
+ middle = 0;
+ --inc_dec_count;
+ deletes.push_back(*it);
+ if (!isMiddle) isMiddle = true;
+ if (isMiddle) ++b;
+ if (l_cnt >= length) {
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ isAfter = true;
+ }
+ break;
+ case SES_COMMON :
+ ++b;++d;
+ if (common[1].empty() && adds.empty() && deletes.empty() && change.empty()) {
+ if (static_cast<long long>(common[0].size()) < DTL_CONTEXT_SIZE) {
+ if (a == 0 && c == 0) {
+ if (!wasSwapped()) {
+ a = einfo.beforeIdx;
+ c = einfo.afterIdx;
+ } else {
+ a = einfo.afterIdx;
+ c = einfo.beforeIdx;
+ }
+ }
+ common[0].push_back(*it);
+ } else {
+ rotate(common[0].begin(), common[0].begin() + 1, common[0].end());
+ common[0].pop_back();
+ common[0].push_back(*it);
+ ++a;++c;
+ --b;--d;
+ }
+ }
+ if (isMiddle && !isAfter) {
+ ++middle;
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ change.push_back(*it);
+ if (middle >= DTL_SEPARATE_SIZE || l_cnt >= length) {
+ isAfter = true;
+ }
+ adds.clear();
+ deletes.clear();
+ }
+ break;
+ default :
+ // no through
+ break;
+ }
+ // compose unified format hunk
+ if (isAfter && !change.empty()) {
+ sesElemVec_iter cit = it;
+ long long cnt = 0;
+ for (long long i=0;i<DTL_SEPARATE_SIZE && (cit != ses_v.end());++i, ++cit) {
+ if (cit->second.type == SES_COMMON) {
+ ++cnt;
+ }
+ }
+ if (cnt < DTL_SEPARATE_SIZE && l_cnt < length) {
+ middle = 0;
+ isAfter = false;
+ continue;
+ }
+ if (static_cast<long long>(common[0].size()) >= DTL_SEPARATE_SIZE) {
+ long long c0size = static_cast<long long>(common[0].size());
+ rotate(common[0].begin(),
+ common[0].begin() + (size_t)c0size - DTL_SEPARATE_SIZE,
+ common[0].end());
+ for (long long i=0;i<c0size - DTL_SEPARATE_SIZE;++i) {
+ common[0].pop_back();
+ }
+ a += c0size - DTL_SEPARATE_SIZE;
+ c += c0size - DTL_SEPARATE_SIZE;
+ }
+ if (a == 0) ++a;
+ if (c == 0) ++c;
+ if (wasSwapped()) swap(a, c);
+ hunk.a = a;
+ hunk.b = b;
+ hunk.c = c;
+ hunk.d = d;
+ hunk.common[0] = common[0];
+ hunk.change = change;
+ hunk.common[1] = common[1];
+ hunk.inc_dec_count = inc_dec_count;
+ uniHunks.push_back(hunk);
+ isMiddle = false;
+ isAfter = false;
+ common[0].clear();
+ common[1].clear();
+ adds.clear();
+ deletes.clear();
+ change.clear();
+ a = b = c = d = middle = inc_dec_count = 0;
+ }
+ }
+ }
+
+ /**
+ * compose ses from stream
+ */
+ template <typename stream>
+ static Ses< elem > composeSesFromStream (stream& st)
+ {
+ elem line;
+ Ses< elem > ret;
+ long long x_idx, y_idx;
+ x_idx = y_idx = 1;
+ while (getline(st, line)) {
+ elem mark(line.begin(), line.begin() + 1);
+ elem e(line.begin() + 1, line.end());
+ if (mark == SES_MARK_DELETE) {
+ ret.addSequence(e, x_idx, 0, SES_DELETE);
+ ++x_idx;
+ } else if (mark == SES_MARK_ADD) {
+ ret.addSequence(e, y_idx, 0, SES_ADD);
+ ++y_idx;
+ } else if (mark == SES_MARK_COMMON) {
+ ret.addSequence(e, x_idx, y_idx, SES_COMMON);
+ ++x_idx;
+ ++y_idx;
+ }
+ }
+ return ret;
+ }
+
+ private :
+ /**
+ * initialize
+ */
+ void init () {
+ M = distance(A.begin(), A.end());
+ N = distance(B.begin(), B.end());
+ if (M < N) {
+ swapped = false;
+ } else {
+ swap(A, B);
+ swap(M, N);
+ swapped = true;
+ }
+ editDistance = 0;
+ delta = N - M;
+ offset = M + 1;
+ huge = false;
+ trivial = false;
+ editDistanceOnly = false;
+ fp = NULL;
+ }
+
+ /**
+ * search shortest path and record the path
+ */
+ long long snake(const long long& k, const long long& above, const long long& below) {
+ long long r = above > below ? path[(size_t)k-1+offset] : path[(size_t)k+1+offset];
+ long long y = max(above, below);
+ long long x = y - k;
+ while ((size_t)x < M && (size_t)y < N && (swapped ? cmp.impl(B[(size_t)y], A[(size_t)x]) : cmp.impl(A[(size_t)x], B[(size_t)y]))) {
+ ++x;++y;
+ }
+
+ path[(size_t)k+offset] = static_cast<long long>(pathCordinates.size());
+ if (!editDistanceOnly) {
+ P p;
+ p.x = x;p.y = y;p.k = r;
+ pathCordinates.push_back(p);
+ }
+ return y;
+ }
+
+ /**
+ * record SES and LCS
+ */
+ bool recordSequence (const editPathCordinates& v) {
+ sequence_const_iter x(A.begin());
+ sequence_const_iter y(B.begin());
+ long long x_idx, y_idx; // line number for Unified Format
+ long long px_idx, py_idx; // cordinates
+ bool complete = false;
+ x_idx = y_idx = 1;
+ px_idx = py_idx = 0;
+ for (size_t i=v.size()-1;!complete;--i) {
+ while(px_idx < v[i].x || py_idx < v[i].y) {
+ if (v[i].y - v[i].x > py_idx - px_idx) {
+ if (!wasSwapped()) {
+ ses.addSequence(*y, 0, y_idx, SES_ADD);
+ } else {
+ ses.addSequence(*y, y_idx, 0, SES_DELETE);
+ }
+ ++y;
+ ++y_idx;
+ ++py_idx;
+ } else if (v[i].y - v[i].x < py_idx - px_idx) {
+ if (!wasSwapped()) {
+ ses.addSequence(*x, x_idx, 0, SES_DELETE);
+ } else {
+ ses.addSequence(*x, 0, x_idx, SES_ADD);
+ }
+ ++x;
+ ++x_idx;
+ ++px_idx;
+ } else {
+ if (!wasSwapped()) {
+ lcs.addSequence(*x);
+ ses.addSequence(*x, x_idx, y_idx, SES_COMMON);
+ } else {
+ lcs.addSequence(*y);
+ ses.addSequence(*y, y_idx, x_idx, SES_COMMON);
+ }
+ ++x;
+ ++y;
+ ++x_idx;
+ ++y_idx;
+ ++px_idx;
+ ++py_idx;
+ }
+ }
+ if (i == 0) complete = true;
+ }
+
+ if (x_idx > static_cast<long long>(M) && y_idx > static_cast<long long>(N)) {
+ // all recording succeeded
+ } else {
+ // trivial difference
+ if (trivialEnabled()) {
+ if (!wasSwapped()) {
+ recordOddSequence(x_idx, M, x, SES_DELETE);
+ recordOddSequence(y_idx, N, y, SES_ADD);
+ } else {
+ recordOddSequence(x_idx, M, x, SES_ADD);
+ recordOddSequence(y_idx, N, y, SES_DELETE);
+ }
+ return true;
+ }
+
+ // nontrivial difference
+ sequence A_(A.begin() + (size_t)x_idx - 1, A.end());
+ sequence B_(B.begin() + (size_t)y_idx - 1, B.end());
+ A = A_;
+ B = B_;
+ M = distance(A.begin(), A.end());
+ N = distance(B.begin(), B.end());
+ delta = N - M;
+ offset = M + 1;
+ delete[] fp;
+ fp = new long long[M + N + 3];
+ fill(&fp[0], &fp[M + N + 3], -1);
+ fill(path.begin(), path.end(), -1);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * record odd sequence in SES
+ */
+ void inline recordOddSequence (long long idx, long long length, sequence_const_iter it, const edit_t et) {
+ while(idx < length){
+ ses.addSequence(*it, idx, 0, et);
+ ++it;
+ ++idx;
+ ++editDistance;
+ }
+ ses.addSequence(*it, idx, 0, et);
+ ++editDistance;
+ }
+
+ /**
+ * join SES vectors
+ */
+ void inline joinSesVec (sesElemVec& s1, sesElemVec& s2) const {
+ if (!s2.empty()) {
+ for (sesElemVec_iter vit=s2.begin();vit!=s2.end();++vit) {
+ s1.push_back(*vit);
+ }
+ }
+ }
+
+ /**
+ * check if the sequences have been swapped
+ */
+ bool inline wasSwapped () const {
+ return swapped;
+ }
+
+ };
+}
+
+#endif // DTL_DIFF_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_DIFF3_H
+#define DTL_DIFF3_H
+
+namespace dtl {
+
+ /**
+ * diff3 class template
+ * sequence must support random_access_iterator.
+ */
+ template <typename elem, typename sequence = vector< elem >, typename comparator = Compare< elem > >
+ class Diff3
+ {
+ private:
+ dtl_typedefs(elem, sequence)
+ sequence A;
+ sequence B;
+ sequence C;
+ sequence S;
+ Diff< elem, sequence, comparator > diff_ba;
+ Diff< elem, sequence, comparator > diff_bc;
+ bool conflict;
+ elem csepabegin;
+ elem csepa;
+ elem csepaend;
+ public :
+ Diff3 () {}
+ Diff3 (const sequence& a,
+ const sequence& b,
+ const sequence& c) : A(a), B(b), C(c),
+ diff_ba(b, a), diff_bc(b, c),
+ conflict(false) {}
+
+ ~Diff3 () {}
+
+ bool isConflict () const {
+ return conflict;
+ }
+
+ sequence getMergedSequence () const {
+ return S;
+ }
+
+ /**
+ * merge changes B and C into A
+ */
+ bool merge () {
+ if (diff_ba.getEditDistance() == 0) { // A == B
+ if (diff_bc.getEditDistance() == 0) { // A == B == C
+ S = B;
+ return true;
+ }
+ S = C;
+ return true;
+ } else { // A != B
+ if (diff_bc.getEditDistance() == 0) { // A != B == C
+ S = A;
+ return true;
+ } else { // A != B != C
+ S = merge_();
+ if (isConflict()) { // conflict occured
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * compose differences
+ */
+ void compose () {
+ diff_ba.compose();
+ diff_bc.compose();
+ }
+
+ private :
+ /**
+ * merge implementation
+ */
+ sequence merge_ () {
+ elemVec seq;
+ Ses< elem > ses_ba = diff_ba.getSes();
+ Ses< elem > ses_bc = diff_bc.getSes();
+ sesElemVec ses_ba_v = ses_ba.getSequence();
+ sesElemVec ses_bc_v = ses_bc.getSequence();
+ sesElemVec_iter ba_it = ses_ba_v.begin();
+ sesElemVec_iter bc_it = ses_bc_v.begin();
+ sesElemVec_iter ba_end = ses_ba_v.end();
+ sesElemVec_iter bc_end = ses_bc_v.end();
+
+ while (!isEnd(ba_end, ba_it) || !isEnd(bc_end, bc_it)) {
+ while (true) {
+ if (!isEnd(ba_end, ba_it) &&
+ !isEnd(bc_end, bc_it) &&
+ ba_it->first == bc_it->first &&
+ ba_it->second.type == SES_COMMON &&
+ bc_it->second.type == SES_COMMON) {
+ // do nothing
+ } else {
+ break;
+ }
+ if (!isEnd(ba_end, ba_it)) seq.push_back(ba_it->first);
+ else if (!isEnd(bc_end, bc_it)) seq.push_back(bc_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ }
+ if (isEnd(ba_end, ba_it) || isEnd(bc_end, bc_it)) break;
+ if ( ba_it->second.type == SES_COMMON
+ && bc_it->second.type == SES_DELETE) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_COMMON &&
+ bc_it->second.type == SES_ADD) {
+ seq.push_back(bc_it->first);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_COMMON) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_DELETE) {
+ if (ba_it->first == bc_it->first) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else {
+ // conflict
+ conflict = true;
+ return B;
+ }
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_ADD) {
+ // conflict
+ conflict = true;
+ return B;
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_COMMON) {
+ seq.push_back(ba_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_DELETE) {
+ // conflict
+ conflict = true;
+ return B;
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_ADD) {
+ if (ba_it->first == bc_it->first) {
+ seq.push_back(ba_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else {
+ // conflict
+ conflict = true;
+ return B;
+ }
+ }
+ }
+
+ if (isEnd(ba_end, ba_it)) {
+ addDecentSequence(bc_end, bc_it, seq);
+ } else if (isEnd(bc_end, bc_it)) {
+ addDecentSequence(ba_end, ba_it, seq);
+ }
+
+ sequence mergedSeq(seq.begin(), seq.end());
+ return mergedSeq;
+ }
+
+ /**
+ * join elem vectors
+ */
+ void inline joinElemVec (elemVec& s1, elemVec& s2) const {
+ if (!s2.empty()) {
+ for (elemVec_iter vit=s2.begin();vit!=s2.end();++vit) {
+ s1.push_back(*vit);
+ }
+ }
+ }
+
+ /**
+ * check if sequence is at end
+ */
+ template <typename T_iter>
+ bool inline isEnd (const T_iter& end, const T_iter& it) const {
+ return it == end ? true : false;
+ }
+
+ /**
+ * increment iterator until iterator is at end
+ */
+ template <typename T_iter>
+ void inline forwardUntilEnd (const T_iter& end, T_iter& it) const {
+ if (!isEnd(end, it)) ++it;
+ }
+
+ /**
+ * add elements whose SES's type is ADD
+ */
+ void inline addDecentSequence (const sesElemVec_iter& end, sesElemVec_iter& it, elemVec& seq) const {
+ while (!isEnd(end, it)) {
+ if (it->second.type == SES_ADD) seq.push_back(it->first);
+ ++it;
+ }
+ }
+
+ };
+}
+
+#endif // DTL_DIFF3_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_LCS_H
+#define DTL_LCS_H
+
+namespace dtl {
+
+ /**
+ * Longest Common Subsequence template class
+ */
+ template <typename elem>
+ class Lcs : public Sequence< elem >
+ {
+ public :
+ Lcs () {}
+ ~Lcs () {}
+ };
+}
+
+#endif // DTL_LCS_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_SEQUENCE_H
+#define DTL_SEQUENCE_H
+
+namespace dtl {
+
+ /**
+ * sequence class template
+ */
+ template <typename elem>
+ class Sequence
+ {
+ public :
+ typedef vector< elem > elemVec;
+ Sequence () {}
+ virtual ~Sequence () {}
+
+ elemVec getSequence () const {
+ return sequence;
+ }
+ void addSequence (elem e) {
+ sequence.push_back(e);
+ }
+ protected :
+ elemVec sequence;
+ };
+}
+
+#endif // DTL_SEQUENCE_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_SES_H
+#define DTL_SES_H
+
+namespace dtl {
+
+ /**
+ * Shortest Edit Script template class
+ */
+ template <typename elem>
+ class Ses : public Sequence< elem >
+ {
+ private :
+ typedef pair< elem, elemInfo > sesElem;
+ typedef vector< sesElem > sesElemVec;
+ public :
+
+ Ses () : onlyAdd(true), onlyDelete(true), onlyCopy(true), deletesFirst(false) {
+ nextDeleteIdx = 0;
+ }
+ Ses (bool moveDel) : onlyAdd(true), onlyDelete(true), onlyCopy(true), deletesFirst(moveDel) {
+ nextDeleteIdx = 0;
+ }
+ ~Ses () {}
+
+ bool isOnlyAdd () const {
+ return onlyAdd;
+ }
+
+ bool isOnlyDelete () const {
+ return onlyDelete;
+ }
+
+ bool isOnlyCopy () const {
+ return onlyCopy;
+ }
+
+ bool isOnlyOneOperation () const {
+ return isOnlyAdd() || isOnlyDelete() || isOnlyCopy();
+ }
+
+ bool isChange () const {
+ return !onlyCopy;
+ }
+
+ using Sequence< elem >::addSequence;
+ void addSequence (elem e, long long beforeIdx, long long afterIdx, const edit_t type) {
+ elemInfo info;
+ info.beforeIdx = beforeIdx;
+ info.afterIdx = afterIdx;
+ info.type = type;
+ sesElem pe(e, info);
+ if (!deletesFirst) {
+ sequence.push_back(pe);
+ }
+ switch (type) {
+ case SES_DELETE:
+ onlyCopy = false;
+ onlyAdd = false;
+ if (deletesFirst) {
+ sequence.insert(sequence.begin() + nextDeleteIdx, pe);
+ nextDeleteIdx++;
+ }
+ break;
+ case SES_COMMON:
+ onlyAdd = false;
+ onlyDelete = false;
+ if (deletesFirst) {
+ sequence.push_back(pe);
+ nextDeleteIdx = sequence.size();
+ }
+ break;
+ case SES_ADD:
+ onlyDelete = false;
+ onlyCopy = false;
+ if (deletesFirst) {
+ sequence.push_back(pe);
+ }
+ break;
+ }
+ }
+
+ sesElemVec getSequence () const {
+ return sequence;
+ }
+ private :
+ sesElemVec sequence;
+ bool onlyAdd;
+ bool onlyDelete;
+ bool onlyCopy;
+ bool deletesFirst;
+ size_t nextDeleteIdx;
+ };
+}
+
+#endif // DTL_SES_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef DTL_H
+#define DTL_H
+
+#include "variables.hpp"
+#include "functors.hpp"
+#include "Sequence.hpp"
+#include "Lcs.hpp"
+#include "Ses.hpp"
+#include "Diff.hpp"
+#include "Diff3.hpp"
+
+#endif // DTL_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_FUNCTORS_H
+#define DTL_FUNCTORS_H
+
+namespace dtl {
+
+ /**
+ * printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class Printer
+ {
+ public :
+ Printer () : out_(cout) {}
+ Printer (stream& out) : out_(out) {}
+ virtual ~Printer () {}
+ virtual void operator() (const sesElem& se) const = 0;
+ protected :
+ stream& out_;
+ };
+
+ /**
+ * common element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class CommonPrinter : public Printer < sesElem, stream >
+ {
+ public :
+ CommonPrinter () : Printer < sesElem, stream > () {}
+ CommonPrinter (stream& out) : Printer < sesElem, stream > (out) {}
+ ~CommonPrinter () {}
+ void operator() (const sesElem& se) const {
+ this->out_ << SES_MARK_COMMON << se.first << endl;
+ }
+ };
+
+ /**
+ * ses element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class ChangePrinter : public Printer < sesElem, stream >
+ {
+ public :
+ ChangePrinter () : Printer < sesElem, stream > () {}
+ ChangePrinter (stream& out) : Printer < sesElem, stream > (out) {}
+ ~ChangePrinter () {}
+ void operator() (const sesElem& se) const {
+ switch (se.second.type) {
+ case SES_ADD:
+ this->out_ << SES_MARK_ADD << se.first << endl;
+ break;
+ case SES_DELETE:
+ this->out_ << SES_MARK_DELETE << se.first << endl;
+ break;
+ case SES_COMMON:
+ this->out_ << SES_MARK_COMMON << se.first << endl;
+ break;
+ }
+ }
+ };
+
+ /**
+ * unified format element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class UniHunkPrinter
+ {
+ public :
+ UniHunkPrinter () : out_(cout) {}
+ UniHunkPrinter (stream& out) : out_(out) {}
+ ~UniHunkPrinter () {}
+ void operator() (const uniHunk< sesElem >& hunk) const {
+ out_ << "@@"
+ << " -" << hunk.a << "," << hunk.b
+ << " +" << hunk.c << "," << hunk.d
+ << " @@" << endl;
+
+ for_each(hunk.common[0].begin(), hunk.common[0].end(), CommonPrinter< sesElem, stream >(out_));
+ for_each(hunk.change.begin(), hunk.change.end(), ChangePrinter< sesElem, stream >(out_));
+ for_each(hunk.common[1].begin(), hunk.common[1].end(), CommonPrinter< sesElem, stream >(out_));
+ }
+ private :
+ stream& out_;
+ };
+
+ /**
+ * compare class template
+ */
+ template <typename elem>
+ class Compare
+ {
+ public :
+ Compare () {}
+ virtual ~Compare () {}
+ virtual inline bool impl (const elem& e1, const elem& e2) const {
+ return e1 == e2;
+ }
+ };
+}
+
+#endif // DTL_FUNCTORS_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_VARIABLES_H
+#define DTL_VARIABLES_H
+
+#include <vector>
+#include <list>
+#include <string>
+#include <algorithm>
+#include <iostream>
+
+namespace dtl {
+
+ using std::vector;
+ using std::string;
+ using std::pair;
+ using std::ostream;
+ using std::list;
+ using std::for_each;
+ using std::distance;
+ using std::fill;
+ using std::cout;
+ using std::endl;
+ using std::rotate;
+ using std::swap;
+ using std::max;
+
+ /**
+ * version string
+ */
+ const string version = "1.18";
+
+ /**
+ * type of edit for SES
+ */
+ typedef int edit_t;
+ const edit_t SES_DELETE = -1;
+ const edit_t SES_COMMON = 0;
+ const edit_t SES_ADD = 1;
+
+ /**
+ * mark of SES
+ */
+#define SES_MARK_DELETE "-"
+#define SES_MARK_COMMON " "
+#define SES_MARK_ADD "+"
+
+ /**
+ * info for Unified Format
+ */
+ typedef struct eleminfo {
+ long long beforeIdx; // index of prev sequence
+ long long afterIdx; // index of after sequence
+ edit_t type; // type of edit(Add, Delete, Common)
+ bool operator==(const eleminfo& other) const{
+ return (this->beforeIdx == other.beforeIdx && this->afterIdx == other.afterIdx && this->type == other.type);
+ }
+ } elemInfo;
+
+ const long long DTL_SEPARATE_SIZE = 3;
+ const long long DTL_CONTEXT_SIZE = 3;
+
+ /**
+ * cordinate for registering route
+ */
+ typedef struct Point {
+ long long x; // x cordinate
+ long long y; // y cordinate
+ long long k; // vertex
+ } P;
+
+ /**
+ * limit of cordinate size
+ */
+ const unsigned long long MAX_CORDINATES_SIZE = 2000000;
+
+ typedef vector< long long > editPath;
+ typedef vector< P > editPathCordinates;
+
+ /**
+ * Structure of Unified Format Hunk
+ */
+ template <typename sesElem>
+ struct uniHunk {
+ long long a, b, c, d; // @@ -a,b +c,d @@
+ vector< sesElem > common[2]; // anteroposterior commons on changes
+ vector< sesElem > change; // changes
+ long long inc_dec_count; // count of increace and decrease
+ };
+
+#define dtl_typedefs(elem, sequence) \
+ typedef pair< elem, elemInfo > sesElem; \
+ typedef vector< sesElem > sesElemVec; \
+ typedef vector< uniHunk< sesElem > > uniHunkVec; \
+ typedef list< elem > elemList; \
+ typedef vector< elem > elemVec; \
+ typedef typename uniHunkVec::iterator uniHunkVec_iter; \
+ typedef typename sesElemVec::iterator sesElemVec_iter; \
+ typedef typename elemList::iterator elemList_iter; \
+ typedef typename sequence::iterator sequence_iter; \
+ typedef typename sequence::const_iterator sequence_const_iter; \
+ typedef typename elemVec::iterator elemVec_iter;
+
+
+}
+
+#endif // DTL_VARIABLES_H
--- /dev/null
+
+# SConstruct for dtl examples
+
+env = Environment(CPPPATH='..')
+debug = ARGUMENTS.get('debug', 'n')
+if debug == 'y' or debug == 'yes':
+ env.Append(CPPFLAGS = ['-Wall', '-g'])
+else:
+ env.Append(CPPFLAGS = ['-Wall', '-O2'])
+
+conf = Configure(env);
+
+if not conf.CheckCXX():
+ print "The C++ compiler is not installed!"
+ Exit(1)
+
+libs = ['stdc++']
+for lib in libs:
+ if not conf.CheckLib(lib):
+ print "library " + lib + " not installed!"
+ Exit(1)
+
+conf.Finish()
+
+targets = { 'strdiff' : ['strdiff.cpp', 'common.cpp'], # diff between two string sequences
+ 'intdiff' : ['intdiff.cpp'], # diff between two integer sequences
+ 'unidiff' : ['unidiff.cpp', 'common.cpp'], # unified diff between two files
+ 'unistrdiff' : ['unistrdiff.cpp', 'common.cpp'], # unified diff between two strings
+ 'bdiff' : ['bdiff.cpp', 'common.cpp'], # diff between two byte sequences
+ 'strdiff3' : ['strdiff3.cpp', 'common.cpp'], # three-way string diff program using dtl
+ 'intdiff3' : ['intdiff3.cpp'], # three-way integer diff program using dtl
+ 'patch' : ['patch.cpp', 'common.cpp'], # string patch program using dtl
+ 'fpatch' : ['fpatch.cpp', 'common.cpp'], # file patch program using dtl
+ 'st2ses' : ['st2ses.cpp', 'common.cpp'], # convert SES format file to SES instance
+ 'strdiff_cp' : ['strdiff_cp.cpp', 'common.cpp'], # diff between two string sequences with custom printer
+ }
+
+[ env.Program(target, targets[target]) for target in targets ]
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <cstdio>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+using namespace std;
+
+using dtl::Diff;
+
+typedef unsigned char elem;
+typedef vector< elem > sequence;
+
+static int create_byte_seq(const char *fs, sequence& seq);
+static int create_byte_seq(const char *fs, sequence& seq)
+{
+ int fd;
+ int siz;
+ elem buf[BUFSIZ];
+ if ((fd = open(fs, O_RDONLY)) == -1) {
+ cout << "Opening failed." << endl;
+ return -1;
+ }
+ while ((siz = read(fd, buf, sizeof(buf))) > 0) {
+ for (int i=0;i<siz;++i) {
+ seq.push_back(buf[i]);
+ }
+ }
+ if (siz < 0) {
+ close(fd);
+ cout << "Read error." << endl;
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ string fs1(argv[1]);
+ string fs2(argv[2]);
+ sequence seq1;
+ sequence seq2;
+
+ create_byte_seq(fs1.c_str(), seq1);
+ create_byte_seq(fs2.c_str(), seq2);
+
+ Diff< elem, sequence > d(seq1, seq2);
+ d.compose();
+
+ if (d.getEditDistance() == 0) {
+ cout << fs1 << " is the same as " << fs2 << endl;
+ } else {
+ cout << fs1 << " is different from " << fs2 << endl;
+ }
+
+ return 0;
+}
--- /dev/null
+
+#include "common.hpp"
+
+bool isFileExist (string& fs) {
+ FILE *fp;
+ if ((fp = fopen(fs.c_str(), "r")) == NULL) {
+ return false;
+ }
+ fclose(fp);
+ return true;
+}
+
+bool isFewArgs (int argc, int limit) {
+ bool ret = false;
+ if (argc < limit) {
+ ret = true;
+ }
+ return ret;
+}
+
--- /dev/null
+
+#ifndef DTL_EXAMPLE_COMMON_H
+#define DTL_EXAMPLE_COMMON_H
+
+#include <string>
+#include <cstdio>
+
+using namespace std;
+
+bool isFileExist (string& fs);
+bool isFewArgs (int argc, int limit = 3);
+
+#endif
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <vector>
+#include <cassert>
+
+using namespace std;
+
+using dtl::Diff;
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ string A(argv[1]);
+ string B(argv[2]);
+ bool fileExist = true;
+
+ if (!isFileExist(A)) {
+ cerr << "file A does not exist" << endl;
+ fileExist = false;
+ }
+
+ if (!isFileExist(B)) {
+ cerr << "file B does not exist" << endl;
+ fileExist = false;
+ }
+
+ if (!fileExist) {
+ return -1;
+ }
+
+ typedef string elem;
+ typedef vector< elem > sequence;
+
+ ifstream Aifs(A.c_str());
+ ifstream Bifs(B.c_str());
+ elem buf;
+ sequence ALines, BLines;
+ ostringstream ossLine, ossInfo;
+
+ while(getline(Aifs, buf)){
+ ALines.push_back(buf);
+ }
+ while(getline(Bifs, buf)){
+ BLines.push_back(buf);
+ }
+
+ Diff< elem > d(ALines, BLines);
+ d.compose();
+
+ sequence s1 = ALines;
+ sequence s2 = d.patch(s1);
+
+ // fpatch
+ assert(BLines == s2);
+ cout << "fpatch succeeded" << endl;
+
+ d.composeUnifiedHunks();
+ sequence s3 = d.uniPatch(s1);
+
+ // unipatch
+ assert(BLines == s3);
+ cout << "unipatch succeeded" << endl;
+
+ return 0;
+}
--- /dev/null
+
+#include <iostream>
+#include <vector>
+#include <dtl/dtl.hpp>
+
+using namespace std;
+
+using dtl::Diff;
+
+int main(int, char**){
+
+ int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ int b[] = {3, 5, 1, 4, 5, 1, 7, 9, 6, 10};
+ int asiz = sizeof(a) / sizeof(int);
+ int bsiz = sizeof(b) / sizeof(int);
+ for (int i=0;i<asiz;++i) {
+ cout << a[i] << " ";
+ }
+ cout << endl;
+ for (int i=0;i<bsiz;++i) {
+ cout << b[i] << " ";
+ }
+ cout << endl;
+
+ typedef int elem;
+ typedef vector< int > sequence;
+
+ sequence A(&a[0], &a[asiz]);
+ sequence B(&b[0], &b[bsiz]);
+ Diff< elem > d(A, B);
+ d.compose();
+
+ // editDistance
+ cout << "editDistance:" << d.getEditDistance() << endl;
+
+ // Longest Common Subsequence
+ sequence lcs_v = d.getLcsVec();
+ cout << "LCS: ";
+ for (sequence::iterator vit=lcs_v.begin();vit!=lcs_v.end();++vit) {
+ cout << *vit << " ";
+ }
+ cout << endl;
+
+ // Shortest Edit Script
+ cout << "SES" << endl;
+ d.printSES();
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+using namespace std;
+
+using dtl::Diff3;
+
+int main(int, char**) {
+
+ int a[10] = {1, 2, 3, 4, 5, 6, 7, 3, 9, 10};
+ int b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ int c[10] = {1, 2, 3, 9, 5, 6, 7, 8, 9, 10};
+ int answer[10] = {1, 2, 3, 9, 5, 6, 7, 3, 9, 10};
+
+ cout << "a:";
+ for (int i=0;i<10;++i) {
+ cout << a[i] << " ";
+ }
+ cout << endl;
+ cout << "b:";
+ for (int i=0;i<10;++i) {
+ cout << b[i] << " ";
+ }
+ cout << endl;
+ cout << "c:";
+ for (int i=0;i<10;++i) {
+ cout << c[i] << " ";
+ }
+ cout << endl;
+
+ typedef int elem;
+ typedef vector< int > sequence;
+ sequence A(&a[0], &a[10]);
+ sequence B(&b[0], &b[10]);
+ sequence C(&c[0], &c[10]);
+ sequence Answer(&answer[0], &answer[10]);
+ Diff3< elem > diff3(A, B, C);
+ diff3.compose();
+ if (!diff3.merge()) {
+ cerr << "conflict." << endl;
+ return -1;
+ }
+ sequence s = diff3.getMergedSequence();
+ cout << "s:";
+ for (sequence::iterator it=s.begin();it!=s.end();++it) {
+ cout << *it << " ";
+ }
+ cout << endl;
+
+ assert(s == Answer);
+ cout << "intdiff3 OK" << endl;
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <vector>
+#include <cassert>
+
+using namespace std;
+
+using dtl::Diff;
+
+int main(int argc, char *argv[]) {
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef char elem;
+ typedef string sequence;
+
+ sequence A(argv[1]);
+ sequence B(argv[2]);
+
+ Diff< elem, sequence > d(A, B);
+ d.compose();
+
+ sequence s1(A);
+ sequence s2 = d.patch(s1);
+ d.composeUnifiedHunks();
+ sequence s3 = d.uniPatch(s1);
+
+ cout << "before:" << s1 << endl;
+ cout << "after :" << s2 << endl;
+ assert(B == s2);
+ cout << "patch succeeded" << endl;
+
+ cout << "before:" << s1 << endl;
+ cout << "after :" << s3 << endl;
+ assert(B == s3);
+ cout << "unipatch succeeded" << endl;
+
+ return 0;
+}
--- /dev/null
+#ifndef DTL_PRINTERS
+#define DTL_PRINTERS
+
+template <typename sesElem, typename stream = ostream >
+class customChangePrinter : public Printer < sesElem, stream >
+{
+public :
+ customChangePrinter () : Printer < sesElem, stream > () {}
+ customChangePrinter (stream& out) : Printer < sesElem, stream > (out) {}
+ ~customChangePrinter () {}
+ void operator() (const sesElem& se) const {
+ switch (se.second.type) {
+ case SES_ADD:
+ this->out_ << "Add: " << se.first << endl;
+ break;
+ case SES_DELETE:
+ this->out_ << "Delete: " << se.first << endl;
+ break;
+ case SES_COMMON:
+ this->out_ << "Common: " << se.first << endl;
+ break;
+ }
+ }
+};
+
+#endif // DTL_PRINTERS
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <string>
+
+using namespace std;
+using namespace dtl;
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc, 2)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef string elem;
+ typedef vector< string > sequence;
+
+ string s(argv[1]);
+
+ if (!isFileExist(s)) {
+ cerr << s << " is invalid." << endl;
+ return -1;
+ }
+
+ ifstream fs(s.c_str());
+ const Ses< elem > ses = Diff< elem, sequence >::composeSesFromStream< ifstream >(fs);
+ dtl::Diff< elem, sequence >::printSES(ses);
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+using dtl::Diff;
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef char elem;
+ typedef string sequence;
+
+ sequence A(argv[1]);
+ sequence B(argv[2]);
+
+ Diff< elem, sequence > d(A, B);
+ //d.onOnlyEditDistance();
+ d.compose();
+
+ // editDistance
+ cout << "editDistance:" << d.getEditDistance() << endl;
+
+ // Longest Common Subsequence
+ vector< elem > lcs_v = d.getLcsVec();
+ sequence lcs_s(lcs_v.begin(), lcs_v.end());
+ cout << "LCS:" << lcs_s << endl;
+
+ // Shortest Edit Script
+ cout << "SES" << endl;
+ d.printSES();
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <vector>
+#include <string>
+
+using namespace std;
+
+using dtl::Diff3;
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc, 4)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef char elem;
+ typedef string sequence;
+
+ sequence A(argv[1]);
+ sequence B(argv[2]);
+ sequence C(argv[3]);
+
+ Diff3< elem, sequence > diff3(A, B, C);
+ diff3.compose();
+ if (!diff3.merge()) {
+ cerr << "conflict." << endl;
+ return 0;
+ }
+ cout << "result:" << diff3.getMergedSequence() << endl;
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <sstream>
+#include <string>
+
+using namespace std;
+
+using dtl::Diff;
+using dtl::SES_ADD;
+using dtl::SES_DELETE;
+using dtl::SES_COMMON;
+using dtl::Printer;
+
+#include "printers.hpp"
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef char elem;
+ typedef string sequence;
+
+ sequence A(argv[1]);
+ sequence B(argv[2]);
+
+ Diff< elem, sequence > d(A, B);
+ d.compose();
+
+ // Shortest Edit Script
+ cout << "SES" << endl;
+
+ d.printSES < ostream, customChangePrinter > (cout);
+
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+
+#include <time.h>
+#include <sys/stat.h>
+
+using namespace std;
+
+using dtl::Diff;
+using dtl::elemInfo;
+using dtl::uniHunk;
+
+static void showStats (string fp1, string fp2);
+static void unifiedDiff (string fp1, string fp2);
+
+static void showStats (string fp1, string fp2)
+{
+ const int MAX_LENGTH = 255;
+ char time_format[] = "%Y-%m-%d %H:%M:%S %z";
+ time_t rawtime[2];
+ struct tm *timeinfo[2];
+ struct stat st[2];
+
+ if (stat(fp1.c_str(), &st[0]) == -1) {
+ cerr << "argv1 is invalid." << endl;
+ exit(-1);
+ }
+ if (stat(fp2.c_str(), &st[1]) == -1) {
+ cerr << "argv2 is invalid" << endl;
+ exit(-1);
+ }
+
+ char buf[2][MAX_LENGTH + 1];
+ rawtime[0] = st[0].st_mtime;
+ timeinfo[0] = localtime(&rawtime[0]);
+ strftime(buf[0], MAX_LENGTH, time_format, timeinfo[0]);
+ cout << "--- " << fp1 << '\t' << buf[0] << endl;
+ rawtime[1] = st[1].st_mtime;
+ timeinfo[1] = localtime(&rawtime[1]);
+ strftime(buf[1], MAX_LENGTH, time_format, timeinfo[1]);
+ cout << "+++ " << fp2 << '\t' << buf[1] << endl;
+}
+
+static void unifiedDiff (string fp1, string fp2)
+{
+ typedef string elem;
+ typedef vector< elem > sequence;
+ typedef pair< elem, elemInfo > sesElem;
+
+ ifstream Aifs(fp1.c_str());
+ ifstream Bifs(fp2.c_str());
+ elem buf;
+ sequence ALines, BLines;
+
+ while(getline(Aifs, buf)){
+ ALines.push_back(buf);
+ }
+ while(getline(Bifs, buf)){
+ BLines.push_back(buf);
+ }
+
+ Diff< elem > diff(ALines, BLines);
+ diff.onHuge();
+ //diff.onUnserious();
+ diff.compose();
+
+ // type unihunk definition test
+ uniHunk< sesElem > hunk;
+
+ if (diff.getEditDistance() > 0) {
+ showStats(fp1, fp2); // show file info
+ }
+
+ diff.composeUnifiedHunks();
+ diff.printUnifiedFormat();
+}
+
+
+int main(int argc, char *argv[])
+{
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ string s1(argv[1]);
+ string s2(argv[2]);
+ bool fileExist = true;
+
+ if (!isFileExist(s1)) {
+ cerr << s1 << " is invalid." << endl;
+ fileExist = false;
+ }
+
+ if (!isFileExist(s2)) {
+ cerr << s2 << " is invalid." << endl;
+ fileExist = false;
+ }
+
+ if (!fileExist) {
+ return -1;
+ }
+
+ unifiedDiff(s1, s2);
+ return 0;
+}
--- /dev/null
+
+#include <dtl/dtl.hpp>
+#include "common.hpp"
+#include <iostream>
+#include <vector>
+
+using namespace std;
+
+using dtl::Diff;
+
+int main(int argc, char *argv[]){
+
+ if (isFewArgs(argc)) {
+ cerr << "Too few arguments." << endl;
+ return -1;
+ }
+
+ typedef char elem;
+ typedef string sequence;
+
+ sequence A(argv[1]);
+ sequence B(argv[2]);
+
+ Diff<elem, sequence > d(A, B);
+ d.compose();
+ d.composeUnifiedHunks();
+
+ // editDistance
+ cout << "editDistance:" << d.getEditDistance() << endl;
+
+ // Longest Common Subsequence
+ vector<elem> lcs_v = d.getLcsVec();
+ sequence lcs_s(lcs_v.begin(), lcs_v.end());
+ cout << "LCS:" << lcs_s << endl;
+
+ // print Unified Format
+ d.printUnifiedFormat();
+
+ return 0;
+}
--- /dev/null
+#include "dtl_test_common.hpp"
+
+class Intdifftest : public ::testing::Test
+{
+protected :
+ dtl_test_typedefs(int, vector< int >)
+ typedef struct case_t {
+ sequence A;
+ sequence B;
+ size_t editdis;
+ elemVec lcs_v;
+ sequence lcs_s;
+ sesElemVec ses_seq;
+ uniHunkVec hunk_v;
+ size_t editdis_ses;
+ size_t editdis_uni;
+ string path_rses;
+ string path_rhunks;
+ } case_t;
+ typedef vector< case_t > caseVec;
+ caseVec cases;
+
+ case_t createCase (const sequence a, const sequence b, string test_name) {
+ case_t c;
+ string diff_name("intdiff");
+ Diff< elem > diff(a, b);
+ diff.compose();
+ diff.composeUnifiedHunks();
+
+ if (test_name != "") {
+ string path_lses = create_path(test_name, diff_name, TYPE_DIFF_SES);
+ string path_rses = create_path(test_name, diff_name, TYPE_DIFF_SES, true);
+ string path_lhunks = create_path(test_name, diff_name, TYPE_DIFF_UNI);
+ string path_rhunks = create_path(test_name, diff_name, TYPE_DIFF_UNI, true);
+
+ create_file< elem, sequence, Compare< elem > >(path_rses, diff, TYPE_DIFF_SES);
+ create_file< elem, sequence, Compare< elem > >(path_rhunks, diff, TYPE_DIFF_UNI);
+ c.editdis_ses = cal_diff_uni(path_lses, path_rses);
+ c.editdis_uni = cal_diff_uni(path_lhunks, path_rhunks);
+ c.path_rses = path_rses;
+ c.path_rhunks = path_rhunks;
+ }
+
+
+ c.A = a;
+ c.B = b;
+ c.editdis = diff.getEditDistance();
+ c.lcs_v = diff.getLcsVec();
+ c.ses_seq = diff.getSes().getSequence();
+ return c;
+ }
+
+ void SetUp() {
+ cases.push_back(createCase(sequence(0), sequence(0), "diff_test0"));
+ sequence B1;
+ B1.push_back(1);
+ cases.push_back(createCase(sequence(0), B1, "diff_test1"));
+ sequence A2;
+ A2.push_back(1);
+ cases.push_back(createCase(A2, sequence(0), "diff_test2"));
+ int a4[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ int b4[] = {3, 5, 1, 4, 5, 1, 7, 9, 6, 10};
+ int a4siz = sizeof(a4) / sizeof(int);
+ int b4siz = sizeof(b4) / sizeof(int);
+ sequence A4(&a4[0], &a4[a4siz]);
+ sequence B4(&b4[0], &b4[b4siz]);
+ cases.push_back(createCase(A4, B4, "diff_test3"));
+ int a5[] = {1, 2, 3, 4, 5};
+ int b5[] = {3, 5, 1, 4, 5};
+ int a5siz = sizeof(a5) / sizeof(int);
+ int b5siz = sizeof(b5) / sizeof(int);
+ sequence A5(&a5[0], &a5[a5siz]);
+ sequence B5(&b5[0], &b5[b5siz]);
+ cases.push_back(createCase(A5, B5, "diff_test4"));
+ }
+
+ void TearDown () {
+ for_each(cases.begin(), cases.end(), Remover< case_t >());
+ }
+
+};
+
+/**
+ * Intdifftest
+ * check list is following
+ * - editdistance
+ * - LCS
+ * - SES
+ */
+TEST_F (Intdifftest, diff_test0) {
+ EXPECT_EQ(0, cases[0].editdis);
+
+ EXPECT_TRUE(cases[0].lcs_v.empty());
+
+ ASSERT_EQ(0, cases[0].editdis_ses);
+
+ ASSERT_EQ(0, cases[0].editdis_uni);
+}
+
+TEST_F (Intdifftest, diff_test1) {
+ EXPECT_EQ(1, cases[1].editdis);
+
+ EXPECT_TRUE(cases[1].lcs_v.empty());
+
+ ASSERT_EQ(0, cases[1].editdis_ses);
+
+ ASSERT_EQ(0, cases[1].editdis_uni);
+}
+
+TEST_F (Intdifftest, diff_test2) {
+ EXPECT_EQ(1, cases[2].editdis);
+
+ EXPECT_TRUE(cases[2].lcs_v.empty());
+
+ ASSERT_EQ(0, cases[2].editdis_ses);
+
+ ASSERT_EQ(0, cases[2].editdis_uni);
+}
+
+TEST_F (Intdifftest, diff_test3) {
+ EXPECT_EQ(8, cases[3].editdis);
+
+ EXPECT_EQ(3, cases[3].lcs_v[0]);
+ EXPECT_EQ(4, cases[3].lcs_v[1]);
+ EXPECT_EQ(5, cases[3].lcs_v[2]);
+ EXPECT_EQ(7, cases[3].lcs_v[3]);
+ EXPECT_EQ(9, cases[3].lcs_v[4]);
+
+ ASSERT_EQ(0, cases[3].editdis_ses);
+
+ ASSERT_EQ(0, cases[3].editdis_uni);
+}
+
+TEST_F (Intdifftest, diff_test4) {
+ EXPECT_EQ(4, cases[4].editdis);
+
+ EXPECT_EQ(3, cases[4].lcs_v[0]);
+ EXPECT_EQ(4, cases[4].lcs_v[1]);
+ EXPECT_EQ(5, cases[4].lcs_v[2]);
+
+ ASSERT_EQ(0, cases[4].editdis_ses);
+
+ ASSERT_EQ(0, cases[4].editdis_uni);
+}
--- /dev/null
+#include "dtl_test_common.hpp"
+#include "comparators.hpp"
+
+class Objdifftest : public ::testing::Test
+{
+protected :
+ dtl_test_typedefs(string, vector<elem>)
+ typedef struct case_t {
+ sequence A;
+ sequence B;
+ sesElemVec expected;
+ sesElemVec ses_seq;
+ } case_t;
+ typedef vector< case_t > caseVec;
+
+ caseVec obj_diff_cases;
+
+ template < typename comparator >
+ case_t createCase (const sequence a, const sequence b, sesElemVec ses, string test_name) {
+ case_t c;
+ elemVec lcs_v;
+ string diff_name("objdiff");
+
+ Diff< elem, sequence, comparator > diff(a, b, true);
+
+ diff.compose();
+
+ c.A = a;
+ c.B = b;
+ c.ses_seq = diff.getSes().getSequence();
+ c.expected = ses;
+
+ return c;
+ }
+
+ void SetUp(void) {
+ {
+ string array1[] = {"the", "quick", "brown"};
+ string array2[] = {"The", "Quick", "Fox"};
+
+ sequence A(array1, array1 + (sizeof(array1) / sizeof(array1[0])));
+ sequence B(array2, array2 + (sizeof(array2) / sizeof(array2[0])));
+
+ dtl::Ses< elem > ses;
+ ses.addSequence("the", 1, 1, dtl::SES_COMMON);
+ ses.addSequence("quick", 2, 2, dtl::SES_COMMON);
+ ses.addSequence("brown", 3, 0, dtl::SES_DELETE);
+ ses.addSequence("Fox", 0, 3, dtl::SES_ADD);
+
+ obj_diff_cases.push_back(createCase< StringCaseInsensitive >(A, B, ses.getSequence(), "objdiff_test0_pattern"));
+ }
+
+ {
+ string array1[] = {"b", "c", "e", "g"};
+ string array2[] = {"a", "d", "e", "f", "h"};
+
+ sequence A(array1, array1 + (sizeof(array1) / sizeof(array1[0])));
+ sequence B(array2, array2 + (sizeof(array2) / sizeof(array2[0])));
+
+ dtl::Ses< elem > ses;
+ ses.addSequence("b", 1, 0, dtl::SES_DELETE);
+ ses.addSequence("c", 2, 0, dtl::SES_DELETE);
+ ses.addSequence("a", 0, 1, dtl::SES_ADD);
+ ses.addSequence("d", 0, 2, dtl::SES_ADD);
+ ses.addSequence("e", 3, 3, dtl::SES_COMMON);
+ ses.addSequence("g", 4, 0, dtl::SES_DELETE);
+ ses.addSequence("f", 0, 4, dtl::SES_ADD);
+ ses.addSequence("h", 0, 5, dtl::SES_ADD);
+
+ obj_diff_cases.push_back(createCase< StringCaseInsensitive >(A, B, ses.getSequence(), "objdiff_test1_unswapped"));
+ }
+ {
+ string array1[] = {"a", "d", "e", "f", "h"};
+ string array2[] = {"b", "c", "e", "g"};
+
+ sequence A(array1, array1 + (sizeof(array1) / sizeof(array1[0])));
+ sequence B(array2, array2 + (sizeof(array2) / sizeof(array2[0])));
+
+ dtl::Ses< elem > ses;
+ ses.addSequence("a", 1, 0, dtl::SES_DELETE);
+ ses.addSequence("d", 2, 0, dtl::SES_DELETE);
+ ses.addSequence("b", 0, 1, dtl::SES_ADD);
+ ses.addSequence("c", 0, 2, dtl::SES_ADD);
+ ses.addSequence("e", 3, 3, dtl::SES_COMMON);
+ ses.addSequence("f", 4, 0, dtl::SES_DELETE);
+ ses.addSequence("h", 5, 0, dtl::SES_DELETE);
+ ses.addSequence("g", 0, 4, dtl::SES_ADD);
+
+ obj_diff_cases.push_back(createCase< StringCaseInsensitive >(A, B, ses.getSequence(), "objdiff_test2_swapped"));
+ }
+ }
+
+ void TearDown () {
+ //for_each(obj_diff_cases.begin(), obj_diff_cases.end(), Remover< case_t >());
+ }
+};
+
+
+/**
+ * Objdifftest
+ * check list:
+ * - SES pattern "SES_COMMON, SES_DELETE, SES_ADD"
+ * - Indepence of results from swapping
+ */
+
+TEST_F (Objdifftest, objdiff_test0_pattern) {
+ EXPECT_EQ(obj_diff_cases[0].expected, obj_diff_cases[0].ses_seq);
+}
+
+TEST_F (Objdifftest, objdiff_test1_unswapped) {
+ EXPECT_EQ(obj_diff_cases[1].expected, obj_diff_cases[1].ses_seq);
+}
+
+TEST_F (Objdifftest, objdiff_test2_swapped) {
+ EXPECT_EQ(obj_diff_cases[2].expected, obj_diff_cases[2].ses_seq);
+}
--- /dev/null
+#include "dtl_test_common.hpp"
+
+class Patchtest : public ::testing::Test
+{
+protected :
+ dtl_test_typedefs(char, string)
+ typedef struct case_t {
+ sequence A;
+ sequence B;
+ Diff< elem, sequence > diff;
+ } case_t;
+ typedef vector< case_t > caseVec;
+
+ caseVec cases;
+
+ case_t createCase (sequence a, sequence b) {
+ case_t c;
+ c.A = a;
+ c.B = b;
+ c.diff = Diff< elem, sequence >(a, b);
+ c.diff.compose();
+ c.diff.composeUnifiedHunks();
+ return c;
+ }
+
+ void SetUp() {
+ cases.push_back(createCase("abc", "abd")); // 0
+ cases.push_back(createCase("acbdeacbed", "acebdabbabed")); // 1
+ cases.push_back(createCase("abcdef", "dacfea")); // 2
+ cases.push_back(createCase("abcbda", "bdcaba")); // 3
+ cases.push_back(createCase("bokko", "bokkko")); // 4
+ cases.push_back(createCase("", "")); // 5
+ cases.push_back(createCase("a", "")); // 6
+ cases.push_back(createCase("", "b")); // 7
+ cases.push_back(createCase("abcdefq3wefarhgorequgho4euhfteowauhfwehogfewrquhoi23hroewhoahfotrhguoiewahrgqqabcdef",
+ "3abcdef4976fd86ouofita67t85r876e5e746578tgliuhopoqqabcdef")); // 8
+ cases.push_back(createCase("abcqqqeqqqccc", "abdqqqeqqqddd")); // 9
+ }
+
+ void TearDown () {}
+
+};
+
+/**
+ * Patchtest
+ * check list is following
+ * - patch function
+ * - uniPatch function
+ */
+TEST_F (Patchtest, patch_test0) {
+ ASSERT_EQ(cases[0].B, cases[0].diff.patch(cases[0].A));
+ ASSERT_EQ(cases[0].B, cases[0].diff.uniPatch(cases[0].A));
+}
+
+TEST_F (Patchtest, patch_test1) {
+ ASSERT_EQ(cases[1].B, cases[1].diff.patch(cases[1].A));
+ ASSERT_EQ(cases[1].B, cases[1].diff.uniPatch(cases[1].A));
+}
+
+TEST_F (Patchtest, patch_test2) {
+ ASSERT_EQ(cases[2].B, cases[2].diff.patch(cases[2].A));
+ ASSERT_EQ(cases[2].B, cases[2].diff.uniPatch(cases[2].A));
+}
+
+TEST_F (Patchtest, patch_test3) {
+ ASSERT_EQ(cases[3].B, cases[3].diff.patch(cases[3].A));
+ ASSERT_EQ(cases[3].B, cases[3].diff.uniPatch(cases[3].A));
+}
+
+TEST_F (Patchtest, patch_test4) {
+ ASSERT_EQ(cases[4].B, cases[4].diff.patch(cases[4].A));
+ ASSERT_EQ(cases[4].B, cases[4].diff.uniPatch(cases[4].A));
+}
+
+TEST_F (Patchtest, patch_test5) {
+ ASSERT_EQ(cases[5].B, cases[5].diff.patch(cases[5].A));
+ ASSERT_EQ(cases[5].B, cases[5].diff.uniPatch(cases[5].A));
+}
+
+TEST_F (Patchtest, patch_test6) {
+ ASSERT_EQ(cases[6].B, cases[6].diff.patch(cases[6].A));
+ ASSERT_EQ(cases[6].B, cases[6].diff.uniPatch(cases[6].A));
+}
+
+TEST_F (Patchtest, patch_test7) {
+ ASSERT_EQ(cases[7].B, cases[7].diff.patch(cases[7].A));
+ ASSERT_EQ(cases[7].B, cases[7].diff.uniPatch(cases[7].A));
+}
+
+TEST_F (Patchtest, patch_test8) {
+ ASSERT_EQ(cases[8].B, cases[8].diff.patch(cases[8].A));
+ ASSERT_EQ(cases[8].B, cases[8].diff.uniPatch(cases[8].A));
+}
+
+TEST_F (Patchtest, patch_test9) {
+ ASSERT_EQ(cases[9].B, cases[9].diff.patch(cases[9].A));
+ ASSERT_EQ(cases[9].B, cases[9].diff.uniPatch(cases[9].A));
+}
--- /dev/null
+# -*- coding: utf-8 -*-
+# SConstrunct for dtl test
+
+import os
+
+def path_chomp(path):
+ if path[-1] == '/':
+ return path[:-1]
+ return path
+
+if not 'GTEST_ROOT' in os.environ:
+ print "set environment variable 'GTEST_ROOT'"
+ Exit(1)
+
+gtest_root = path_chomp(os.environ['GTEST_ROOT'])
+if gtest_root[-1] == '/':
+ gtest_root = gtest_root[:-1]
+
+gtest_includes = gtest_root + '/include'
+gtest_libs = gtest_root + '/lib/.libs'
+
+flags = ['-Wall', '-O2']
+libs = ['stdc++', 'pthread']
+includes = ['..', gtest_includes]
+target = 'dtl_test'
+
+env = Environment(CPPFLAGS=flags,
+ CPPPATH=includes,
+ LIBPATH=[gtest_libs],
+ )
+
+conf = Configure(env);
+
+if not conf.CheckCXX():
+ print "c++ compiler is not installed!"
+ Exit(1)
+
+for lib in libs:
+ if not conf.CheckLib(lib):
+ print "library " + lib + " not installed!"
+ Exit(1)
+
+conf.Finish()
+
+test = env.Program(target,
+ [Glob('*.cpp'), gtest_libs + '/libgtest.a']
+ )
+
+test_alias = env.Alias('check',
+ test,
+ test[0].abspath)
+
+env.AlwaysBuild(test_alias)
--- /dev/null
+#include "dtl_test_common.hpp"
+#include "comparators.hpp"
+
+class Strdiff3test : public ::testing::Test
+{
+protected :
+ dtl_test_typedefs(char, string)
+ typedef struct case_t {
+ sequence S;
+ bool is_merge_success;
+ sequence merged_seq;
+ } case_t;
+ typedef vector< case_t > caseVec;
+
+ caseVec merge_cases;
+ caseVec detect_cases;
+ caseVec custom_cases;
+
+ template < typename comparator >
+ case_t createCase (sequence a, sequence b, sequence c, sequence s) {
+ Diff3< elem, sequence, comparator > diff3(a, b, c);
+ case_t ct;
+
+ diff3.compose();
+
+ ct.S = s;
+ ct.is_merge_success = diff3.merge();
+ ct.merged_seq = diff3.getMergedSequence();
+ return ct;
+ }
+
+ void SetUp() {
+ // merge test
+ merge_cases.push_back(createCase< Compare < elem > >("ab", "b", "bc", "abc")); // 0
+ merge_cases.push_back(createCase< Compare < elem > >("bc", "b", "ab", "abc")); // 1
+ merge_cases.push_back(createCase< Compare < elem > >("qqqabc", "abc", "abcdef", "qqqabcdef")); // 2
+ merge_cases.push_back(createCase< Compare < elem > >("abcdef", "abc", "qqqabc", "qqqabcdef")); // 3
+ merge_cases.push_back(createCase< Compare < elem > >("aaacccbbb", "aaabbb", "aaabbbqqq", "aaacccbbbqqq")); // 4
+ merge_cases.push_back(createCase< Compare < elem > >("aaabbbqqq", "aaabbb", "aaacccbbb", "aaacccbbbqqq")); // 5
+ merge_cases.push_back(createCase< Compare < elem > >("aeaacccbbb", "aaabbb", "aaabbbqqq", "aeaacccbbbqqq")); // 6
+ merge_cases.push_back(createCase< Compare < elem > >("aaabbbqqq", "aaabbb", "aeaacccbbb", "aeaacccbbbqqq")); // 7
+ merge_cases.push_back(createCase< Compare < elem > >("aeaacccbbb", "aaabbb", "aaabebbqqq", "aeaacccbebbqqq")); // 8
+ merge_cases.push_back(createCase< Compare < elem > >("aaabebbqqq", "aaabbb", "aeaacccbbb", "aeaacccbebbqqq")); // 9
+ merge_cases.push_back(createCase< Compare < elem > >("aaacccbbb", "aaabbb", "aeaabbbqqq", "aeaacccbbbqqq")); // 10
+ merge_cases.push_back(createCase< Compare < elem > >("aeaabbbqqq", "aaabbb", "aaacccbbb", "aeaacccbbbqqq")); // 11
+ merge_cases.push_back(createCase< Compare < elem > >("aaacccbbb", "aaabbb", "aaabeebbeeqqq","aaacccbeebbeeqqq")); // 12
+ merge_cases.push_back(createCase< Compare < elem > >("aaabeebbeeqqq", "aaabbb", "aaacccbbb", "aaacccbeebbeeqqq")); // 13
+ merge_cases.push_back(createCase< Compare < elem > >("aiueo", "aeo", "aeKokaki", "aiueKokaki")); // 14
+ merge_cases.push_back(createCase< Compare < elem > >("aeKokaki", "aeo", "aiueo", "aiueKokaki")); // 15
+ merge_cases.push_back(createCase< Compare < elem > >("1234567390", "1234567890", "1239567890", "1239567390")); // 16
+ merge_cases.push_back(createCase< Compare < elem > >("1239567890", "1234567890", "1234567390", "1239567390")); // 17
+ merge_cases.push_back(createCase< Compare < elem > >("qabcdef", "abcdef", "ab", "qab")); // 18
+ merge_cases.push_back(createCase< Compare < elem > >("ab", "abcdef", "qabcdef", "qab")); // 19
+ merge_cases.push_back(createCase< Compare < elem > >("abcdf", "abcdef", "acdef", "acdf")); // 20
+ merge_cases.push_back(createCase< Compare < elem > >("acdef", "abcdef", "abcdf", "acdf")); // 21
+ merge_cases.push_back(createCase< Compare < elem > >("acdef", "abcdef", "abcdfaa", "acdfaa")); // 22
+ merge_cases.push_back(createCase< Compare < elem > >("abcdfaa", "abcdef", "acdef", "acdfaa")); // 23
+
+ // detect confliction test
+ detect_cases.push_back(createCase< Compare < elem > >("adc", "abc", "aec", "")); // 0
+ detect_cases.push_back(createCase< Compare < elem > >("abqdcf", "abcdef", "abqqef", "")); // 1
+
+ // use custom comparator
+ custom_cases.push_back(createCase< CaseInsensitive >("abc", "abc", "abC", "abc"));
+ }
+
+ void TearDown () {}
+
+};
+
+/**
+ * Strdiff3test
+ * check list is following
+ * - merge function
+ * - detect confliction
+ */
+TEST_F (Strdiff3test, merge_test0) {
+ ASSERT_TRUE(merge_cases[0].is_merge_success);
+ ASSERT_EQ(merge_cases[0].S, merge_cases[0].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test1) {
+ ASSERT_TRUE(merge_cases[1].is_merge_success);
+ ASSERT_EQ(merge_cases[1].S, merge_cases[1].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test2) {
+ ASSERT_TRUE(merge_cases[2].is_merge_success);
+ ASSERT_EQ(merge_cases[2].S, merge_cases[2].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test3) {
+ ASSERT_TRUE(merge_cases[3].is_merge_success);
+ ASSERT_EQ(merge_cases[3].S, merge_cases[3].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test4) {
+ ASSERT_TRUE(merge_cases[4].is_merge_success);
+ ASSERT_EQ(merge_cases[4].S, merge_cases[4].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test5) {
+ ASSERT_TRUE(merge_cases[5].is_merge_success);
+ ASSERT_EQ(merge_cases[5].S, merge_cases[5].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test6) {
+ ASSERT_TRUE(merge_cases[6].is_merge_success);
+ ASSERT_EQ(merge_cases[6].S, merge_cases[6].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test7) {
+ ASSERT_TRUE(merge_cases[7].is_merge_success);
+ ASSERT_EQ(merge_cases[7].S, merge_cases[7].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test8) {
+ ASSERT_TRUE(merge_cases[8].is_merge_success);
+ ASSERT_EQ(merge_cases[8].S, merge_cases[8].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test9) {
+ ASSERT_TRUE(merge_cases[9].is_merge_success);
+ ASSERT_EQ(merge_cases[9].S, merge_cases[9].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test10) {
+ ASSERT_TRUE(merge_cases[10].is_merge_success);
+ ASSERT_EQ(merge_cases[10].S, merge_cases[10].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test11) {
+ ASSERT_TRUE(merge_cases[11].is_merge_success);
+ ASSERT_EQ(merge_cases[11].S, merge_cases[11].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test12) {
+ ASSERT_TRUE(merge_cases[12].is_merge_success);
+ ASSERT_EQ(merge_cases[12].S, merge_cases[12].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test13) {
+ ASSERT_TRUE(merge_cases[13].is_merge_success);
+ ASSERT_EQ(merge_cases[13].S, merge_cases[13].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test14) {
+ ASSERT_TRUE(merge_cases[14].is_merge_success);
+ ASSERT_EQ(merge_cases[14].S, merge_cases[14].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test15) {
+ ASSERT_TRUE(merge_cases[15].is_merge_success);
+ ASSERT_EQ(merge_cases[15].S, merge_cases[15].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test16) {
+ ASSERT_TRUE(merge_cases[16].is_merge_success);
+ ASSERT_EQ(merge_cases[16].S, merge_cases[16].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test17) {
+ ASSERT_TRUE(merge_cases[17].is_merge_success);
+ ASSERT_EQ(merge_cases[17].S, merge_cases[17].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test18) {
+ ASSERT_TRUE(merge_cases[18].is_merge_success);
+ ASSERT_EQ(merge_cases[18].S, merge_cases[18].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test19) {
+ ASSERT_TRUE(merge_cases[19].is_merge_success);
+ ASSERT_EQ(merge_cases[19].S, merge_cases[19].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test20) {
+ ASSERT_TRUE(merge_cases[20].is_merge_success);
+ ASSERT_EQ(merge_cases[20].S, merge_cases[20].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test21) {
+ ASSERT_TRUE(merge_cases[21].is_merge_success);
+ ASSERT_EQ( merge_cases[21].S, merge_cases[21].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test22) {
+ ASSERT_TRUE(merge_cases[22].is_merge_success);
+ ASSERT_EQ( merge_cases[22].S, merge_cases[22].merged_seq);
+}
+
+TEST_F (Strdiff3test, merge_test23) {
+ ASSERT_TRUE(merge_cases[23].is_merge_success);
+ ASSERT_EQ(merge_cases[23].S, merge_cases[23].merged_seq);
+}
+
+TEST_F (Strdiff3test, detect_confliction_test0) {
+ ASSERT_FALSE(detect_cases[0].is_merge_success);
+}
+
+TEST_F (Strdiff3test, detect_confliction_test1) {
+ ASSERT_FALSE(detect_cases[1].is_merge_success);
+}
+
+TEST_F (Strdiff3test, custom_comparator_test0) {
+ ASSERT_TRUE(custom_cases[0].is_merge_success);
+ ASSERT_EQ(custom_cases[0].S, custom_cases[0].merged_seq);
+}
--- /dev/null
+#include "dtl_test_common.hpp"
+#include "comparators.hpp"
+
+class Strdifftest : public ::testing::Test
+{
+protected :
+ dtl_test_typedefs(char, string)
+ typedef struct case_t {
+ sequence A;
+ sequence B;
+ size_t editdis;
+ elemVec lcs_v;
+ sequence lcs_s;
+ sesElemVec ses_seq;
+ uniHunkVec hunk_v;
+ size_t editdis_ses;
+ size_t editdis_uni;
+ string path_rses;
+ string path_rhunks;
+ } case_t;
+ typedef vector< case_t > caseVec;
+
+ caseVec diff_cases;
+ caseVec only_editdis_cases;
+ caseVec custom_cases;
+
+ template < typename comparator >
+ case_t createCase (const sequence a, const sequence b, string test_name, bool onlyEditdis = false) {
+ case_t c;
+ elemVec lcs_v;
+ string diff_name("strdiff");
+
+ Diff< elem, sequence, comparator > diff(a, b);
+ if (onlyEditdis) {
+ diff.onOnlyEditDistance();
+ }
+
+ diff.compose();
+ diff.composeUnifiedHunks();
+ lcs_v = diff.getLcsVec();
+
+ if (test_name != "") {
+ string path_lses = create_path(test_name, diff_name, TYPE_DIFF_SES);
+ string path_rses = create_path(test_name, diff_name, TYPE_DIFF_SES, true);
+ string path_lhunks = create_path(test_name, diff_name, TYPE_DIFF_UNI);
+ string path_rhunks = create_path(test_name, diff_name, TYPE_DIFF_UNI, true);
+ diff_resultset_exist_check(path_lses);
+ diff_resultset_exist_check(path_lhunks);
+
+ create_file< elem, sequence, comparator >(path_rses, diff, TYPE_DIFF_SES);
+ create_file< elem, sequence, comparator >(path_rhunks, diff, TYPE_DIFF_UNI);
+
+ c.editdis_ses = cal_diff_uni(path_lses, path_rses);
+ c.editdis_uni = cal_diff_uni(path_lhunks, path_rhunks);
+ c.path_rses = path_rses;
+ c.path_rhunks = path_rhunks;
+ }
+
+ c.A = a;
+ c.B = b;
+ c.editdis = diff.getEditDistance();
+ c.lcs_s = sequence(lcs_v.begin(), lcs_v.end());
+ c.ses_seq = diff.getSes().getSequence();
+ c.hunk_v = diff.getUniHunks();
+
+ return c;
+ }
+
+ void SetUp(void) {
+ diff_cases.push_back(createCase< Compare< elem > >("abc", "abd", "diff_test0"));
+ diff_cases.push_back(createCase< Compare< elem > >("acbdeacbed", "acebdabbabed", "diff_test1"));
+ diff_cases.push_back(createCase< Compare< elem > >("abcdef", "dacfea", "diff_test2"));
+ diff_cases.push_back(createCase< Compare< elem > >("abcbda", "bdcaba", "diff_test3"));
+ diff_cases.push_back(createCase< Compare< elem > >("bokko", "bokkko", "diff_test4"));
+ diff_cases.push_back(createCase< Compare< elem > >("", "", "diff_test5"));
+ diff_cases.push_back(createCase< Compare< elem > >("a", "", "diff_test6"));
+ diff_cases.push_back(createCase< Compare< elem > >("", "b", "diff_test7"));
+ diff_cases.push_back(createCase< Compare< elem > >("acbdeaqqqqqqqcbed", "acebdabbqqqqqqqabed", "diff_test8"));
+
+ only_editdis_cases.push_back(createCase< Compare< elem > >("abc", "abd", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("acbdeacbed", "acebdabbabed", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("abcdef", "dacfea", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("abcbda", "bdcaba", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("bokko", "bokkko", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("", "", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("a", "", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("", "b", "", true));
+ only_editdis_cases.push_back(createCase< Compare< elem > >("acbdeaqqqqqqqcbed", "acebdabbqqqqqqqabed", "", true));
+
+ custom_cases.push_back(createCase< CaseInsensitive >("abc", "Abc", "custom_test0"));
+ }
+
+ void TearDown () {
+ for_each(diff_cases.begin(), diff_cases.end(), Remover< case_t >());
+ for_each(custom_cases.begin(), custom_cases.end(), Remover< case_t >());
+ }
+};
+
+
+/**
+ * Strdifftest
+ * check list is following
+ * - editdistance
+ * - LCS
+ * - SES
+ * - Unified Format Difference
+ * - onOnlyEditDistance
+ */
+
+TEST_F (Strdifftest, diff_test0) {
+
+ EXPECT_EQ(2, diff_cases[0].editdis);
+
+ EXPECT_EQ("ab", diff_cases[0].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[0].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[0].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test1) {
+ EXPECT_EQ(6, diff_cases[1].editdis);
+
+ EXPECT_EQ("acbdabed", diff_cases[1].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[1].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[1].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test2) {
+ EXPECT_EQ(6, diff_cases[2].editdis);
+
+ EXPECT_EQ("acf", diff_cases[2].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[2].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[2].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test3) {
+ EXPECT_EQ(4, diff_cases[3].editdis);
+
+ EXPECT_EQ("bcba", diff_cases[3].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[3].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[3].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test4) {
+ EXPECT_EQ(1, diff_cases[4].editdis);
+
+ EXPECT_EQ("bokko", diff_cases[4].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[4].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[4].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test5) {
+ EXPECT_EQ(0, diff_cases[5].editdis);
+
+ EXPECT_EQ("", diff_cases[5].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[5].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[5].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test6) {
+ EXPECT_EQ(1, diff_cases[6].editdis);
+
+ EXPECT_EQ("", diff_cases[6].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[6].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[6].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test7) {
+ EXPECT_EQ(1, diff_cases[7].editdis);
+
+ EXPECT_EQ("", diff_cases[7].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[7].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[7].editdis_uni);
+}
+
+TEST_F (Strdifftest, diff_test8) {
+ EXPECT_EQ(6, diff_cases[8].editdis);
+
+ EXPECT_EQ("acbdaqqqqqqqbed", diff_cases[8].lcs_s);
+
+ ASSERT_EQ(0, diff_cases[8].editdis_ses);
+
+ ASSERT_EQ(0, diff_cases[8].editdis_uni);
+}
+
+TEST_F (Strdifftest, only_editdis_test0) {
+ EXPECT_EQ(2, only_editdis_cases[0].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[0].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[0].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[0].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test1) {
+ EXPECT_EQ(6, only_editdis_cases[1].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[1].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[1].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[1].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test2) {
+ EXPECT_EQ(6, only_editdis_cases[2].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[2].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[2].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[2].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test3) {
+ EXPECT_EQ(4, only_editdis_cases[3].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[3].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[3].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[3].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test4) {
+ EXPECT_EQ(1, only_editdis_cases[4].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[4].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[4].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[4].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test5) {
+ EXPECT_EQ(0, only_editdis_cases[5].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[5].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[5].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[5].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test6) {
+ EXPECT_EQ(1, only_editdis_cases[6].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[6].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[6].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[6].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test7) {
+ EXPECT_EQ(1, only_editdis_cases[7].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[7].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[7].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[7].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, only_editdis_test8) {
+ EXPECT_EQ(6, only_editdis_cases[8].editdis);
+
+ EXPECT_EQ("", only_editdis_cases[8].lcs_s);
+
+ ASSERT_TRUE(only_editdis_cases[8].ses_seq.empty());
+
+ ASSERT_TRUE(only_editdis_cases[8].hunk_v.empty());
+}
+
+TEST_F (Strdifftest, custom_comparator_test0) {
+ EXPECT_EQ(0, custom_cases[0].editdis);
+
+ EXPECT_EQ("abc", custom_cases[0].lcs_s);
+
+ ASSERT_EQ(0, custom_cases[0].editdis_ses);
+
+ ASSERT_TRUE(custom_cases[0].hunk_v.empty());
+}
--- /dev/null
+#ifndef DTL_COMPARATORS
+#define DTL_COMPARATORS
+
+class CaseInsensitive: public dtl::Compare<char> {
+public:
+ virtual bool impl(const char& a, const char& b) const {
+ return tolower(a) == tolower(b);
+ }
+};
+
+class StringCaseInsensitive: public dtl::Compare<string> {
+public:
+ virtual bool impl(string& a, string& b) const {
+ if (a.length() == b.length()) {
+ bool equal = (strncasecmp(a.c_str(), b.c_str(), a.length()) == 0);
+ return equal;
+ }
+ else {
+ return false;
+ }
+ }
+};
+
+#endif // DTL_COMPARATORS
--- /dev/null
+/**
+ * It is necessary to use googletest to run tests.
+ */
+
+#include <gtest/gtest.h>
+
+int main (int argc, char *argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
--- /dev/null
+
+#include "dtl_test_common.hpp"
+
+string create_path (const string& test_name, string diff_name, enum type_diff t, bool is_use_suffix) {
+ string ret;
+ switch (t) {
+ case TYPE_DIFF_SES:
+ ret = (getcwd(NULL, 0) + string("/") + string("ses") + string("/") + diff_name + string("/") + test_name);
+ break;
+ case TYPE_DIFF_UNI:
+ ret = (getcwd(NULL, 0) + string("/") + string("hunks") + string("/") + diff_name + string("/") + test_name);
+ break;
+ }
+ ret += is_use_suffix ? "_" : "";
+ return ret;
+}
+
+size_t cal_diff_uni (const string& path_l, const string& path_r) {
+ string buf;
+ ifstream lifs(path_l.c_str());
+ ifstream rifs(path_r.c_str());
+
+ vector< string > llines;
+ vector< string > rlines;
+
+ while (getline(lifs, buf)) {
+ llines.push_back(buf);
+ }
+
+ while (getline(rifs, buf)) {
+ rlines.push_back(buf);
+ }
+
+ Diff< string, vector< string > > diff_uni(llines, rlines);
+ diff_uni.compose();
+ return diff_uni.getEditDistance();
+}
+
+bool is_file_exist (string& fs) {
+ FILE *fp;
+ if ((fp = fopen(fs.c_str(), "r")) == NULL) {
+ return false;
+ }
+ fclose(fp);
+ return true;
+}
+
+void diff_resultset_exist_check (string &fs) {
+ if (!is_file_exist(fs)) {
+ cerr << "======================================================Error!!!======================================================" << endl;
+ cerr << "diff result set:" << fs << " is not found." << endl;
+ cerr << "======================================================Error!!!======================================================" << endl;
+ cerr << "excute dtl_test in dtl/test!" << endl;
+ exit(EXIT_FAILURE);
+ }
+}
--- /dev/null
+
+#ifndef DTL_TEST_COMMON
+#define DTL_TEST_COMMON
+
+#include <gtest/gtest.h>
+#include <cstdio>
+#include <string>
+#include <vector>
+#include <utility>
+#include <iostream>
+#include <fstream>
+#include <dtl/dtl.hpp>
+
+using std::cerr;
+using std::endl;
+using std::string;
+using std::vector;
+using std::pair;
+using std::ifstream;
+using std::ofstream;
+
+using dtl::Diff;
+using dtl::Diff3;
+using dtl::Compare;
+using dtl::SES_COMMON;
+using dtl::SES_ADD;
+using dtl::SES_DELETE;
+using dtl::elemInfo;
+using dtl::uniHunk;
+
+#define dtl_test_typedefs(e_type, seq_type) \
+ typedef e_type elem; \
+ typedef seq_type sequence; \
+ typedef pair< elem, elemInfo > sesElem; \
+ typedef vector< elem > elemVec; \
+ typedef vector< sesElem > sesElemVec; \
+ typedef vector< uniHunk< sesElem > > uniHunkVec;
+
+enum type_diff { TYPE_DIFF_SES, TYPE_DIFF_UNI };
+
+string create_path (const string& test_name, string diff_name, enum type_diff t, bool is_use_suffix = false);
+size_t cal_diff_uni (const string& path_l, const string& path_r);
+bool is_file_exist (string& fs);
+void diff_resultset_exist_check (string &fs);
+
+template <typename T>
+class Remover {
+public :
+ void operator()(const T& v){
+ remove(v.path_rses.c_str());
+ remove(v.path_rhunks.c_str());
+ }
+};
+
+template < typename elem, typename sequence, typename comparator >
+void create_file (const string& path, Diff< elem, sequence, comparator >& diff, enum type_diff t) {
+ ofstream ofs;
+ ofs.open(path.c_str());
+ switch (t) {
+ case TYPE_DIFF_SES:
+ diff.printSES(ofs);
+ break;
+ case TYPE_DIFF_UNI:
+ diff.printUnifiedFormat(ofs);
+ break;
+ }
+ ofs.close();
+}
+
+#endif // DTL_TEST_COMMON
--- /dev/null
+@@ -1,0 +1,1 @@
++1
--- /dev/null
+@@ -1,1 +1,0 @@
+-1
--- /dev/null
+@@ -1,10 +1,10 @@
+-1
+-2
+ 3
++5
++1
+ 4
+ 5
+-6
++1
+ 7
+-8
+ 9
++6
+ 10
--- /dev/null
+@@ -1,5 +1,5 @@
+-1
+-2
+ 3
++5
++1
+ 4
+ 5
--- /dev/null
+@@ -1,3 +1,3 @@
+ a
+ b
+-c
++d
--- /dev/null
+@@ -1,10 +1,12 @@
+ a
+ c
++e
+ b
+ d
+-e
+ a
+-c
+ b
++b
++a
++b
+ e
+ d
--- /dev/null
+@@ -1,6 +1,6 @@
++d
+ a
+-b
+ c
+-d
+-e
+ f
++e
++a
--- /dev/null
+@@ -1,6 +1,6 @@
+-a
+ b
++d
+ c
++a
+ b
+-d
+ a
--- /dev/null
+@@ -2,4 +2,5 @@
+ o
+ k
+ k
++k
+ o
--- /dev/null
+@@ -1,1 +1,0 @@
+-a
--- /dev/null
+@@ -1,0 +1,1 @@
++b
--- /dev/null
+@@ -1,9 +1,11 @@
+ a
+ c
++e
+ b
+ d
+-e
+ a
++b
++b
+ q
+ q
+ q
+@@ -11,7 +13,7 @@
+ q
+ q
+ q
+-c
++a
+ b
+ e
+ d
--- /dev/null
+-1
+-2
+ 3
++5
++1
+ 4
+ 5
+-6
++1
+ 7
+-8
+ 9
++6
+ 10
--- /dev/null
+-1
+-2
+ 3
++5
++1
+ 4
+ 5
--- /dev/null
+ a
+ b
+ c
--- /dev/null
+ a
+ b
+-c
++d
--- /dev/null
+ a
+ c
++e
+ b
+ d
+-e
+ a
+-c
+ b
++b
++a
++b
+ e
+ d
--- /dev/null
++d
+ a
+-b
+ c
+-d
+-e
+ f
++e
++a
--- /dev/null
+-a
+ b
++d
+ c
++a
+ b
+-d
+ a
--- /dev/null
+ b
+ o
+ k
+ k
++k
+ o
--- /dev/null
+ a
+ c
++e
+ b
+ d
+-e
+ a
++b
++b
+ q
+ q
+ q
+ q
+ q
+ q
+ q
++a
+-c
+ b
+ e
+ d
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="256"
- height="256"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="wordring.svg"
- inkscape:export-filename="C:\Users\katahiromz\Desktop\wordring.png"
- inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
- <defs
- id="defs4">
- <linearGradient
- inkscape:collect="always"
- id="linearGradient3019">
- <stop
- style="stop-color:#ffff00;stop-opacity:1;"
- offset="0"
- id="stop3021" />
- <stop
- style="stop-color:#ffff00;stop-opacity:0;"
- offset="1"
- id="stop3023" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient3019"
- id="linearGradient3025"
- x1="120.12196"
- y1="1092.5084"
- x2="120.12195"
- y2="796.36218"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(3.0283356e-6,2.0715836e-5)" />
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.82"
- inkscape:cx="37.195121"
- inkscape:cy="86.702902"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:snap-page="true"
- inkscape:snap-bbox="true"
- inkscape:window-width="1366"
- inkscape:window-height="716"
- inkscape:window-x="-8"
- inkscape:window-y="-8"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="レイヤー 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,-796.36218)">
- <rect
- y="797.99231"
- x="1.6301203"
- height="254.38026"
- width="254.38026"
- id="rect2989"
- style="font-size:121.58960724px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.95999995;fill:#ffffff;fill-opacity:1;stroke:#00003d;stroke-width:3.26024055;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans" />
- <rect
- style="opacity:0.95999995;fill:url(#linearGradient3025);fill-opacity:1;stroke:#00003d;stroke-width:4.08758211;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- id="rect2991"
- width="251.91241"
- height="251.91241"
- x="2.0437911"
- y="798.40594" />
- <path
- inkscape:connector-curvature="0"
- id="path3028"
- style="font-size:218.86129761px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#008000;fill-opacity:1;stroke:none;font-family:'TT-JTCウインZ10';-inkscape-font-specification:'TT-JTCウインZ10'"
- d="m 100.52056,833.92792 0,22.56057 -79.46437,0 0,-22.56057 z m 9.7546,30.24076 0,25.92065 -98.25982,0 0,-25.92065 z m -8.08918,32.44882 0,21.98452 -82.08146,0 0,-21.98452 z m 0,29.47276 0,22.08051 -82.08146,0 0,-22.08051 z m 3.68771,29.47272 0,59.04142 -87.43459,0 0,-59.04142 z m -36.520293,32.8328 0,-8.25615 -14.393997,0 0,8.25615 z M 233.04047,836.808 l 0,91.29833 -18.31963,0 29.26382,70.75373 -42.34927,15.74434 -32.95154,-86.49807 -5.82897,0 -7.73231,86.69007 -43.65782,-5.0882 8.56502,-97.34635 0,-75.55386 z m -45.08532,57.21743 0,-23.32858 -24.14861,0 0,23.32858 z" />
- </g>
-</svg>
--- /dev/null
+Tatsuhiko Kubo <cubicdaiya@gmail.com>
+Jan Weiß <jan@geheimwerk.de>
--- /dev/null
+In short, Diff Template Library is distributed under so called "BSD license",
+
+Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the authors nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
return result;
}
+Text::pointer Text::clone()
+{
+ return pointer();
+}
+
QString Text::debug_dump() const
{
QString result;
int RangeData::type() const { return Type; }
+UserData::pointer RangeData::clone()
+{
+ return create(m_begin, m_tail);
+}
+
QString RangeData::debug_dump() const
{
return QString("[RangeData:") + QString::number(m_begin) + ","
QString to_string() const;
+ pointer clone();
+
virtual QString debug_dump() const;
static pointer create();
int tail() const;
int type() const;
+ UserData::pointer clone();
QString debug_dump() const;
static pointer create(int begin_, int tail_);
virtual ~UserData() { }
virtual int type() const { return User; }
+ virtual pointer clone() = 0;
virtual QString debug_dump() const { return "struct UserData."; }
};