/* */
This source file includes following definitions.
- generate
1 /* -*- coding: utf-8; mode: c++; -*-
2 * Concordance to A. S. Pushkin's Works - KWIC generation
3 * $Id: ConcordanceKWIC.cpp 47 2014-06-04 17:03:50Z isao $
4 * Copyright (C) 2012, isao yasuda
5 */
6
7 #include "Concordance.hpp"
8
9 using namespace Wt;
10 using namespace boost::interprocess;
11
12 // 共有メモリオブジェクト
13 extern managed_shared_memory segment1; // Corpus, TitleDB
14 extern managed_shared_memory segment2; // Word Tree
15 extern managed_shared_memory segment3; // work
16
17 // 共有メモリリソースアドレス
18 extern shmmap_type* corpus_map; // Corpus map アドレス
19 extern shmmap_type* tdb_map; // TitleDB アドレス
20 extern offset_ptr<tree> word_tree_l; // Word Tree Lemmatized form アドレス
21 extern offset_ptr<tree> word_tree_a; // Word Tree Appearance form アドレス
22
23 // 近接隣接距離 MAX 値
24 extern int MAXDIST;
25
26 // リンクリソース
27 static const std::string URLbase = "/pushkin/web/";
28
29 // コンコーダンス生成
30 void Concordance::generate()
31 {
32 boost::timer t; // 処理時間タイマ
33 std::stringstream ss; // 数値変換用ストリングストリーム
34
35 // ユーザ入力単語式
36 std::string ldata = (expEdit->text()).toUTF8(); // 元入力
37 std::string userip = "IP: " + ipadr + " Req: " + ldata;
38 logging(userip.c_str(), INFO);
39
40 // 大文字変換
41 std::string udata = boost::locale::to_upper(ldata);
42
43 // 実行 Word Tree の選択,Lemmatizer 実行可否判断
44 offset_ptr<tree> word_tree; // 実行 Word Tree
45 WString opts(ldata, UTF8);
46 opts = L" (Ваш ввод: " + opts;
47 // Lem Tree on なら見出語形ツリー選択
48 if (wbg0->checkedId() == 0) {
49 word_tree = word_tree_l; // select Lemmatized
50 opts += L"; DB: Lemmatized; Лемматизация ввода: ON)";
51 } else {
52 // off なら出現形ツリー選択
53 word_tree = word_tree_a; // select Non Lemmatized
54 opts += L"; DB: Non Lemmatized; Лемматизация входа: OFF)";
55 }
56
57 // ユーザ入力をトークナイザで分解し,式を vector にセット
58 std::vector<std::string> wds;
59 exp_tokenizer(udata, wds);
60 if (wds.empty()) {
61 errorsend("No expressions specified.");
62 return;
63 }
64
65 // 式のチェック
66 std::vector<query> qrv; // クエリ構造体の vector
67 for (std::vector<std::string>::iterator wi = wds.begin();
68 wi != wds.end(); wi++) {
69 // クエリをチェックし,演算種別/オペランドをセットした vector を作成し,rc 判別
70 int r = checkquery(*wi, genrev, qrv);
71 switch (r) {
72 case 1: {// 記号だけの文字条件エラー
73 escapetag(*wi);
74 errorsend("Regular expression has no normal charcters: " + *wi);
75 return;
76 }
77 case 2: {// ジャンル文字条件エラー
78 escapetag(*wi);
79 errorsend("On genre condition more than 7 genres expression must have more than 1 normal character: " + *wi);
80 return;
81 }
82 case 3: {// 近接隣接演算種別エラー
83 escapetag(*wi);
84 errorsend("Adjacent kind error. Kind must be W or L: " + *wi);
85 return;
86 }
87 case 4: {// 近接隣接演算距離エラー
88 escapetag(*wi);
89 char num[10];
90 sprintf(num, "%d", MAXDIST);
91 std::string msg(num);
92 msg = "Adjacent operand > MAXDIST " + msg + ": " + *wi;
93 errorsend(msg);
94 return;
95 }
96 case 5: {// 近接隣接演算フォーマットエラー
97 escapetag(*wi);
98 errorsend("Adjacent format error: " + *wi);
99 return;
100 }
101 default:// 0 かその他正常
102 break;
103 }
104 }
105
106 // 入力があっても適切なクエリがひとつもなかった場合
107 if (qrv.empty()) {
108 errorsend("No valid expressions.");
109 return;
110 }
111
112 // ラベルの出力
113 wmesg->setTextFormat(XHTMLText);
114 wmesg->setText(L"<div class=\"blk\"></div><br />Выражения: ");
115
116 // ユーザ入力内容の表示
117 // 変換済ユーザ入力
118 WString wdata(udata, UTF8);
119 wdata += opts;
120 pmesg->setTextFormat(PlainText);
121 pmesg->setStyleClass("exp");
122 pmesg->setText(wdata);
123
124 // char_string 用アロケータ(Shared Memory Resources)
125 void_allocator valloc(segment3.get_segment_manager());
126 // メッセージワーク
127 std::string sm("");
128 // 出力テーブル行初期化
129 int row = 0;
130 // KWIC タブを activate
131 tabs->setCurrentIndex(tabs->indexOf(kwic));
132 // テーブルのクリア(前回出力を消す)
133 ctbl->clear();
134
135 // ヘッダ1の出力 - 背景: 青; 文字: 灰
136 // header 1: (0)No.<r> : (1){null}<l> : (2)Context<l> : (3)Position<c>
137 WTableCell* cell00 = ctbl->elementAt(row, 0);
138 cell00->addWidget(new Wt::WText(L"No.", XHTMLText));
139 cell00->setStyleClass("seqh");
140 WTableCell* cell01 = ctbl->elementAt(row, 1);
141 cell01->addWidget(new Wt::WText(L" ", XHTMLText));
142 cell01->setStyleClass("preh");
143 WTableCell* cell02 = ctbl->elementAt(row, 2);
144 cell02->addWidget(new Wt::WText(L"Context", XHTMLText));
145 cell02->setStyleClass("afth");
146 WTableCell* cell03 = ctbl->elementAt(row, 3);
147 cell03->addWidget(new Wt::WText(L"Position", XHTMLText));
148 cell03->setStyleClass("posh");
149 row++;
150
151 // 単語式ごとに Word Tree を探索し,該当単語ノードを出力する
152 int totalc = 0; // トータル明細行
153 for (std::vector<query>::iterator qit = qrv.begin();
154 qit != qrv.end(); qit++) {
155
156 // 式表示: タグセーフにして型変換し,式ヘッダ出力
157 std::string ex(qit->qsrc);
158 escapetag(ex);
159 WString Exp(ex, UTF8);
160 // header 2: (0-4){exp}<l> 背景: 青; 文字: 灰;
161 WTableCell* cell10 = ctbl->elementAt(row, 0);
162 cell10->addWidget(new Wt::WText(Exp, XHTMLText));
163 cell10->setColumnSpan(4);
164 cell10->setStyleClass("exd");
165 row++;
166
167 // 各種探索でヒットした単語情報の vector
168 std::vector<wordinfo> twdvec;
169 boost::u32regex rw;
170
171 // 単語式の種別に応じて Tree を探索し,該当単語ノードを取得し,vector にセット
172 if (qit->adjk) {
173 // 近接隣接演算
174 // Lemmatizing on なら Lemmatizer で入力を見出語変換する
175 if (wbg1->checkedId() == 0) {
176 if (! qit->kind1)
177 lemmatize(qit->qexp1, lem); // 検査ワード見出語変換
178 if (! qit->kind2)
179 lemmatize(qit->qexp2, lem); // 対照ワード見出語変換
180 }
181 word_tree->find_adjacent_tree_set
182 (qit->qexp1, qit->qexp2, qit->adjk, qit->adjd, twdvec, genrev);
183 } else {
184 if (qit->kind1) {
185 // 正規表現探索
186 try {
187 rw = boost::make_u32regex((qit->qexp1).c_str());
188 word_tree->find_regex_tree_set(rw, twdvec, genrev);
189 } catch (const std::exception& e) {
190 errorsend("Regular expression error: " + qit->qexp1);
191 }
192 } else {
193 // 完全一致探索
194 // Lemmatizing on なら Lemmatizer で入力を見出語変換する
195 if (wbg1->checkedId() == 0)
196 lemmatize(qit->qexp1, lem); // 見出語変換
197 word_tree->find_tree_set(qit->qexp1, twdvec, genrev);
198 }
199 }
200
201 // コンコーダンス明細出力
202 int seq = 1; // 明細行カウンタ
203 char_string keyc(valloc); // Shared Memory Map キー
204
205 // 単語ノードごとのループ
206 for (std::vector<wordinfo>::iterator ndit = twdvec.begin();
207 ndit != twdvec.end(); ndit++) {
208
209 // MAXLINES 行を超えを検知したら,そこで終了
210 if (row >= MAXLINES) {
211 ss << userip << " " << row << " rows proceeded.\n";
212 logging(ss.str().c_str(), INFO);
213 ssclear(ss);
214 goto END;
215 }
216
217 // 見出語,出現回数行を出力 - 背景 赤; 文字 薄紅
218 WString Word(ndit->word.get(), UTF8);
219 char wc[7];
220 std::sprintf(wc, "%d", ndit->counter);
221 WString Count(wc, UTF8);
222 // header 3:(0-1)Выражение: {exp}<l>:(2){counter}<r>:(3){null}<l>
223 WTableCell* cell20 = ctbl->elementAt(row, 0);
224 cell20->addWidget(new Wt::WText(Word, XHTMLText));
225 cell20->setStyleClass("lem");
226 cell20->setColumnSpan(2);
227 WTableCell* cell22 = ctbl->elementAt(row, 2);
228 cell22->addWidget(new Wt::WText(Count, XHTMLText));
229 cell22->setStyleClass("cnt");
230 cell22->setColumnSpan(1);
231 WTableCell* cell23 = ctbl->elementAt(row, 3);
232 cell23->addWidget(new Wt::WText(L" ", XHTMLText));
233 cell23->setStyleClass("spc");
234 row++;
235
236 // Context ヒット明細行出力
237 for (std::vector<offset_ptr<wpos> >::iterator wpi =
238 ndit->wposv.begin(); wpi != ndit->wposv.end(); wpi++) {
239
240 // 位置情報取得
241 int gen = (*wpi)->getgenre(); // ジャンル
242 int fno = (*wpi)->getfileno(); // 作品番号
243 int lno = (*wpi)->getlineno(); // 行番号
244 int lid = (*wpi)->getlineid(); // ID
245 int lps = (*wpi)->getlinepos(); // 行内出現アドレス
246
247 // 1.シーケンス番号編集
248 char wcp[22];
249 std::sprintf(wcp, "%d", seq++);
250 WString Seq(wcp, UTF8);
251
252 // 2.コンテクスト編集
253 // - "ジャンル:作品番号:行番号:ID" をキーに Corpus 本文を取得する
254 std::sprintf(wcp, "%02d:%04d:%06d:%06d",
255 gen, fno, lno, lid);
256 keyc = wcp;
257 Shmit mit = corpus_map->find(keyc);
258 // - カレント行を取得
259 std::string curln, preln, aftln;
260 if (mit != corpus_map->end())
261 curln = (mit->second).c_str();
262 else
263 std::cerr << "* corpus key: " << wcp << " not found.\n";
264 // - 前行を取得
265 if (mit != corpus_map->begin()) {
266 mit--;
267 // 同一作品なら前の行をセット
268 if (checkfno((mit->first).c_str(), fno))
269 preln = (mit->second).c_str();
270 else
271 preln = "";
272 mit++;
273 } else
274 std::cerr << "* preln is : " << wcp << " not found.\n";
275 // - 後行を取得
276 if (++mit != corpus_map->end())
277 // 同一作品ならあとの行をセット
278 if (checkfno((mit->first).c_str(), fno))
279 aftln = (mit->second).c_str();
280 else
281 aftln = "";
282 // - ヒット語/行の強調装飾
283 std::string out1, out2;
284 escapetag(curln);
285 escapetag(preln);
286 escapetag(aftln);
287 //emphword(curln, preln, aftln, out1, out2, lps);
288 emphword(curln, preln, aftln, out1, out2, lps, cprelen, caftlen); // debug
289 // - Context 前後行をセット
290 WString Pre(out1, UTF8);
291 WString Aft(out2, UTF8);
292
293 // 3.位置情報編集 (ジャンル:作品番号:行番号)
294 std::sprintf(wcp, "%02d:%04d:%06d", gen, fno, lno);
295 std::string posit(wcp);
296 // - "ジャンル:作品番号" をキーに TitleDB 情報を取得
297 std::string title;
298 std::sprintf(wcp, "%02d:%04d", gen, fno);
299 keyc = wcp; // char_string に変換
300 mit = tdb_map->find(keyc);
301 // - Title
302 if (mit != tdb_map->end()) {
303 title = (mit->second).c_str();
304 escapetag(title);
305 } else {
306 std::string s2 = keyc.c_str();
307 sm = "Key: " + s2 + " not found in tdb.";
308 title = "Unknown";
309 logging(sm.c_str(), WARN);
310 }
311 // - 位置情報/Title をリンク付きで出力
312 std::sprintf(wcp, "%04d.html#%06d", fno, lid);
313 std::string s3 = "<a href=\"" + URLbase + wcp
314 + "\" target=\"_blank\" title=\""
315 + title + "\">" + posit + "</a>";
316 WString Pos(s3, UTF8);
317
318 // 4.Context 明細行の出力 - 背景: 背景色; 文字: 文字色;
319 // contents:(0){seq}<r>:(1){pre}<l>:(2){aft}<l>:(3){pos}<c>
320 WTableCell* cell30 = ctbl->elementAt(row, 0);
321 cell30->addWidget(new Wt::WText(Seq, XHTMLText));
322 cell30->setStyleClass("seq");
323 WTableCell* cell31 = ctbl->elementAt(row, 1);
324 cell31->addWidget(new Wt::WText(Pre, XHTMLText));
325 cell31->setStyleClass("pre");
326 WTableCell* cell32 = ctbl->elementAt(row, 2);
327 cell32->addWidget(new Wt::WText(Aft, XHTMLText));
328 cell32->setStyleClass("aft");
329 WTableCell* cell33 = ctbl->elementAt(row, 3);
330 cell33->addWidget(new Wt::WText(Pos, XHTMLText));
331 cell33->setStyleClass("pos");
332 row++;
333
334 // 5.トータル明細出力カウンタのインクリメント
335 totalc++;
336
337 // 6.1000 行を超えるごとにロギング出力
338 if ((row > 1000) && ((row % 1000) == 0)) {
339 ss << userip << " " << row << " rows proceeded.\n";
340 logging(ss.str().c_str(), INFO);
341 ssclear(ss);
342 }
343 }
344 }
345 }
346 END:
347 // 終了処理
348 double elps = t.elapsed(); // 処理時間
349 ss << userip << " elapse: " << elps << " sec; " << row << " rec;"
350 << " genres: ";
351 for (std::vector<int>::iterator gi = genrev.begin();
352 gi != genrev.end(); gi++)
353 ss << *gi << " ";
354 ss << "\n";
355 logging(ss.str().c_str(), INFO);
356 ssclear(ss);
357 // 終了メッセージ
358 if (row >= MAXLINES) {
359 // MAX 超過の場合: 警告
360 ss << "KWIC proceeded over " << MAXLINES << " rows. Broken off."
361 << "Reconsider your condition of Expression or Genres.\n";
362 errorsend(ss.str());
363 ssclear(ss);
364 } else {
365 // 正常終了の場合: 処理時間,出力 Context 数
366 ss << "<div class=\"nrm\">Normally ended. Elapse time: " << elps
367 << " sec; Output: " << totalc << " hit(s);</div>";
368 WString emsg(ss.str());
369 ssclear(ss);
370 // 画面に終了メッセージをレイアウト
371 cmesg->setTextFormat(XHTMLText);
372 cmesg->setText(emsg);
373 }
374 }
/* */