245ca2bbed5482108aad6a29c14bb662decb157d
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / UAS / DtSR / DtSR_SearchResultsEntry.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 // $TOG: DtSR_SearchResultsEntry.C /main/13 1998/04/17 11:42:06 mgreess $
24 /*      Copyright (c) 1995,1996 FUJITSU LIMITED         */
25 /*      All Rights Reserved                             */
26
27 #include <assert.h>
28 #include <limits.h>
29 #include <string.h>
30
31 #include <strstream.h>
32
33 #include <StyleSheet/DocParser.h>
34 #include <StyleSheet/Resolver.h>
35 #include <StyleSheet/StyleSheet.h>
36 #include "Tml_TextRenderer.hh"
37
38 #include "config.h"
39 #include "Basic/Error.hh"
40 #include "Basic/FolioObject.hh"
41 #include "Basic/List.hh"
42 #include "Basic/Long_Lived.hh"
43 #include "Managers/MessageMgr.hh"
44 #include "Managers/StyleSheetMgr.hh"
45
46 #include "DtSR_SearchResultsEntry.hh"
47 #include "DtSR_SearchEngine.hh"
48
49 #include "TextParser.hh"
50
51 #include "Other/XmStringLocalized.hh"
52 #include "Managers/CatMgr.hh"
53
54 #ifndef True
55 #define True    1
56 #endif
57 #ifndef False
58 #define False   0
59 #endif
60
61 DtSR_SearchResultsEntry::DtSR_SearchResultsEntry(const char* id,
62                                                  const char* book,
63                                                  const char* section,
64                                                  int dbn, short language,
65                                         UAS_Pointer<DtSR_SearchResults>)
66         : UAS_SearchResultsEntry(id, book, section, Inv_Relevance),
67           f_dbn(dbn), f_language(language), f_zone(0)
68 {
69     int i;
70     for (i=0; i<=UAS_SearchZones::uas_all; i++)
71         f_proximity[i] = 0;
72
73     UAS_String url("mmdb:LOCATOR=");
74     url = url + id;
75     UAS_Pointer<UAS_Common> sec = UAS_Common::create(url);
76
77     f_id = sec->locator();
78 }
79
80 DtSR_SearchResultsEntry::~DtSR_SearchResultsEntry()
81 {
82 }
83
84 UAS_Pointer<UAS_List<UAS_TextRun> >
85 DtSR_SearchResultsEntry::matches() const
86 {
87     if (f_matches == 0) {
88         // preserve logical constness
89         ((DtSR_SearchResultsEntry*)this)->f_matches
90                 = ((DtSR_SearchResultsEntry*)this)->create_matches();
91     }
92
93     return f_matches;
94 }
95
96 int
97 DtSR_SearchResultsEntry::unset_proximity(DtSR_SearchZones::uas_zones uas_zone)
98 {
99     if (uas_zone > UAS_SearchZones::uas_all) { // range error
100         return Beyond_Range;
101     }
102     f_zone &= ~(0x01<<uas_zone);
103     return f_proximity[uas_zone] = 0;
104 }
105
106 int
107 DtSR_SearchResultsEntry::set_proximity(DtSR_SearchZones::uas_zones uas_zone,
108                                         int proximity)
109 {
110     if (uas_zone > UAS_SearchZones::uas_all) { // range error
111         return Beyond_Range;
112     }
113     else if (f_zone & 0x01<<uas_zone) { // already set, reject
114         return Conflict;
115     }
116
117     f_zone |= 0x01<<uas_zone;
118
119     return f_proximity[uas_zone] = proximity;
120 }
121
122 int
123 DtSR_SearchResultsEntry::overlay_proximity(DtSR_SearchZones::uas_zones uas_zone,
124                                                 int proximity)
125 {
126     if (uas_zone > UAS_SearchZones::uas_all) // range error
127         return Beyond_Range;
128
129     if (f_zone & 0x01<<uas_zone == 0)
130         return set_proximity(uas_zone, proximity);
131
132     return f_proximity[uas_zone] = (f_proximity[uas_zone] + proximity) / 2;
133 }
134
135 int
136 DtSR_SearchResultsEntry::get_proximity(DtSR_SearchZones::uas_zones uas_zone) const
137 {
138     if (uas_zone > UAS_SearchZones::uas_all) { // range error
139         return Beyond_Range;
140     }
141     else if ((f_zone & 0x01<<uas_zone) == 0) { // proximity not set yet
142         return Unspecified;
143     }
144
145     return f_proximity[uas_zone];
146 }
147
148 UAS_Pointer<UAS_String>
149 DtSR_SearchResultsEntry::parse_abstract(const char* abs,
150                 UAS_Pointer<UAS_String> &id, UAS_Pointer<UAS_String> &book,
151                 UAS_Pointer<UAS_String> &section)
152 {
153
154     char* abstract = (char *)abs; // logical constness
155
156     assert( abstract && *abstract );
157
158     if (abstract == NULL || *abstract == '\0')
159         return NULL;
160
161     char *p = abstract, *head = p;
162     if ((p = strchr(p, '\t')) == NULL) {
163         return NULL;
164     }
165     else {
166         *p = '\0';
167         id = new UAS_String(head);
168         *p++ = '\t'; // essential to maintain logical constness
169         
170         head = p;
171         if (*p == '\0' || (p = strchr(p, '\t')) == NULL) {
172             return NULL;
173         }
174         else {
175             *p = '\0';
176             book = new UAS_String(head);
177             *p++ = '\t'; // essential to maintain logical constness
178             head = p;
179             section = new UAS_String(p);
180         }
181     }
182
183 #ifdef DEBUG
184     fprintf(stderr, "id=%s, book=%s, section=%s\n", (char*)*(UAS_String*)id,
185                 (char*)*(UAS_String*)book, (char*)*(UAS_String*)section);
186 #endif
187
188     return section;
189 }
190
191 int
192 DtSR_SearchResultsEntry::section_in_abstract(char* abstract, const char* id)
193 {
194     if (abstract == NULL || *abstract == '\0' || id == NULL || *id == '\0')
195         return False;
196
197     UAS_Pointer<UAS_String> section_id, book, section;
198
199     if (parse_abstract(abstract, section_id, book, section) == (const int)NULL) {
200 #ifdef DEBUG
201         fprintf(stderr, "(ERROR) parse_abstract failed\n");
202 #endif
203         return False;
204     }
205
206     if (strcmp(id, (char*)*(UAS_String*)section_id))
207         return False;
208     else
209         return True;
210 }
211
212 static unsigned int proximity2relevance(int prox)
213 {
214     if (prox == 0)
215         return DtSR_SearchResultsEntry::Utmost_Relevance;
216
217     float inv_prox;
218
219     if (prox == (int)INT_MAX)
220         inv_prox = 0;
221     else
222         inv_prox = 1 / (float)prox; // 0 to 1;
223
224     return inv_prox * DtSR_SearchResultsEntry::Utmost_Relevance;
225 }
226
227 unsigned int
228 DtSR_SearchResultsEntry::relevance()
229 {
230     if (f_relevance != Inv_Relevance)
231         return f_relevance;
232
233     int prox;
234     if ((prox = get_proximity(DtSR_SearchZones::uas_all)) >= 0) {
235         return f_relevance = proximity2relevance(prox);
236     }
237
238     int nzones = 0;
239     unsigned int relevance = 0;
240     for (int i=0; i<DtSR_SearchZones::uas_all; i++) {
241         if ((prox = get_proximity((DtSR_SearchZones::uas_zones) i)) >= 0) {
242             relevance += proximity2relevance(prox);
243             nzones++;
244         }
245     }
246     assert( nzones > 0 );
247     return f_relevance = relevance / nzones;
248 }
249
250 UAS_Pointer<UAS_List<UAS_TextRun> >
251 DtSR_SearchResultsEntry::create_matches()
252 {
253
254 #ifdef DEBUG
255     fprintf(stderr, "(DEBUG) UAS_Common is being created from id=\"%s\"\n",
256                                                         (char*)f_id);
257 #endif
258     UAS_Pointer<UAS_Common> doc = UAS_Common::create(f_id);
259
260 #ifdef DEBUG
261     fprintf(stderr,
262                 "(DEBUG) id=%s\n\t"
263                 "book_name=%s title=%s\n",
264                 (char*)(doc->id()),
265                 (char*)(doc->book_name()), (char*)(doc->title()));
266 #endif
267
268 #ifdef DUMP_NODES
269     {
270         ofstream out("OriginalText");
271         out << (char *) doc->data();
272     }
273 #endif
274
275     try
276         {
277             style_sheet_mgr().initOnlineStyleSheet(doc);
278         }
279     catch_noarg (StyleSheetSyntaxError)
280         {
281 #ifdef JOE_HATES_THIS
282             message_mgr().error_dialog(
283                 (char*)UAS_String(CATGETS(Set_Messages, 39, "File a Bug")));
284 #else
285             throw(CASTEXCEPT Exception());
286 #endif
287         }
288     end_try;
289
290     istrstream input((char *)doc->data());
291     ostrstream output;
292     
293     try
294         {
295             Tml_TextRenderer    renderer(output, f_search_res->search_zones());
296             Resolver resolver(*gPathTab, renderer);
297             DocParser docparser(resolver);
298
299             docparser.parse(input);
300         }
301     catch_any()
302         {
303             ON_DEBUG(cerr << "DtSR_SearchResultsEntry::create_matches...exception thrown" << '\n' << flush);
304             rethrow;
305         }
306     end_try;
307
308     char* text = output.str();
309     *(text + output.pcount()) = '\0';
310
311 #ifdef DUMP_NODES
312     {
313         ofstream out("ParsedText");
314         out << text;
315     }
316 #endif
317
318 #ifdef DEBUG
319     fprintf(stderr, "(DEBUG) stems=0x%p, count=%d\n",
320                         (char*)f_search_res->stems(f_dbn)->stems(),
321                         f_search_res->stems(f_dbn)->count());
322
323     int n_of_stems = 0;
324     for (; n_of_stems < f_search_res->stems(f_dbn)->count(); n_of_stems++) {
325         fprintf(stderr, "(DEBUG) %dth stem = %s\n", n_of_stems,
326                         (f_search_res->stems(f_dbn)->stems())[n_of_stems]);
327     }
328 #endif
329
330     int stype = f_search_res->search_type();
331
332     DtSrHitword* kwics = NULL;
333     long n_kwics = 0;
334
335     char* parseout = NULL;
336
337     // hack! overwrite f_language, since austext's value is wrong
338     // In future, the next lines should be removed.
339     const char* lang = getenv("LANG");
340     if (lang && !strncmp(lang, "ja", strlen("ja")))
341         f_language = DtSrLaJPN;
342     else
343         f_language = DtSrLaENG;
344         
345     if (f_language == DtSrLaJPN) { // do not trust DtSearchHighlight!
346         int count        = f_search_res->stems(f_dbn)->count();
347
348         ostrstream stemsbuf;
349         for (int i = 0; i < count; i++) {
350             stemsbuf << (f_search_res->stems(f_dbn)->stems())[i] << '\n';
351         }
352         char* stems = stemsbuf.str();
353         *(stems + stemsbuf.pcount()) = '\0';
354
355         parseout = StringParser::hilite(text, count, stems);
356
357         assert( parseout != NULL );
358
359         delete[] stems;
360     }
361     else {
362
363         static DtSR_SearchEngine& search_engine = DtSR_SearchEngine::search_engine();
364         if (DtSearchHighlight(
365                 search_engine.char_db_name(f_dbn),
366                 text, &kwics, &n_kwics, stype,
367                 (char*)f_search_res->stems(f_dbn)->stems(),
368                 f_search_res->stems(f_dbn)->count()) != DtSrOK) {
369
370             fprintf(stderr, "(ERROR) DtSearchHighlight failed\n");
371 #ifdef DEBUG
372             abort();
373 #endif
374         }
375
376 #ifdef DEBUG
377         fprintf(stderr, "(DEBUG) %d hit found in %s\n", n_kwics, (char*)f_id);
378 #endif
379     }
380
381     UAS_Pointer<UAS_List<UAS_TextRun> >
382                                 matches = new UAS_List<UAS_TextRun>;
383
384     // convert kwics to textrun
385     if (parseout == NULL && kwics) {
386         ostrstream textrunbuf;
387         for (int i = 0; i < n_kwics; i++)
388             textrunbuf << kwics[i].offset << '\t' << kwics[i].length << '\n';
389         parseout = textrunbuf.str();
390         *(parseout + textrunbuf.pcount()) = '\0';
391     }
392     else if (parseout == NULL)
393     {
394         if (text)
395             delete[] text;
396         return matches;
397     }
398
399 #ifdef DEBUG
400     fprintf(stderr, "(DEBUG) byte offset and length\n%s", parseout);
401 #endif
402
403     istrstream textruns(parseout);
404
405     char linebuf[128];
406     while (textruns.get(linebuf, 128, '\n')) {
407         char newline;
408         textruns.get(newline);
409         assert( newline == '\n');
410
411         char* off_str = linebuf;
412         char* len_str = strchr(linebuf, '\t');
413         assert( len_str && *len_str == '\t' );
414         *len_str++ = '\0';
415
416         int mode = True;
417
418         const char* cursor = (const char*)text;
419         assert( *cursor == ShiftIn || *cursor == ShiftOut );
420
421         int off = atoi(off_str);
422         int vcc = 0;
423         
424         while (off > 0) {
425
426             int scanned = 0;
427             if (*cursor == '\n' || *cursor == '\t' || *cursor == ' '  ||
428                 *cursor == 0x0D || (unsigned char)*cursor == 0xA0) {
429                 scanned++;
430             }
431             else if (*cursor == ShiftIn || *cursor == ShiftOut) {
432                 if (*cursor == ShiftIn)
433                     mode = True;
434                 else
435                     mode = False;
436                 scanned++;
437             }
438             else {
439                 scanned = mblen(cursor, MB_CUR_MAX);
440                 assert( scanned >= 0 );
441                 vcc++;
442             }
443
444             off -= scanned;
445             cursor += scanned;
446         }
447
448         if (mode == False)
449             continue;
450
451         assert( off == 0 );
452
453         int len = atoi(len_str);
454         // remove leading white-spaces
455         for (; len && (*cursor == ' ' || *cursor == '\t' ||
456                        *cursor == '\n'|| *cursor == 0x0D); cursor++, len--);
457
458         // remove trailing white-spaces
459         if (len > 0) {
460             for (const char*  p = cursor + len - 1;
461                  *p==' ' || *p=='\t' || *p=='\n' || *p==0x0D; p--, len--);
462         }
463
464         if (len == 0)
465             continue;
466
467         int vlen = 0;
468         for (; len > 0; vlen++) {
469             int scanned = mblen(cursor, MB_CUR_MAX);
470             assert( scanned >= 0 );
471             len -= scanned;
472             cursor += scanned;
473         }
474
475         UAS_Pointer<UAS_TextRun> textrun = new UAS_TextRun(vcc, vlen);
476         matches->insert_item(textrun);
477     }
478
479     if (text)
480         delete[] text;
481     if (parseout)
482         delete[] parseout;
483
484     return matches;
485 }
486
487 UAS_Pointer<DtSR_SearchResults>
488 DtSR_SearchResultsEntry::search_result(UAS_Pointer<DtSR_SearchResults>& res)
489 {
490     UAS_Pointer<DtSR_SearchResults> rval = f_search_res;
491
492     f_search_res = res;
493
494     return rval;
495 }
496
497 void
498 DtSR_SearchResultsEntry::unreference()
499 {
500     static int nest = 0;
501     if (nest++ == 0)
502         UAS_Base::unreference();
503     nest--;
504 }