Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[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 libraries 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 <sstream>
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)0) {
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 (unsigned int)(inv_prox * DtSR_SearchResultsEntry::Utmost_Relevance);
225 }
226
227 unsigned int
228 DtSR_SearchResultsEntry::relevance()
229 {
230     if ((int)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     mtry
276         {
277             style_sheet_mgr().initOnlineStyleSheet(doc);
278         }
279 //  catch_noarg (StyleSheetSyntaxError)
280     mcatch_any()
281         {
282 #ifdef JOE_HATES_THIS
283             message_mgr().error_dialog(
284                 (char*)UAS_String(CATGETS(Set_Messages, 39, "File a Bug")));
285 #else
286             throw(CASTEXCEPT Exception());
287 #endif
288         }
289     end_try;
290
291     istringstream input((char *)doc->data());
292     ostringstream output;
293     
294     mtry
295         {
296             Tml_TextRenderer    renderer(output, f_search_res->search_zones());
297             Resolver resolver(*gPathTab, renderer);
298             DocParser docparser(resolver);
299
300             docparser.parse(input);
301         }
302     mcatch_any()
303         {
304             ON_DEBUG(cerr << "DtSR_SearchResultsEntry::create_matches...exception thrown" << '\n' << flush);
305             rethrow;
306         }
307     end_try;
308
309     string outpstr = output.str();
310     char* text = (char*)outpstr.c_str();
311
312 #ifdef DUMP_NODES
313     {
314         ofstream out("ParsedText");
315         out << text;
316     }
317 #endif
318
319 #ifdef DEBUG
320     fprintf(stderr, "(DEBUG) stems=0x%p, count=%d\n",
321                         (char*)f_search_res->stems(f_dbn)->stems(),
322                         f_search_res->stems(f_dbn)->count());
323
324     int n_of_stems = 0;
325     for (; n_of_stems < f_search_res->stems(f_dbn)->count(); n_of_stems++) {
326         fprintf(stderr, "(DEBUG) %dth stem = %s\n", n_of_stems,
327                         (f_search_res->stems(f_dbn)->stems())[n_of_stems]);
328     }
329 #endif
330
331     int stype = f_search_res->search_type();
332
333     DtSrHitword* kwics = NULL;
334     long n_kwics = 0;
335
336     const char* parseout = NULL;
337
338     // hack! overwrite f_language, since austext's value is wrong
339     // In future, the next lines should be removed.
340     const char* lang = getenv("LANG");
341     if (lang && !strncmp(lang, "ja", strlen("ja")))
342         f_language = DtSrLaJPN;
343     else
344         f_language = DtSrLaENG;
345         
346     if (f_language == DtSrLaJPN) { // do not trust DtSearchHighlight!
347         int count        = f_search_res->stems(f_dbn)->count();
348
349         ostringstream stemsbuf;
350         for (int i = 0; i < count; i++) {
351             stemsbuf << (f_search_res->stems(f_dbn)->stems())[i] << '\n';
352         }
353         string stemsbstr = stemsbuf.str();
354         char* stems = (char*)stemsbstr.c_str();
355
356         parseout = StringParser::hilite(text, count, stems);
357
358         assert( parseout != NULL );
359
360         delete[] stems;
361     }
362     else {
363
364         static DtSR_SearchEngine& search_engine = DtSR_SearchEngine::search_engine();
365         if (DtSearchHighlight(
366                 search_engine.char_db_name(f_dbn),
367                 text, &kwics, &n_kwics, stype,
368                 (char*)f_search_res->stems(f_dbn)->stems(),
369                 f_search_res->stems(f_dbn)->count()) != DtSrOK) {
370
371             fprintf(stderr, "(ERROR) DtSearchHighlight failed\n");
372 #ifdef DEBUG
373             abort();
374 #endif
375         }
376
377 #ifdef DEBUG
378         fprintf(stderr, "(DEBUG) %ld hit found in %s\n", n_kwics, (char*)f_id);
379 #endif
380     }
381
382     UAS_Pointer<UAS_List<UAS_TextRun> >
383                                 matches = new UAS_List<UAS_TextRun>;
384
385     // convert kwics to textrun
386     string textrbstr;
387     if (parseout == NULL && kwics) {
388         ostringstream textrunbuf;
389         for (int i = 0; i < n_kwics; i++)
390             textrunbuf << kwics[i].offset << '\t' << kwics[i].length << '\n';
391         textrbstr = textrunbuf.str();
392         parseout = (char*)textrbstr.c_str();
393     }
394     else if (parseout == NULL)
395     {
396         return matches;
397     }
398
399 #ifdef DEBUG
400     fprintf(stderr, "(DEBUG) byte offset and length\n%s", parseout);
401 #endif
402
403     istringstream 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                 vcc++;
441
442                 /* skip one byte in case of failure */
443                 if (scanned < 0)
444                     scanned = 1;
445             }
446
447             off -= scanned;
448             cursor += scanned;
449         }
450
451         if (mode == False)
452             continue;
453
454         assert( off == 0 );
455
456         int len = atoi(len_str);
457         // remove leading white-spaces
458         for (; len && (*cursor == ' ' || *cursor == '\t' ||
459                        *cursor == '\n'|| *cursor == 0x0D); cursor++, len--);
460
461         // remove trailing white-spaces
462         if (len > 0) {
463             for (const char*  p = cursor + len - 1;
464                  *p==' ' || *p=='\t' || *p=='\n' || *p==0x0D; p--, len--);
465         }
466
467         if (len == 0)
468             continue;
469
470         int vlen = 0;
471         for (; len > 0; vlen++) {
472             int scanned = mblen(cursor, MB_CUR_MAX);
473             assert( scanned >= 0 );
474             len -= scanned;
475             cursor += scanned;
476         }
477
478         UAS_Pointer<UAS_TextRun> textrun = new UAS_TextRun(vcc, vlen);
479         matches->insert_item(textrun);
480     }
481
482     return matches;
483 }
484
485 UAS_Pointer<DtSR_SearchResults>
486 DtSR_SearchResultsEntry::search_result(UAS_Pointer<DtSR_SearchResults>& res)
487 {
488     UAS_Pointer<DtSR_SearchResults> rval = f_search_res;
489
490     f_search_res = res;
491
492     return rval;
493 }
494
495 void
496 DtSR_SearchResultsEntry::unreference()
497 {
498     static int nest = 0;
499     if (nest++ == 0)
500         UAS_Base::unreference();
501     nest--;
502 }