source: trunk/gsdl/src/recpt/formattools.cpp@ 825

Last change on this file since 825 was 825, checked in by davidb, 24 years ago

Support for more than one metavalue for the same metadata name

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/**********************************************************************
2 *
3 * formattools.cpp --
4 * Copyright (C) 1999 The New Zealand Digital Library Project
5 *
6 * A component of the Greenstone digital library software
7 * from the New Zealand Digital Library Project at the
8 * University of Waikato, New Zealand.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 * $Id: formattools.cpp 825 1999-12-13 02:45:16Z davidb $
25 *
26 *********************************************************************/
27
28/*
29 $Log$
30 Revision 1.15 1999/12/13 02:45:16 davidb
31 Support for more than one metavalue for the same metadata name
32
33 Revision 1.14 1999/10/30 22:23:11 sjboddie
34 moved table functions from browsetools
35
36 Revision 1.13 1999/10/14 23:01:24 sjboddie
37 changes for new browsing support
38
39 Revision 1.12 1999/10/10 08:14:07 sjboddie
40 - metadata now returns mp rather than array
41 - redesigned browsing support (although it's not finished so
42 won't currently work ;-)
43
44 Revision 1.11 1999/09/28 20:38:19 rjmcnab
45 fixed a couple of bugs
46
47 Revision 1.10 1999/09/07 04:56:55 sjboddie
48 added GPL notice
49
50 Revision 1.9 1999/09/02 00:31:25 rjmcnab
51 fixed small error.
52
53 Revision 1.8 1999/08/20 00:56:38 sjboddie
54 added cgisafe option - you can now do something like [cgisafe:Title] if
55 you want Title to be entered safely into a url
56
57 Revision 1.7 1999/08/10 22:38:08 sjboddie
58 added some more format options
59
60 Revision 1.6 1999/07/30 02:25:42 sjboddie
61 made format_date function global
62
63 Revision 1.5 1999/07/21 05:00:00 sjboddie
64 added some date formatting
65
66 Revision 1.4 1999/07/20 03:02:15 sjboddie
67 added an [icon] option, added ability to call get_formatted_string
68 with icon and link arguments set
69
70 Revision 1.3 1999/07/09 02:44:35 sjboddie
71 fixed parent(All) function so it only outputs parents and not current
72 level meta
73
74 Revision 1.2 1999/07/08 20:48:33 rjmcnab
75 Added ability to print the result number
76
77 Revision 1.1 1999/07/07 05:49:34 sjboddie
78 had another crack at the format string code - created a new formattools
79 module. It can now handle {If} and {Or} statements although there's a
80 bug preventing nested if's and or's.
81
82 */
83
84
85#include "formattools.h"
86#include "cgiutils.h"
87
88// a few function prototypes
89static text_t format_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
90 const text_t &link, const text_t &icon,
91 const text_t &text, bool highlight);
92
93static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end,
94 format_t *formatlistptr, text_tset &metadata, bool &getParents);
95
96void metadata_t::clear() {
97 metaname.clear();
98 metacommand = mNone;
99 parentcommand = pNone;
100 parentoptions.clear();
101}
102
103void decision_t::clear() {
104 command = dMeta;
105 meta.clear();
106}
107
108void format_t::clear() {
109 command = comText;
110 decision.clear();
111 text.clear();
112 meta.clear();
113 nextptr = NULL;
114 ifptr = NULL;
115 elseptr = NULL;
116 orptr = NULL;
117}
118
119void formatinfo_t::clear() {
120 DocumentImages = false;
121 DocumentTitles = true;
122 DocumentHeading = "{Or}{[parent(Top):Title],[Title],untitled}<br>";
123 DocumentContents = true;
124 DocumentArrowsBottom = true;
125 DocumentButtons.erase (DocumentButtons.begin(), DocumentButtons.end());
126 DocumentButtons.push_back ("Detach");
127 DocumentButtons.push_back ("Highlight");
128 DocumentButtons.push_back ("Expand Text");
129 DocumentButtons.push_back ("Expand Contents");
130 DocumentText = "[Text]";
131 formatstrings.erase (formatstrings.begin(), formatstrings.end());
132}
133
134// simply checks to see if formatstring begins with a <td> tag
135bool is_table_content (const text_t &formatstring) {
136 text_t::const_iterator here = formatstring.begin();
137 text_t::const_iterator end = formatstring.end();
138
139 while (here != end) {
140 if (*here != ' ') {
141 if (*here == '<') {
142 if ((*(here+1) == 't' || *(here+1) == 'T') &&
143 (*(here+2) == 'd' || *(here+2) == 'D') &&
144 (*(here+3) == '>' || *(here+3) == ' '))
145 return true;
146 } else return false;
147 }
148 here ++;
149 }
150 return false;
151}
152
153bool is_table_content (const format_t *formatlistptr) {
154
155 if (formatlistptr == NULL) return false;
156
157 if (formatlistptr->command == comText)
158 return is_table_content (formatlistptr->text);
159
160 return false;
161}
162
163// returns false if key isn't in formatstringmap
164bool get_formatstring (const text_t &key, const text_tmap &formatstringmap,
165 text_t &formatstring) {
166
167 formatstring.clear();
168 text_tmap::const_iterator it = formatstringmap.find(key);
169 if (it == formatstringmap.end()) return false;
170 formatstring = (*it).second;
171 return true;
172}
173
174// tries to find "key1key2" then "key1" then "key2"
175bool get_formatstring (const text_t &key1, const text_t &key2,
176 const text_tmap &formatstringmap,
177 text_t &formatstring) {
178
179 formatstring.clear();
180 text_tmap::const_iterator it = formatstringmap.find(key1 + key2);
181 if (it != formatstringmap.end()) {
182 formatstring = (*it).second;
183 return true;
184 }
185 it = formatstringmap.find(key1);
186 if (it != formatstringmap.end()) {
187 formatstring = (*it).second;
188 return true;
189 }
190 it = formatstringmap.find(key2);
191 if (it != formatstringmap.end()) {
192 formatstring = (*it).second;
193 return true;
194 }
195 return false;
196}
197
198
199// returns a date of form _textmonthnn_ 31, 1999
200// input is date of type 19991231
201// at least the year must be present in date
202text_t format_date (const text_t &date) {
203
204 if (date.size() < 4) return "";
205
206 text_t::const_iterator datebegin = date.begin();
207
208 text_t year = substr (datebegin, datebegin+4);
209
210 if (date.size() < 6) return year;
211
212 text_t month = "_textmonth" + substr (datebegin+4, datebegin+6) + "_";
213 int imonth = month.getint();
214 if (imonth < 0 || imonth > 12) return year;
215
216 if (date.size() < 8) return month + ", " + year;
217
218 text_t day = substr (datebegin+6, datebegin+8);
219 if (day[0] == '0') day = substr (day.begin()+1, day.end());
220 int iday = day.getint();
221 if (iday < 0 || iday > 31) return month + ", " + year;
222
223 return month + " " + day + ", " + year;
224}
225
226static void get_parent_options (text_t &instring, metadata_t &metaoption) {
227
228 text_t meta, com, op;
229 bool inbraces = false;
230 bool inquotes = false;
231 bool foundcolon = false;
232 text_t::const_iterator here = instring.begin()+6;
233 text_t::const_iterator end = instring.end();
234 while (here != end) {
235 if (*here == '(') inbraces = true;
236 else if (*here == ')') inbraces = false;
237 else if (*here == '\'' && !inquotes) inquotes = true;
238 else if (*here == '\'' && inquotes) inquotes = false;
239 else if (*here == ':' && !inbraces) foundcolon = true;
240 else if (foundcolon) meta.push_back (*here);
241 else if (inquotes) op.push_back (*here);
242 else com.push_back (*here);
243 here ++;
244 }
245 instring = meta;
246 if (com.empty())
247 metaoption.parentcommand = pImmediate;
248 else if (com == "Top")
249 metaoption.parentcommand = pTop;
250 else if (com == "All") {
251 metaoption.parentcommand = pAll;
252 metaoption.parentoptions = op;
253 }
254}
255
256static void parse_meta (text_t &meta, metadata_t &metaoption,
257 text_tset &metadata, bool &getParents) {
258
259 if (meta.size() > 8 && (substr(meta.begin(), meta.begin()+8) == "cgisafe:")) {
260 metaoption.metacommand = mCgiSafe;
261 meta = substr (meta.begin()+8, meta.end());
262 }
263
264 if (meta.size() > 7 && (substr (meta.begin(), meta.begin()+6) == "parent")) {
265 getParents = true;
266 get_parent_options (meta, metaoption);
267 }
268
269 metadata.insert (meta);
270 metaoption.metaname = meta;
271}
272
273static void parse_meta (text_t &meta, format_t *formatlistptr,
274 text_tset &metadata, bool &getParents) {
275
276 if (meta == "link")
277 formatlistptr->command = comLink;
278 else if (meta == "/link")
279 formatlistptr->command = comEndLink;
280
281 else if (meta == "num")
282 formatlistptr->command = comNum;
283
284 else if (meta == "icon")
285 formatlistptr->command = comIcon;
286
287 else if (meta == "Text")
288 formatlistptr->command = comDoc;
289
290 else if (meta == "highlight")
291 formatlistptr->command = comHighlight;
292
293 else if (meta == "/highlight")
294 formatlistptr->command = comEndHighlight;
295
296 else {
297 formatlistptr->command = comMeta;
298 parse_meta (meta, formatlistptr->meta, metadata, getParents);
299 }
300}
301
302static bool parse_string (const text_t &formatstring, format_t *formatlistptr,
303 text_tset &metadata, bool &getParents) {
304
305 text_t text;
306 text_t::const_iterator here = formatstring.begin();
307 text_t::const_iterator end = formatstring.end();
308
309 while (here != end) {
310
311 if (*here == '\\')
312 text.push_back (*(++here));
313
314 else if (*here == '{') {
315 if (!text.empty()) {
316 formatlistptr->command = comText;
317 formatlistptr->text = text;
318 formatlistptr->nextptr = new format_t();
319 formatlistptr = formatlistptr->nextptr;
320
321 text.clear();
322 }
323 if (parse_action (++here, end, formatlistptr, metadata, getParents)) {
324 formatlistptr->nextptr = new format_t();
325 formatlistptr = formatlistptr->nextptr;
326 if (here == end) break;
327 }
328 } else if (*here == '[') {
329 if (!text.empty()) {
330 formatlistptr->command = comText;
331 formatlistptr->text = text;
332 formatlistptr->nextptr = new format_t();
333 formatlistptr = formatlistptr->nextptr;
334
335 text.clear();
336 }
337 text_t meta;
338 here ++;
339 while (*here != ']') {
340 if (here == end) return false;
341 meta.push_back (*here);
342 here ++;
343 }
344 parse_meta (meta, formatlistptr, metadata, getParents);
345 formatlistptr->nextptr = new format_t();
346 formatlistptr = formatlistptr->nextptr;
347
348 } else
349 text.push_back (*here);
350
351 if (here != end) here ++;
352 }
353 if (!text.empty()) {
354 formatlistptr->command = comText;
355 formatlistptr->text = text;
356 formatlistptr->nextptr = new format_t();
357 formatlistptr = formatlistptr->nextptr;
358
359 }
360 return true;
361}
362
363
364static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end,
365 format_t *formatlistptr, text_tset &metadata, bool &getParents) {
366
367 text_t::const_iterator it = findchar (here, end, '}');
368 if (it == end) return false;
369
370 text_t com = substr (here, it);
371 here = findchar (it, end, '{');
372 if (here == end) return false;
373 else here ++;
374
375 if (com == "If") formatlistptr->command = comIf;
376 else if (com == "Or") formatlistptr->command = comOr;
377 else return false;
378
379 int curlycount = 0;
380 int commacount = 0;
381 text_t text;
382 while (here != end) {
383
384 if (*here == '\\') {
385 here++;
386 if (here != end) text.push_back(*here);
387
388 } else if (*here == '{') {curlycount ++; text.push_back(*here);}
389 else if (*here == '}' && curlycount > 0) {
390 curlycount --;
391 text.push_back(*here);
392 }
393
394 else if ((*here == ',' || *here == '}') && curlycount <= 0) {
395
396 if (formatlistptr->command == comOr) {
397 // the {Or}{this, or this, or this, or this} statement
398 // or'ed statements may be either [metadata] or plain text
399 format_t *or_ptr;
400
401 // find the next unused orptr
402 if (formatlistptr->orptr == NULL) {
403 formatlistptr->orptr = new format_t();
404 or_ptr = formatlistptr->orptr;
405 } else {
406 or_ptr = formatlistptr->orptr;
407 while (or_ptr->nextptr != NULL)
408 or_ptr = or_ptr->nextptr;
409 or_ptr->nextptr = new format_t();
410 or_ptr = or_ptr->nextptr;
411 }
412
413 text_t::const_iterator beginbracket = text.begin();
414 text_t::const_iterator endbracket = (text.end() - 1);
415 if ((*beginbracket == '[') && (*endbracket == ']')) {
416 // it's metadata
417 text_t meta = substr (beginbracket+1, endbracket);
418 parse_meta (meta, or_ptr, metadata, getParents);
419
420 } else {
421 // assume it's plain text
422 or_ptr->command = comText;
423 or_ptr->text = text;
424 }
425 text.clear();
426
427 } else {
428 // the {If}{decide,do,else} statement
429 if (commacount == 0) {
430 // If decision only supports metadata at present
431
432 // remove the surrounding square brackets
433 text_t::const_iterator beginbracket = text.begin();
434 text_t::const_iterator endbracket = (text.end() - 1);
435 if ((*beginbracket == '[') && (*endbracket == ']')) {
436 text_t meta = substr (beginbracket+1, endbracket);
437 parse_meta (meta, formatlistptr->decision.meta, metadata, getParents);
438 commacount ++;
439 text.clear();
440 }
441
442 } else if (commacount == 1) {
443 formatlistptr->ifptr = new format_t();
444 parse_string (text, formatlistptr->ifptr, metadata, getParents);
445 commacount ++;
446 text.clear();
447
448 } else if (commacount == 2) {
449 formatlistptr->elseptr = new format_t();
450 parse_string (text, formatlistptr->elseptr, metadata, getParents);
451 commacount ++;
452 text.clear();
453 }
454 }
455 if (*here == '}') break;
456
457 } else text.push_back(*here);
458
459 if (here != end) here ++;
460 }
461
462 return true;
463}
464
465
466bool parse_formatstring (const text_t &formatstring, format_t *formatlistptr,
467 text_tset &metadata, bool &getParents) {
468
469 formatlistptr->clear();
470 getParents = false;
471
472 return (parse_string (formatstring, formatlistptr, metadata, getParents));
473}
474
475
476// note: all the format_date stuff is assuming that all Date metadata is going to
477// be of the form yyyymmdd, this is of course, crap ;)
478
479static text_t get_meta (ResultDocInfo_t &docinfo, const metadata_t &meta) {
480
481 // make sure we have the requested metadata
482 MetadataInfo_tmap::iterator it = docinfo.metadata.find (meta.metaname);
483 if (it == docinfo.metadata.end()) return "";
484
485 MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
486
487 switch (meta.parentcommand) {
488 case pNone:
489 {
490 text_t classifier_metaname = docinfo.classifier_metadata_type;
491 int metaname_index
492 = (classifier_metaname == meta.metaname) ? docinfo.classifier_metadata_offset : 0;
493 text_t metadata_item = docinfo.metadata[meta.metaname].values[metaname_index];
494
495 if (meta.metaname == "Date")
496 return format_date (metadata_item);
497 if (meta.metacommand == mCgiSafe)
498 return cgi_safe (metadata_item);
499 else return metadata_item;
500 }
501
502 case pImmediate:
503 if (parent != NULL) {
504 if (meta.metaname == "Date")
505 return format_date (parent->values[0]);
506 if (meta.metacommand == mCgiSafe)
507 return cgi_safe (parent->values[0]);
508 else return parent->values[0];
509 }
510 break;
511
512 case pTop:
513 if (parent != NULL) {
514 while (parent->parent != NULL) parent = parent->parent;
515
516 if (meta.metaname == "Date")
517 return format_date (parent->values[0]);
518 if (meta.metacommand == mCgiSafe)
519 return cgi_safe (parent->values[0]);
520 else return parent->values[0];
521 }
522 break;
523
524 case pAll:
525 MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
526 if (parent != NULL) {
527 text_tarray tmparray;
528 while (parent != NULL) {
529 tmparray.push_back (parent->values[0]);
530 parent = parent->parent;
531 }
532 bool first = true;
533 text_t tmp;
534 text_tarray::reverse_iterator here = tmparray.rbegin();
535 text_tarray::reverse_iterator end = tmparray.rend();
536 while (here != end) {
537 if (!first) tmp += meta.parentoptions;
538 if (meta.metaname == "Date") tmp += format_date (*here);
539 else tmp += *here;
540 first = false;
541 here ++;
542 }
543 if (meta.metacommand == mCgiSafe) return cgi_safe (tmp);
544 else return tmp;
545 }
546 }
547 return "";
548}
549
550static text_t get_or (ResultDocInfo_t &docinfo, format_t *orptr,
551 const text_t &link, const text_t &icon,
552 const text_t &text, bool highlight) {
553
554 text_t tmp;
555 while (orptr != NULL) {
556
557 tmp = format_string (docinfo, orptr, link, icon, text, highlight);
558 if (!tmp.empty()) return tmp;
559
560 orptr = orptr->nextptr;
561 }
562 return "";
563}
564
565static text_t get_if (ResultDocInfo_t &docinfo, const decision_t &decision,
566 format_t *ifptr, format_t *elseptr, const text_t &link,
567 const text_t &icon, const text_t &text, bool highlight) {
568
569 // not much of a choice yet ...
570 if (decision.command == dMeta) {
571 if (get_meta (docinfo, decision.meta) != "") {
572 if (ifptr != NULL)
573 return get_formatted_string (docinfo, ifptr, link, icon, text, highlight);
574 }
575 else {
576 if (elseptr != NULL)
577 return get_formatted_string (docinfo, elseptr, link, icon, text, highlight);
578 }
579 }
580 return "";
581}
582
583text_t format_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
584 const text_t &link, const text_t &icon,
585 const text_t &text, bool highlight) {
586
587 if (formatlistptr == NULL) return "";
588
589 switch (formatlistptr->command) {
590 case comText:
591 return formatlistptr->text;
592 case comLink:
593 return link;
594 case comEndLink:
595 if (link.empty()) return "";
596 else return "</a>";
597 case comIcon:
598 return icon;
599 case comNum:
600 return docinfo.result_num;
601 case comMeta:
602 return get_meta (docinfo, formatlistptr->meta);
603 case comDoc:
604 return text;
605 case comHighlight:
606 if (highlight) return "<b>";
607 break;
608 case comEndHighlight:
609 if (highlight) return "</b>";
610 break;
611 case comIf:
612 return get_if (docinfo, formatlistptr->decision, formatlistptr->ifptr,
613 formatlistptr->elseptr, link, icon, text, highlight);
614 case comOr:
615 return get_or (docinfo, formatlistptr->orptr, link, icon, text, highlight);
616 }
617 return "";
618}
619
620
621text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
622 const text_t &link, const text_t &icon) {
623
624 text_t text;
625
626 text_t ft;
627 while (formatlistptr != NULL) {
628 ft += format_string (docinfo, formatlistptr, link, icon, text, false);
629 formatlistptr = formatlistptr->nextptr;
630 }
631 return ft;
632}
633
634
635text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr) {
636
637 text_t link = "<a href=\"_httpdocument_&cl=search&d=" + docinfo.OID + "\">";
638 text_t icon = "_icontext_";
639 text_t text;
640
641 text_t ft;
642 while (formatlistptr != NULL) {
643 ft += format_string (docinfo, formatlistptr, link, icon, text, false);
644 formatlistptr = formatlistptr->nextptr;
645 }
646 return ft;
647}
648
649
650text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
651 const text_t &text) {
652
653 text_t link = "<a href=\"_httpdocument_&cl=search&d=" + docinfo.OID + "\">";
654 text_t icon = "_icontext_";
655
656 text_t ft;
657 while (formatlistptr != NULL) {
658 ft += format_string (docinfo, formatlistptr, link, icon, text, false);
659 formatlistptr = formatlistptr->nextptr;
660 }
661 return ft;
662}
663
664
665text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
666 const text_t &link, const text_t &icon, const text_t &text) {
667
668 text_t ft;
669 while (formatlistptr != NULL) {
670 ft += format_string (docinfo, formatlistptr, link, icon, text, false);
671 formatlistptr = formatlistptr->nextptr;
672 }
673 return ft;
674}
675
676text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
677 const text_t &link, const text_t &icon, bool highlight) {
678
679 text_t text, ft;
680 while (formatlistptr != NULL) {
681 ft += format_string (docinfo, formatlistptr, link, icon, text, highlight);
682 formatlistptr = formatlistptr->nextptr;
683 }
684 return ft;
685}
686
687text_t get_formatted_string (ResultDocInfo_t &docinfo, format_t *formatlistptr,
688 const text_t &link, const text_t &icon,
689 const text_t &text, bool highlight) {
690
691 text_t ft;
692 while (formatlistptr != NULL) {
693 ft += format_string (docinfo, formatlistptr, link, icon, text, highlight);
694 formatlistptr = formatlistptr->nextptr;
695 }
696 return ft;
697}
698
699
Note: See TracBrowser for help on using the repository browser.