source: gsdl/trunk/runtime-src/src/recpt/formattools.cpp@ 20756

Last change on this file since 20756 was 20756, checked in by mdewsnip, 12 years ago

Fixed memory bug (causing crash on some Windows machines) in format_date(), found by Valgrind.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 52.1 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 *********************************************************************/
25
26#include "formattools.h"
27#include "cgiutils.h"
28#include "recptprototools.h"
29#include "OIDtools.h"
30#include "summarise.h"
31
32#include <assert.h>
33
34static bool metadata_spanwrap = false;
35
36// a few function prototypes
37
38static text_t format_string (const text_t& collection, recptproto* collectproto,
39 ResultDocInfo_t &docinfo, displayclass &disp,
40 format_t *formatlistptr, text_tmap &options,
41 ostream& logout);
42
43static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end,
44 format_t *formatlistptr, text_tset &metadata, bool &getParents);
45
46static text_t format_summary (const text_t& collection, recptproto* collectproto,
47 ResultDocInfo_t &docinfo, displayclass &disp,
48 text_tmap &options, ostream& logout);
49static text_t format_text (const text_t& collection, recptproto* collectproto,
50 ResultDocInfo_t &docinfo, displayclass &disp,
51 text_tmap &options, ostream& logout);
52
53static text_t expand_metadata(const text_t &metavalue, const text_t& collection,
54 recptproto* collectproto, ResultDocInfo_t &docinfo,
55 displayclass &disp, text_tmap &options,
56 ostream &logout);
57
58
59void metadata_t::clear() {
60 metaname.clear();
61 metacommand = mNone;
62 mqualifier.parent = pNone;
63 mqualifier.sibling = sNone;
64 mqualifier.child = cNone;
65 pre_tree_traverse.clear();
66 parentoptions.clear();
67 siblingoptions.clear();
68 childoptions.clear();
69}
70
71void decision_t::clear() {
72 command = dMeta;
73 meta.clear();
74 text.clear();
75}
76
77void format_t::clear() {
78 command = comText;
79 decision.clear();
80 text.clear();
81 meta.clear();
82 nextptr = NULL;
83 ifptr = NULL;
84 elseptr = NULL;
85 orptr = NULL;
86}
87
88void formatinfo_t::clear() {
89 DocumentImages = false;
90 DocumentTitles = true;
91 DocumentHeading = "{Or}{[parent(Top):Title],[Title],untitled}<br>";
92 DocumentContents = true;
93 DocumentArrowsBottom = true;
94 DocumentArrowsTop = false;
95 DocumentSearchResultLinks = false;
96 DocumentButtons.erase (DocumentButtons.begin(), DocumentButtons.end());
97 // DocumentButtons.push_back ("Expand Text");
98 // DocumentButtons.push_back ("Expand Contents");
99 DocumentButtons.push_back ("Detach");
100 DocumentButtons.push_back ("Highlight");
101 RelatedDocuments = "";
102 DocumentText = "[Text]";
103 formatstrings.erase (formatstrings.begin(), formatstrings.end());
104 DocumentUseHTML = false;
105 AllowExtendedOptions = false;
106}
107
108// simply checks to see if formatstring begins with a <td> tag
109bool is_table_content (const text_t &formatstring) {
110 text_t::const_iterator here = formatstring.begin();
111 text_t::const_iterator end = formatstring.end();
112
113 while (here != end) {
114 if (*here != ' ') {
115 if ((*here == '<') && ((here+3) < end)) {
116 if ((*(here+1) == 't' || *(here+1) == 'T') &&
117 (*(here+2) == 'd' || *(here+2) == 'D') &&
118 (*(here+3) == '>' || *(here+3) == ' '))
119 return true;
120 } else return false;
121 }
122 ++here;
123 }
124 return false;
125}
126
127bool is_table_content (const format_t *formatlistptr) {
128
129 if (formatlistptr == NULL) return false;
130
131 if (formatlistptr->command == comText)
132 return is_table_content (formatlistptr->text);
133
134 return false;
135}
136
137// returns false if key isn't in formatstringmap
138bool get_formatstring (const text_t &key, const text_tmap &formatstringmap,
139 text_t &formatstring) {
140
141 formatstring.clear();
142 text_tmap::const_iterator it = formatstringmap.find(key);
143 if (it == formatstringmap.end()) return false;
144 formatstring = (*it).second;
145 return true;
146}
147
148// tries to find "key1key2" then "key1" then "key2"
149bool get_formatstring (const text_t &key1, const text_t &key2,
150 const text_tmap &formatstringmap,
151 text_t &formatstring) {
152
153 formatstring.clear();
154 text_tmap::const_iterator it = formatstringmap.find(key1 + key2);
155 if (it != formatstringmap.end()) {
156 formatstring = (*it).second;
157 return true;
158 }
159 it = formatstringmap.find(key1);
160 if (it != formatstringmap.end()) {
161 formatstring = (*it).second;
162 return true;
163 }
164 it = formatstringmap.find(key2);
165 if (it != formatstringmap.end()) {
166 formatstring = (*it).second;
167 return true;
168 }
169 return false;
170}
171
172
173text_t remove_namespace(const text_t &meta_name) {
174 text_t::const_iterator end = meta_name.end();
175 text_t::const_iterator it = findchar(meta_name.begin(), end, '.');
176 if (it != end) {
177 return substr(it+1, end);
178 }
179
180 return meta_name;
181
182}
183// returns a date of form _format:date_(year, month, day)
184// input is date of type yyyy-?mm-?dd
185// at least the year must be present in date
186text_t format_date (const text_t &date) {
187
188 if (date.size() < 4) return "";
189
190 text_t::const_iterator datebegin = date.begin();
191
192 text_t year = substr (datebegin, datebegin+4);
193 int chars_seen_so_far = 4;
194 if (chars_seen_so_far == date.size()) return "_format:date_("+year+")";
195
196 if (date[chars_seen_so_far] == '-') ++chars_seen_so_far ;
197 if (date.size() < chars_seen_so_far+2) return "_format:date_("+year+")";
198
199 text_t month = substr (datebegin+chars_seen_so_far, datebegin+chars_seen_so_far+2);
200 int imonth = month.getint();
201 if (imonth <= 0 || imonth > 12) return "_format:date_("+year+")";
202
203 chars_seen_so_far += 2;
204 if (chars_seen_so_far == date.size()) return "_format:date_("+year+","+month+")";
205
206 if (date[chars_seen_so_far] == '-') ++chars_seen_so_far ;
207 if (date.size() < chars_seen_so_far+2) return "_format:date_("+year+","+month+")";
208
209 text_t day = substr (datebegin+chars_seen_so_far, datebegin+chars_seen_so_far+2);
210 if (day[0] == '0') day = substr (day.begin()+1, day.end());
211 int iday = day.getint();
212 if (iday <= 0 || iday > 31) return "_format:date_("+year+","+month+")";
213
214 return "_format:date_("+year+","+month+","+day+")";
215}
216
217// converts an iso639 language code to its English equivalent
218// should we be checking that the macro exists??
219text_t iso639 (const text_t &langcode) {
220 if (langcode.empty()) return "";
221 return "_iso639:iso639"+langcode+"_";
222}
223
224
225text_t get_href (const text_t &link) {
226
227 text_t href;
228
229 text_t::const_iterator here = findchar(link.begin(), link.end(), '"');
230 text_t::const_iterator end = link.end();
231 if (here == end) return g_EmptyText;
232
233 ++here;
234 while (here != end) {
235 if (*here == '"') break;
236 href.push_back(*here);
237 ++here;
238 }
239
240 return href;
241}
242
243//this function gets the information associated with the relation
244//metadata for the document associated with 'docinfo'. This relation
245//metadata consists of a line of pairs containing 'collection, document OID'
246//(this is the OID of the document related to the current document, and
247//the collection the related document belongs to). For each of these pairs
248//the title metadata is obtained and then an html link between the title
249//of the related doc and the document's position (the document will be
250//found in "<a href=\"_httpdocument_&c=collection&cl=search&d=OID">
251//(where collection is the related documents collection, and OID is the
252//related documents OID). A list of these html links are made for as many
253//related documents as there are. This list is then returned. If there are
254//no related documents available for the current document then the string
255//'.. no related documents .. ' is returned.
256text_t get_related_docs(const text_t& collection, recptproto* collectproto,
257 ResultDocInfo_t &docinfo, ostream& logout){
258
259 text_tset metadata;
260
261 //insert the metadata we wish to collect
262 metadata.insert("dc.Relation");
263 metadata.insert("Title");
264 metadata.insert("Subject"); //for emails, where title data doesn't apply
265
266 FilterResponse_t response;
267 text_t relation = ""; //string for displaying relation metadata
268 text_t relationTitle = ""; //the related documents Title (or subject)
269 text_t relationOID = ""; //the related documents OID
270
271 //get the information associated with the metadata for current doc
272 if (get_info (docinfo.OID, collection, "", metadata,
273 false, collectproto, response, logout)) {
274
275 //if the relation metadata exists, store for displaying
276 if(!response.docInfo[0].metadata["dc.Relation"].values.empty()){
277 relationOID += response.docInfo[0].metadata["dc.Relation"].values[0];
278
279 //split relation data into pairs of collectionname,ID number
280 text_tarray relationpairs;
281 splitchar (relationOID.begin(), relationOID.end(), ' ', relationpairs);
282
283 text_tarray::const_iterator currDoc = relationpairs.begin();
284 text_tarray::const_iterator lastDoc = relationpairs.end();
285
286 //iterate through the pairs to split and display
287 while(currDoc != lastDoc){
288
289 //split pairs into collectionname and ID
290 text_tarray relationdata;
291 splitchar ((*currDoc).begin(), (*currDoc).end(), ',', relationdata);
292
293 //get first element in the array (collection)
294 text_tarray::const_iterator doc_data = relationdata.begin();
295 text_t document_collection = *doc_data;
296 ++doc_data; //increment to get next item in array (oid)
297 text_t document_OID = *doc_data;
298
299 //create html link to related document
300 relation += "<a href=\"_httpdocument_&c=" + document_collection;
301 relation += "&cl=search&d=" + document_OID;
302
303 //get the information associated with the metadata for related doc
304 if (get_info (document_OID, document_collection, "", metadata,
305 false, collectproto, response, logout)) {
306
307 //if title metadata doesn't exist, collect subject metadata
308 //if that doesn't exist, just call it 'related document'
309 if (!response.docInfo[0].metadata["Title"].values[0].empty())
310 relationTitle = response.docInfo[0].metadata["Title"].values[0];
311 else if (!response.docInfo[0].metadata["Subject"].values.empty())
312 relationTitle = response.docInfo[0].metadata["Subject"].values[0];
313 else relationTitle = "RELATED DOCUMENT";
314
315 }
316
317 //link the related document's title to its page
318 relation += "\">" + relationTitle + "</a>";
319 relation += " (" + document_collection + ")<br>";
320
321 ++currDoc;
322 }
323 }
324
325 }
326
327 if(relation.empty()) //no relation data for documnet
328 relation = ".. no related documents .. ";
329
330 return relation;
331}
332
333
334
335static void get_parent_options (text_t &instring, metadata_t &metaoption) {
336
337 assert (instring.size() > 7);
338 if (instring.size() <= 7) return;
339
340 text_t meta, com, op;
341 bool inbraces = false;
342 bool inquotes = false;
343 bool foundcolon = false;
344 text_t::const_iterator here = instring.begin()+6;
345 text_t::const_iterator end = instring.end();
346 while (here != end) {
347 if (foundcolon) meta.push_back (*here);
348 else if (*here == '(') inbraces = true;
349 else if (*here == ')') inbraces = false;
350 else if (*here == '\'' && !inquotes) inquotes = true;
351 else if (*here == '\'' && inquotes) inquotes = false;
352 else if (*here == ':' && !inbraces) foundcolon = true;
353 else if (inquotes) op.push_back (*here);
354 else com.push_back (*here);
355 ++here;
356 }
357
358 instring = meta;
359 if (com.empty())
360 metaoption.mqualifier.parent = pImmediate;
361 else if (com == "Top")
362 metaoption.mqualifier.parent = pTop;
363 else if (com == "All") {
364 metaoption.mqualifier.parent = pAll;
365 metaoption.parentoptions = op;
366 }
367}
368
369
370static void get_sibling_options (text_t &instring, metadata_t &metaoption) {
371
372 assert (instring.size() > 8);
373 if (instring.size() <= 8) return;
374 text_t meta, com, op;
375 bool inbraces = false;
376 bool inquotes = false;
377 bool foundcolon = false;
378 text_t::const_iterator here = instring.begin()+7;
379 text_t::const_iterator end = instring.end();
380 while (here != end) {
381 if (foundcolon) meta.push_back (*here);
382 else if (*here == '(') inbraces = true;
383 else if (*here == ')') inbraces = false;
384 else if (*here == '\'' && !inquotes) inquotes = true;
385 else if (*here == '\'' && inquotes) inquotes = false;
386 else if (*here == ':' && !inbraces) foundcolon = true;
387 else if (inquotes) op.push_back (*here);
388 else com.push_back (*here);
389 ++here;
390 }
391
392 instring = meta;
393 metaoption.siblingoptions.clear();
394
395 if (com.empty()) {
396 metaoption.mqualifier.sibling = sAll;
397 metaoption.siblingoptions = " ";
398 }
399 else if (com == "first") {
400 metaoption.mqualifier.sibling = sNum;
401 metaoption.siblingoptions = "0";
402 }
403 else if (com == "last") {
404 metaoption.mqualifier.sibling = sNum;
405 metaoption.siblingoptions = "-2"; // == last
406 }
407 else if (com.getint()>0) {
408 metaoption.mqualifier.sibling = sNum;
409 int pos = com.getint()-1;
410 metaoption.siblingoptions +=pos;
411 }
412 else {
413 metaoption.mqualifier.sibling = sAll;
414 metaoption.siblingoptions = op;
415 }
416}
417
418static void get_child_options (text_t &instring, metadata_t &metaoption) {
419
420 assert (instring.size() > 6);
421 if (instring.size() <= 6) return;
422 text_t meta, com, op;
423 bool inbraces = false;
424 bool inquotes = false;
425 bool foundcolon = false;
426 text_t::const_iterator here = instring.begin()+5;
427 text_t::const_iterator end = instring.end();
428 while (here != end) {
429 if (foundcolon) meta.push_back (*here);
430 else if (*here == '(') inbraces = true;
431 else if (*here == ')') inbraces = false;
432 else if (*here == '\'' && !inquotes) inquotes = true;
433 else if (*here == '\'' && inquotes) inquotes = false;
434 else if (*here == ':' && !inbraces) foundcolon = true;
435 else if (inquotes) op.push_back (*here);
436 else com.push_back (*here);
437 ++here;
438 }
439
440 instring = meta;
441 if (com.empty()) {
442 metaoption.mqualifier.child = cAll;
443 metaoption.childoptions = " ";
444 }
445 else if (com == "first") {
446 metaoption.mqualifier.child = cNum;
447 metaoption.childoptions = ".fc";
448 }
449 else if (com == "last") {
450 metaoption.mqualifier.child = cNum;
451 metaoption.childoptions = ".lc";
452 }
453 else if (com.getint()>0) {
454 metaoption.mqualifier.child = cNum;
455 metaoption.childoptions = "."+com;
456 }
457 else {
458 metaoption.mqualifier.child = cAll;
459 metaoption.childoptions = op;
460 }
461}
462
463
464
465static void parse_meta (text_t &meta, metadata_t &metaoption,
466 text_tset &metadata, bool &getParents) {
467
468 if (meta.size() > 8 && (substr(meta.begin(), meta.begin()+8) == "cgisafe:")) {
469 metaoption.metacommand |= mCgiSafe;
470 meta = substr (meta.begin()+8, meta.end());
471 }
472 if (meta.size() > 7 && (substr(meta.begin(), meta.begin()+7) == "format:")) {
473 metaoption.metacommand |= mSpecial;
474 meta = substr (meta.begin()+7, meta.end());
475 }
476
477 bool had_parent_or_child = true;
478 bool prev_was_parent = false;
479 bool prev_was_child = false;
480
481 while (had_parent_or_child) {
482 if (meta.size() > 7
483 && (substr (meta.begin(), meta.begin()+6) == "parent")) {
484
485 // clear out sibling and child (cmd and options)
486 metaoption.metacommand &= ~(mChild|mSibling);
487 metaoption.childoptions.clear();
488 metaoption.siblingoptions.clear();
489
490 getParents = true;
491 metaoption.metacommand |= mParent;
492 get_parent_options (meta, metaoption);
493
494 if (prev_was_parent) {
495 metaoption.pre_tree_traverse += ".pr";
496 }
497 else if (prev_was_child) {
498 metaoption.pre_tree_traverse += ".fc";
499 }
500
501 prev_was_parent = true;
502 prev_was_child = false;
503 }
504 else if (meta.size() > 6
505 && (substr (meta.begin(), meta.begin()+5) == "child")) {
506
507 // clear out sibling and parent (cmd and options)
508 metaoption.metacommand &= ~(mParent|mSibling);
509 metaoption.parentoptions.clear();
510 metaoption.siblingoptions.clear();
511
512 metaoption.metacommand |= mChild;
513 get_child_options (meta, metaoption);
514 metadata.insert("contains");
515
516 if (prev_was_parent) {
517 metaoption.pre_tree_traverse += ".pr";
518 }
519 else if (prev_was_child) {
520 metaoption.pre_tree_traverse += ".fc";
521 }
522
523 prev_was_child = true;
524 prev_was_parent = false;
525 }
526 else {
527 prev_was_child = false;
528 prev_was_parent = false;
529 had_parent_or_child = false;
530 }
531 }
532
533 // parent/child can have sibling tacked on end also
534 if (meta.size() > 8 && (substr (meta.begin(), meta.begin()+7) == "sibling")) {
535 metaoption.metacommand |= mSibling;
536 get_sibling_options (meta, metaoption);
537 }
538
539 // check for ex. which may occur in format statements
540 if (meta.size()>3 && (substr(meta.begin(), meta.begin()+3) == "ex.")) {
541 meta = substr (meta.begin()+3, meta.end());
542 }
543 metadata.insert (meta);
544 metaoption.metaname = meta;
545}
546
547static void parse_coll_meta(text_t &meta, metadata_t &metaoption) {
548 if (meta == "collection") {
549 // no qualifiers
550 metaoption.metaname = g_EmptyText;
551 return;
552 }
553 meta = substr (meta.begin()+11, meta.end());
554 metaoption.metaname = meta;
555
556}
557
558static void parse_meta (text_t &meta, format_t *formatlistptr,
559 text_tset &metadata, bool &getParents) {
560
561 if (meta == "link")
562 formatlistptr->command = comLink;
563 else if (meta == "/link")
564 formatlistptr->command = comEndLink;
565
566 else if (meta == "href")
567 formatlistptr->command = comHref;
568
569 else if (meta == "num")
570 formatlistptr->command = comNum;
571
572 else if (meta == "icon")
573 formatlistptr->command = comIcon;
574
575 else if (meta == "Text")
576 formatlistptr->command = comDoc;
577
578 else if (meta == "RelatedDocuments")
579 formatlistptr->command = comRel;
580
581 else if (meta == "highlight")
582 formatlistptr->command = comHighlight;
583
584 else if (meta == "/highlight")
585 formatlistptr->command = comEndHighlight;
586
587 else if (meta == "metadata-spanwrap")
588 formatlistptr->command = comMetadataSpanWrap;
589
590 else if (meta == "/metadata-spanwrap")
591 formatlistptr->command = comEndMetadataSpanWrap;
592
593 else if (meta == "Summary")
594 formatlistptr->command = comSummary;
595
596 else if (meta == "DocImage")
597 formatlistptr->command = comImage;
598
599 else if (meta == "DocTOC")
600 formatlistptr->command = comTOC;
601
602 else if (meta == "DocumentButtonDetach")
603 formatlistptr->command = comDocumentButtonDetach;
604
605 else if (meta == "DocumentButtonHighlight")
606 formatlistptr->command = comDocumentButtonHighlight;
607
608 else if (meta == "DocumentButtonExpandContents")
609 formatlistptr->command = comDocumentButtonExpandContents;
610
611 else if (meta == "DocumentButtonExpandText")
612 formatlistptr->command = comDocumentButtonExpandText;
613
614 else if (meta == "DocOID")
615 formatlistptr->command = comOID;
616 else if (meta == "DocTopOID")
617 formatlistptr->command = comTopOID;
618 else if (meta == "DocRank")
619 formatlistptr->command = comRank;
620 else if (meta == "DocTermsFreqTotal")
621 formatlistptr->command = comDocTermsFreqTotal;
622 else if (meta.size() >= 10 && (substr(meta.begin(), meta.begin()+10) == "collection")) {
623 formatlistptr->command = comCollection;
624 parse_coll_meta(meta, formatlistptr->meta);
625 }
626 else {
627 formatlistptr->command = comMeta;
628 parse_meta (meta, formatlistptr->meta, metadata, getParents);
629 }
630}
631
632
633static bool parse_string (const text_t &formatstring, format_t *formatlistptr,
634 text_tset &metadata, bool &getParents) {
635
636 text_t text;
637 text_t::const_iterator here = formatstring.begin();
638 text_t::const_iterator end = formatstring.end();
639
640 while (here != end) {
641
642 if (*here == '\\') {
643 ++here;
644 if (here != end) text.push_back (*here);
645
646 } else if (*here == '{') {
647 if (!text.empty()) {
648 formatlistptr->command = comText;
649 formatlistptr->text = text;
650 formatlistptr->nextptr = new format_t();
651 formatlistptr = formatlistptr->nextptr;
652
653 text.clear();
654 }
655 if (parse_action (++here, end, formatlistptr, metadata, getParents)) {
656
657 formatlistptr->nextptr = new format_t();
658 formatlistptr = formatlistptr->nextptr;
659 if (here == end) break;
660 }
661 } else if (*here == '[') {
662 if (!text.empty()) {
663 formatlistptr->command = comText;
664 formatlistptr->text = text;
665 formatlistptr->nextptr = new format_t();
666 formatlistptr = formatlistptr->nextptr;
667
668 text.clear();
669 }
670 text_t meta;
671 ++here;
672 while (*here != ']') {
673 if (here == end) return false;
674 meta.push_back (*here);
675 ++here;
676 }
677 parse_meta (meta, formatlistptr, metadata, getParents);
678 formatlistptr->nextptr = new format_t();
679 formatlistptr = formatlistptr->nextptr;
680
681 } else
682 text.push_back (*here);
683
684 if (here != end) ++here;
685 }
686 if (!text.empty()) {
687 formatlistptr->command = comText;
688 formatlistptr->text = text;
689 formatlistptr->nextptr = new format_t();
690 formatlistptr = formatlistptr->nextptr;
691
692 }
693 return true;
694}
695
696
697static bool parse_action (text_t::const_iterator &here, const text_t::const_iterator &end,
698 format_t *formatlistptr, text_tset &metadata, bool &getParents) {
699
700 text_t::const_iterator it = findchar (here, end, '}');
701 if (it == end) return false;
702
703 text_t com = substr (here, it);
704 here = findchar (it, end, '{');
705 if (here == end) return false;
706 else ++here;
707
708 if (com == "If" || com == "if" || com == "IF") formatlistptr->command = comIf;
709 else if (com == "Or" || com == "or" || com == "OR") formatlistptr->command = comOr;
710 else return false;
711
712 int commacount = 0;
713 text_t text;
714 while (here != end) {
715
716 if (*here == '\\') {
717 ++here;
718 if (here != end) text.push_back(*here);
719
720 }
721
722 else if (*here == ',' || *here == '}' || *here == '{') {
723
724 if (formatlistptr->command == comOr) {
725 // the {Or}{this, or this, or this, or this} statement
726 format_t *or_ptr;
727
728 // find the next unused orptr
729 if (formatlistptr->orptr == NULL) {
730 formatlistptr->orptr = new format_t();
731 or_ptr = formatlistptr->orptr;
732 } else {
733 or_ptr = formatlistptr->orptr;
734 while (or_ptr->nextptr != NULL)
735 or_ptr = or_ptr->nextptr;
736 or_ptr->nextptr = new format_t();
737 or_ptr = or_ptr->nextptr;
738 }
739
740 if (!text.empty())
741 {
742 if (!parse_string(text, or_ptr, metadata, getParents)) { return false; }
743 }
744
745 if (*here == '{')
746 {
747 // Supports: {Or}{[Booktitle],[Title],{If}{[XXXX],aaa,bbb}}
748 // but not : {Or}{[Booktitle],[Title]{If}{[XXXX],aaa,bbb}}
749 // The latter can always be re-written:
750 // {Or}{[Booktitle],{If}{[Title],[Title]{If}{[XXXX],aaa,bbb}}}
751
752 if (!text.empty()) // already used up allocated format_t
753 {
754 // => allocate new one for detected action
755 or_ptr->nextptr = new format_t();
756 or_ptr = or_ptr->nextptr;
757 }
758 if (!parse_action(++here, end, or_ptr, metadata, getParents))
759 {
760 return false;
761 }
762 }
763 else
764 {
765 if (*here == '}') break;
766 }
767 text.clear();
768
769 }
770
771 // Parse an {If}{decide,do,else} statement
772 else {
773
774 // Read the decision component.
775 if (commacount == 0) {
776 // Decsion can be a metadata element, or a piece of text.
777 // Originally Stefan's code, updated 25/10/2000 by Gordon.
778
779 text_t::const_iterator beginbracket = text.begin();
780 text_t::const_iterator endbracket = (text.end() - 1);
781
782 // Decision is based on a metadata element
783 if ((*beginbracket == '[') && (*endbracket == ']')) {
784 // Ignore the surrounding square brackets
785 text_t meta = substr (beginbracket+1, endbracket);
786 parse_meta (meta, formatlistptr->decision.meta, metadata, getParents);
787 ++commacount;
788 text.clear();
789 }
790
791 // Decision is a piece of text (probably a macro like _cgiargmode_).
792 else {
793
794 // hunt for any metadata in string, which might be uses in
795 // to test a condition, e.g. [Format] eq 'PDF'
796 format_t* dummyformat = new format_t();
797 // update which metadata fields needed
798 // (not interested in updatng formatlistptr)
799 parse_string (text, dummyformat, metadata, getParents);
800 delete dummyformat;
801
802 formatlistptr->decision.command = dText;
803 formatlistptr->decision.text = text;
804 ++commacount;
805 text.clear();
806 }
807 }
808
809 // Read the "then" and "else" components of the {If} statement.
810 else {
811 format_t** nextlistptr = NULL;
812 if (commacount == 1) {
813 nextlistptr = &formatlistptr->ifptr;
814 } else if (commacount == 2 ) {
815 nextlistptr = &formatlistptr->elseptr;
816 } else {
817 return false;
818 }
819
820 if (!text.empty()) {
821 if (*nextlistptr == NULL) {
822 *nextlistptr = new format_t();
823 } else {
824
825 // skip to the end of any format_t statements already added
826 while ((*nextlistptr)->nextptr != NULL)
827 {
828 nextlistptr = &(*nextlistptr)->nextptr;
829 }
830
831 (*nextlistptr)->nextptr = new format_t();
832 nextlistptr = &(*nextlistptr)->nextptr;
833 }
834
835 if (!parse_string (text, *nextlistptr, metadata, getParents))
836 {
837 return false;
838 }
839 text.clear();
840 }
841
842 if (*here == '{')
843 {
844 if (*nextlistptr == NULL) {
845 *nextlistptr = new format_t();
846 } else {
847 // skip to the end of any format_t statements already added
848 while ((*nextlistptr)->nextptr != NULL)
849 {
850 nextlistptr = &(*nextlistptr)->nextptr;
851 }
852
853 (*nextlistptr)->nextptr = new format_t();
854 nextlistptr = &(*nextlistptr)->nextptr;
855 }
856
857 if (!parse_action(++here, end, *nextlistptr, metadata, getParents))
858 {
859 return false;
860 }
861 }
862 else
863 {
864 if (*here == '}') break;
865 ++commacount;
866 }
867 }
868 }
869
870 } else text.push_back(*here);
871
872 if (here != end) ++here;
873 }
874
875 return true;
876}
877
878
879static text_t spanwrap_metatext(const text_t& metatext, const text_t& OID,
880 const text_t metaname, int metapos=-1)
881{
882
883 text_t tag_type = (metaname == "Text") ? "div" : "span";
884 text_t editable_type = (metaname == "Text") ? "text" : "metadata";
885
886 text_t wrapped_metatext = "<" + tag_type + " ";
887 wrapped_metatext += "class=\"editable-" + editable_type + "\" ";
888
889 wrapped_metatext += "docoid=\"" + OID + "\" ";
890 wrapped_metatext += "metaname=\"" + metaname + "\"";
891
892 if (metapos>=0) {
893 text_t metapos_str = metapos;
894 wrapped_metatext += " metapos=\"" + metapos_str + "\"";
895 }
896
897 wrapped_metatext += ">" + metatext + "</" + tag_type + ">";
898
899 return wrapped_metatext;
900}
901
902
903
904bool parse_formatstring (const text_t &formatstring, format_t *formatlistptr,
905 text_tset &metadata, bool &getParents) {
906
907 formatlistptr->clear();
908 getParents = false;
909
910 return (parse_string (formatstring, formatlistptr, metadata, getParents));
911}
912
913// position -1 for all, -2 for the last, 0 for the first, or x for a particular piece
914// metainfo has all the values for the metadata, meta has the request metadata name and options, position tells which values to get
915
916static text_t get_formatted_meta_text(const text_t& OID, MetadataInfo_t &metainfo, const metadata_t &meta, int position, bool no_cgisafe = false)
917{
918 text_t no_ns_metaname = remove_namespace(meta.metaname);
919 text_t formatted_metatext;
920 bool first = true;
921
922 const int start_i=0;
923 const int end_i = metainfo.values.size()-1;
924
925 if (position == -1) { // all
926 for (int i=start_i; i<=end_i; ++i) {
927 if (!first) formatted_metatext += meta.siblingoptions;
928
929 text_t fresh_metatext;
930
931 if (meta.metacommand & mSpecial) {
932 // special formatting
933 if (no_ns_metaname == "Date") fresh_metatext = format_date (metainfo.values[i]);
934 else if (no_ns_metaname == "Language") fresh_metatext = iso639(metainfo.values[i]);
935 else fresh_metatext = "_format:"+meta.metaname+"_("+metainfo.values[i]+")";
936 }
937 else fresh_metatext = metainfo.values[i];
938
939 if (metadata_spanwrap) {
940 fresh_metatext = spanwrap_metatext(fresh_metatext,OID,meta.metaname,i);
941 }
942 formatted_metatext += fresh_metatext;
943
944 first = false;
945
946 }
947 } else {
948 if (position == -2) { // end
949 position = end_i;
950 } else if (position < start_i || position > end_i) {
951 return "";
952 }
953
954 text_t fresh_metatext;
955 if (meta.metacommand & mSpecial) {
956
957 // special formatting
958 if (no_ns_metaname == "Date") fresh_metatext = format_date (metainfo.values[position]);
959 else if (no_ns_metaname == "Language") fresh_metatext = iso639(metainfo.values[position]);
960 else fresh_metatext = "_format:"+meta.metaname+"_("+metainfo.values[position]+")";
961 }
962 else fresh_metatext = metainfo.values[position];
963
964 if (metadata_spanwrap) {
965 fresh_metatext = spanwrap_metatext(fresh_metatext,OID,meta.metaname,position);
966 }
967
968 formatted_metatext += fresh_metatext;
969 }
970
971 if (meta.metacommand & mCgiSafe && !no_cgisafe) return cgi_safe_unicode (formatted_metatext);
972 else return formatted_metatext;
973}
974
975static text_t get_parent_meta (ResultDocInfo_t &docinfo, const metadata_t &meta, int siblings_values)
976{
977
978 MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
979
980 switch (meta.mqualifier.parent) {
981 case pNone:
982 return "Nothing!!";
983 break;
984
985 case pImmediate:
986 if (parent != NULL) {
987 text_t parent_oid = get_parent(docinfo.OID);
988 return get_formatted_meta_text(parent_oid,*parent, meta, siblings_values);
989 }
990 break;
991
992 case pTop:
993 if (parent != NULL) {
994 text_t parent_oid = get_parent(docinfo.OID);
995
996 while (parent->parent != NULL) {
997 parent = parent->parent;
998 parent_oid = get_parent(parent_oid);
999 }
1000 return get_formatted_meta_text(parent_oid,*parent, meta, siblings_values);
1001 }
1002 break;
1003
1004 case pAll:
1005 MetadataInfo_t *parent = docinfo.metadata[meta.metaname].parent;
1006 if (parent != NULL) {
1007 text_t parent_oid = get_parent(docinfo.OID);
1008
1009 text_tarray tmparray;
1010 while (parent != NULL) {
1011 tmparray.push_back (get_formatted_meta_text(parent_oid,*parent, meta, siblings_values, true)); // set no_cgisafe to true, as we'll do it once we have all the metadata
1012 parent = parent->parent;
1013 parent_oid = get_parent(parent_oid);
1014
1015 }
1016 // now join them up - use teh parent separator
1017 bool first = true;
1018 text_t tmp;
1019 text_tarray::reverse_iterator here = tmparray.rbegin();
1020 text_tarray::reverse_iterator end = tmparray.rend();
1021 while (here != end) {
1022 if (!first) tmp += meta.parentoptions;
1023 tmp += *here;
1024 first = false;
1025 ++here;
1026 }
1027 if (meta.metacommand & mCgiSafe) return cgi_safe_unicode (tmp);
1028 else return tmp;
1029 }
1030 }
1031 return "";
1032
1033}
1034
1035static text_t get_child_meta (const text_t& collection,
1036 recptproto* collectproto,
1037 ResultDocInfo_t &docinfo, displayclass &disp,
1038 const metadata_t &meta, text_tmap &options,
1039 ostream& logout, int siblings_values)
1040{
1041 if (docinfo.metadata["contains"].values[0].size()==0) return ""; // no children
1042
1043 const text_t& pre_tree_trav = meta.pre_tree_traverse;
1044 const text_t& child_metaname = meta.metaname;
1045 const text_t& child_field = meta.childoptions;
1046 text_tset child_metadata;
1047 child_metadata.insert(child_metaname);
1048
1049 FilterResponse_t child_response;
1050 if (meta.mqualifier.child == cNum) {
1051 // just one child
1052 //get the information associated with the metadata for child doc
1053 if (!get_info (docinfo.OID+pre_tree_trav+child_field, collection, "",
1054 child_metadata, false, collectproto, child_response,
1055 logout)) return ""; // invalid child number
1056
1057 if (child_response.docInfo.empty()) return false; // no info for the child
1058
1059 ResultDocInfo_t& child_docinfo = child_response.docInfo[0];
1060 MetadataInfo_t& metaname_rec = child_docinfo.metadata[child_metaname];
1061
1062 text_t child_metavalue
1063 = get_formatted_meta_text(child_docinfo.OID,metaname_rec,meta,siblings_values);
1064 return expand_metadata(child_metavalue,collection,collectproto,
1065 child_docinfo,disp,options,logout);
1066 }
1067
1068
1069 if (meta.mqualifier.child != cAll) return false; // invalid qualifier
1070
1071
1072 if (!pre_tree_trav.empty()) {
1073 // need to get relevant "contains" metadata for new (e.g. pre tree trav) node
1074 FilterResponse_t trav_response;
1075
1076 text_tset trav_metadata;
1077 trav_metadata.insert("contains");
1078
1079 if (!get_info (docinfo.OID+pre_tree_trav, collection, "",
1080 trav_metadata, false, collectproto, trav_response,
1081 logout)) return ""; // invalid pre_tree_trav
1082
1083 if (trav_response.docInfo.empty()) return false; // no info for the pre_tree_trav OID
1084
1085 ResultDocInfo_t& trav_docinfo = trav_response.docInfo[0];
1086 // use this for rest of routine
1087 docinfo = trav_docinfo;
1088 }
1089
1090 // we need to get all children
1091 text_t result = "";
1092 text_tarray children;
1093 text_t contains = docinfo.metadata["contains"].values[0];
1094 splitchar (contains.begin(), contains.end(), ';', children);
1095 text_tarray::const_iterator here = children.begin();
1096 text_tarray::const_iterator end = children.end();
1097 bool first = true;
1098 while (here !=end) {
1099 text_t oid = *here;
1100 here++;
1101 if (*(oid.begin()) == '"') translate_parent (oid, docinfo.OID);
1102
1103 //get the information associated with the metadata for child doc
1104 if (!get_info (oid, collection, "", child_metadata,
1105 false, collectproto, child_response, logout) ||
1106 child_response.docInfo.empty()) {
1107 first = false;
1108 continue;
1109 }
1110
1111
1112 ResultDocInfo_t& child_docinfo = child_response.docInfo[0];
1113 MetadataInfo_t& metaname_rec = child_docinfo.metadata[child_metaname];
1114
1115 text_t child_metavalue
1116 = get_formatted_meta_text(child_docinfo.OID,metaname_rec,meta,siblings_values);
1117 if (!first) result += child_field;
1118 first = false;
1119 // need to do this here cos otherwise we are in the wrong document
1120 result += expand_metadata(child_metavalue,collection,collectproto,
1121 child_docinfo,disp,options,logout);
1122 }
1123 return result;
1124
1125}
1126
1127static text_t get_meta (const text_t& collection, recptproto* collectproto,
1128 ResultDocInfo_t &docinfo, displayclass &disp,
1129 const metadata_t &meta, text_tmap &options,
1130 ostream& logout) {
1131
1132 // make sure we have the requested metadata
1133 MetadataInfo_tmap::iterator it = docinfo.metadata.find (meta.metaname);
1134 if (it == docinfo.metadata.end()) return "";
1135
1136 int siblings_values = 0; // default is no siblings, just the first metadata available
1137 if (meta.metacommand & mSibling) {
1138 if (meta.mqualifier.sibling == sAll) {
1139 siblings_values = -1; //all
1140 } else if (meta.mqualifier.sibling == sNum) {
1141 siblings_values = meta.siblingoptions.getint();
1142 }
1143 }
1144 if (meta.metacommand & mParent) {
1145 return get_parent_meta(docinfo,meta,siblings_values);
1146 }
1147
1148 else if (meta.metacommand & mChild) {
1149 return get_child_meta(collection,collectproto,docinfo,disp,meta,
1150 options,logout, siblings_values);
1151 }
1152 else if (meta.metacommand & mSibling) { // only siblings
1153 MetadataInfo_t& metaname_rec = docinfo.metadata[meta.metaname];
1154 return get_formatted_meta_text(docinfo.OID,docinfo.metadata[meta.metaname],meta, siblings_values);
1155 }
1156 else {
1157
1158 // straightforward metadata request (nothing fancy)
1159
1160 text_t classifier_metaname = docinfo.classifier_metadata_type;
1161 int metaname_index
1162 = (classifier_metaname == meta.metaname) ? docinfo.classifier_metadata_offset : 0;
1163 return get_formatted_meta_text(docinfo.OID,docinfo.metadata[meta.metaname], meta, metaname_index);
1164 }
1165
1166 return "";
1167}
1168
1169static text_t get_or (const text_t& collection, recptproto* collectproto,
1170 ResultDocInfo_t &docinfo, displayclass &disp,
1171 format_t *orptr, text_tmap &options,
1172 ostream& logout) {
1173
1174 while (orptr != NULL) {
1175
1176 if (metadata_spanwrap) {
1177 // need to be a bit more careful about this
1178 // => test for it *without* spanwrap, and if defined, then
1179 // got back and generate it again, this time with spanwrap on
1180
1181 metadata_spanwrap = false;
1182 text_t test_tmp = format_string (collection,collectproto,docinfo, disp, orptr,
1183 options, logout);
1184 metadata_spanwrap = true;
1185 if (!test_tmp.empty()) {
1186
1187 return format_string (collection,collectproto,docinfo, disp, orptr,
1188 options, logout);
1189 }
1190 }
1191 else {
1192 text_t tmp = format_string (collection,collectproto,docinfo, disp, orptr,
1193 options, logout);
1194 if (!tmp.empty()) return tmp;
1195 }
1196
1197 orptr = orptr->nextptr;
1198 }
1199 return "";
1200}
1201
1202static bool char_is_whitespace(const char c)
1203{
1204 return ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r'));
1205
1206}
1207
1208static int scan_over_whitespace(const text_t& outstring, const int start_pos)
1209{
1210 int pos = start_pos;
1211 while (pos<outstring.size()) {
1212 if (!char_is_whitespace(outstring[pos])) {
1213 break;
1214 }
1215 ++pos;
1216 }
1217
1218 return pos;
1219}
1220
1221static int rscan_over_whitespace(const text_t& outstring, const int start_pos)
1222{
1223 int pos = start_pos;
1224 while (pos>=0) {
1225 if (!char_is_whitespace(outstring[pos])) {
1226 break;
1227 }
1228 --pos;
1229 }
1230
1231 return pos;
1232}
1233
1234static int rscan_for_whitespace(const text_t& outstring, const int start_pos)
1235{
1236 int pos = start_pos;
1237 while (pos>=0) {
1238 if (char_is_whitespace(outstring[pos])) {
1239 break;
1240 }
1241 --pos;
1242 }
1243
1244 return pos;
1245}
1246
1247
1248static int rscan_for(const text_t& outstring, const int start_pos,
1249 const char find_c)
1250{
1251 int pos = start_pos;
1252 while (pos>=0) {
1253 char c = outstring[pos];
1254 if (outstring[pos] == find_c) {
1255 break;
1256 }
1257 --pos;
1258 }
1259
1260 return pos;
1261}
1262
1263text_t extract_substr(const text_t& outstring, const int start_pos,
1264 const int end_pos)
1265{
1266 text_t extracted_str;
1267 extracted_str.clear();
1268
1269 for (int pos=start_pos; pos<=end_pos; ++pos) {
1270 extracted_str.push_back(outstring[pos]);
1271 }
1272
1273 return extracted_str;
1274}
1275
1276
1277static text_t expand_potential_metadata(const text_t& collection,
1278 recptproto* collectproto,
1279 ResultDocInfo_t &docinfo,
1280 displayclass &disp,
1281 const text_t& intext,
1282 text_tmap &options,
1283 ostream& logout)
1284{
1285 text_t outtext;
1286
1287 // decide if dealing with metadata or text
1288
1289 text_t::const_iterator beginbracket = intext.begin();
1290 text_t::const_iterator endbracket = (intext.end() - 1);
1291
1292 // Decision is based on a metadata element
1293 if ((*beginbracket == '[') && (*endbracket == ']')) {
1294 // Ignore the surrounding square brackets
1295 text_t meta_text = substr (beginbracket+1, endbracket);
1296
1297 if (meta_text == "Text") {
1298 outtext = format_text(collection, collectproto, docinfo, disp, options, logout);
1299 }
1300 else {
1301
1302 text_tset metadata;
1303 bool getParents =false;
1304 metadata_t meta;
1305
1306 parse_meta (meta_text, meta, metadata, getParents);
1307 outtext
1308 = get_meta (collection,collectproto,docinfo,disp,meta,options,logout);
1309 }
1310
1311 }
1312 else {
1313 outtext = intext;
1314 }
1315
1316 return outtext;
1317}
1318
1319
1320
1321
1322static bool uses_expression(const text_t& collection, recptproto* collectproto,
1323 ResultDocInfo_t &docinfo,
1324 displayclass &disp,
1325 const text_t& outstring, text_t& lhs_expr,
1326 text_t& op_expr, text_t& rhs_expr,
1327 text_tmap &options,
1328 ostream& logout)
1329{
1330 // Note: the string may not be of the form: str1 op str2, however
1331 // to deterine this we have to process it on the assumption it is,
1332 // and if at any point an 'erroneous' value is encountered, return
1333 // false and let something else have a go at evaluating it
1334
1335 // Starting at the end of the string and working backwards ..
1336
1337 const int outstring_len = outstring.size();
1338
1339 // skip over white space
1340 int rhs_end = rscan_over_whitespace(outstring,outstring_len-1);
1341
1342 if (rhs_end<=0) {
1343 // no meaningful text or (rhs_end==0) no room for operator
1344 return false;
1345 }
1346
1347 // check for ' or " and then scan over token
1348 const char potential_quote = outstring[rhs_end];
1349 int rhs_start=rhs_end;
1350 bool quoted = false;
1351
1352 if ((potential_quote == '\'') || (potential_quote == '\"')) {
1353 --rhs_end;
1354 rhs_start = rscan_for(outstring,rhs_end-1,potential_quote) +1;
1355 quoted = true;
1356 }
1357 else {
1358 rhs_start = rscan_for_whitespace(outstring,rhs_end-1) +1;
1359 }
1360
1361 if ((rhs_end-rhs_start)<0) {
1362 // no meaningful rhs expression
1363 return false;
1364 }
1365
1366 // form rhs_expr
1367 rhs_expr = extract_substr(outstring,rhs_start,rhs_end);
1368
1369 // skip over white space
1370 const int to_whitespace = (quoted) ? 2 : 1;
1371
1372 int op_end = rscan_over_whitespace(outstring,rhs_start-to_whitespace);
1373 int op_start = rscan_for_whitespace(outstring,op_end-1)+1;
1374
1375 if ((op_end<0) && (op_start<0)) {
1376 // no meaningful expression operator
1377 return false;
1378 }
1379
1380 if (op_end-op_start<0) {
1381 // no meaningful expression operator
1382 return false;
1383 }
1384
1385 op_expr = extract_substr(outstring,op_start,op_end);
1386
1387
1388 // check for operator
1389 if ((op_expr != "eq") && (op_expr != "ne") && (op_expr != "gt") &&
1390 (op_expr != "ge") && (op_expr != "lt") && (op_expr != "le") && (op_expr != "==") && (op_expr != "!=") && (op_expr != ">") && (op_expr != ">=") && (op_expr != "<") && (op_expr != "<=") && (op_expr != "sw") && (op_expr != "ew")) {
1391
1392 // not a valid operator
1393 return false;
1394 }
1395
1396 int lhs_end = rscan_over_whitespace(outstring,op_start-1);
1397 if (lhs_end<0) {
1398 // no meaningful lhs expression
1399 return false;
1400 }
1401
1402 int lhs_start = scan_over_whitespace(outstring,0);
1403
1404 // form lhs_expr from remainder of string
1405 lhs_expr = extract_substr(outstring,lhs_start,lhs_end);
1406
1407 // Now we know we have a valid expression, look up any
1408 // metadata terms
1409
1410 rhs_expr = expand_potential_metadata(collection,collectproto,docinfo,
1411 disp,rhs_expr,options,logout);
1412 lhs_expr = expand_potential_metadata(collection,collectproto,docinfo,
1413 disp,lhs_expr,options,logout);
1414
1415 return true;
1416}
1417
1418static bool eval_expression_true(const text_t& lhs_expr,const text_t& op_expr,
1419 const text_t& rhs_expr, ostream& logout)
1420{
1421 if (op_expr == "eq") return (lhs_expr == rhs_expr);
1422 else if (op_expr == "ne" ) return (lhs_expr != rhs_expr);
1423 else if (op_expr == "gt") return (lhs_expr > rhs_expr);
1424 else if (op_expr == "ge") return (lhs_expr >= rhs_expr);
1425 else if (op_expr == "lt") return (lhs_expr < rhs_expr);
1426 else if (op_expr == "le") return (lhs_expr <= rhs_expr);
1427 else if (op_expr == "==") return (lhs_expr.getint() == rhs_expr.getint());
1428 else if (op_expr == "!=") return (lhs_expr.getint() != rhs_expr.getint());
1429 else if (op_expr == ">") return (lhs_expr.getint() > rhs_expr.getint());
1430 else if (op_expr == ">=") return (lhs_expr.getint() >= rhs_expr.getint());
1431 else if (op_expr == "<") return (lhs_expr.getint() < rhs_expr.getint());
1432 else if (op_expr == "<=") return (lhs_expr.getint() <= rhs_expr.getint());
1433 else if (op_expr == "sw") return (starts_with(lhs_expr,rhs_expr));
1434 else if (op_expr == "ew") return (ends_with(lhs_expr,rhs_expr));
1435 else {
1436 logout << "Error: '" << op_expr << "' is not a recognised operator." << endl;
1437 }
1438
1439 return false;
1440}
1441
1442
1443static text_t get_if (const text_t& collection, recptproto* collectproto,
1444 ResultDocInfo_t &docinfo, displayclass &disp,
1445 const decision_t &decision,
1446 format_t *ifptr, format_t *elseptr,
1447 text_tmap &options, ostream& logout)
1448{
1449 // If the decision component is a metadata element, then evaluate it
1450 // to see whether we output the "then" or the "else" clause
1451 if (decision.command == dMeta) {
1452
1453 bool store_metadata_spanwrap = metadata_spanwrap;
1454 metadata_spanwrap = 0;
1455
1456 // temporarily suspend metadata-spanwrap (if on) so can test if metadata item really exits or not
1457 bool metadata_exists
1458 = (get_meta (collection,collectproto,docinfo,disp,decision.meta,options,
1459 logout) != "");
1460
1461 metadata_spanwrap = store_metadata_spanwrap;
1462
1463 if (metadata_exists) {
1464 if (ifptr != NULL)
1465 return get_formatted_string (collection,collectproto,docinfo, disp, ifptr,
1466 options, logout);
1467 }
1468 else {
1469 if (elseptr != NULL)
1470 return get_formatted_string (collection,collectproto,docinfo, disp, elseptr,
1471 options, logout);
1472 }
1473 }
1474
1475 // If the decision component is text, then evaluate it (it is probably a
1476 // macro like _cgiargmode_) to decide what to output.
1477 else if (decision.command == dText) {
1478
1479 text_t outstring;
1480 disp.expandstring (decision.text, outstring);
1481
1482 // Check for if expression in form: str1 op str2
1483 // (such as [x] eq "y")
1484 text_t lhs_expr, op_expr, rhs_expr;
1485 if (uses_expression(collection,collectproto,docinfo, disp, outstring,lhs_expr,op_expr,rhs_expr, options,logout)) {
1486 if (eval_expression_true(lhs_expr,op_expr,rhs_expr,logout)) {
1487 if (ifptr != NULL) {
1488 return get_formatted_string (collection, collectproto, docinfo, disp, ifptr,
1489 options, logout);
1490 }
1491 else {
1492 return "";
1493 }
1494 } else {
1495 if (elseptr != NULL) {
1496 return get_formatted_string (collection, collectproto, docinfo, disp, elseptr,
1497 options, logout);
1498 }
1499 else {
1500 return "";
1501 }
1502 }
1503 }
1504
1505
1506 // This is a tad tricky. When we expand a string like _cgiargmode_, that is
1507 // a cgi argument macro that has not been set, it evaluates to itself.
1508 // Therefore, were have to say that a piece of text evalautes true if
1509 // it is non-empty and if it is a cgi argument evaulating to itself.
1510
1511 if ((outstring != "") && !((outstring == decision.text) && (outstring[0] == '_'))) {
1512 if (ifptr != NULL)
1513 return get_formatted_string (collection, collectproto, docinfo, disp, ifptr,
1514 options, logout);
1515 } else {
1516 if (elseptr != NULL)
1517 return get_formatted_string (collection, collectproto, docinfo, disp, elseptr,
1518 options, logout);
1519 }
1520 }
1521
1522 return "";
1523}
1524
1525bool includes_metadata(const text_t& text)
1526{
1527 text_t::const_iterator here = text.begin();
1528 text_t::const_iterator end = text.end();
1529 while (here != end) {
1530 if (*here == '[') return true;
1531 ++here;
1532 }
1533
1534 return false;
1535}
1536
1537static text_t expand_metadata(const text_t &metavalue, const text_t& collection,
1538 recptproto* collectproto,
1539 ResultDocInfo_t &docinfo,
1540 displayclass &disp, text_tmap &options,
1541 ostream &logout) {
1542
1543 if (includes_metadata(metavalue)) {
1544
1545 // text has embedded metadata in it => expand it
1546 FilterRequest_t request;
1547 FilterResponse_t response;
1548
1549 request.getParents = false;
1550
1551 format_t *expanded_formatlistptr = new format_t();
1552 parse_formatstring (metavalue, expanded_formatlistptr,
1553 request.fields, request.getParents);
1554
1555 // retrieve metadata
1556 get_info(docinfo.OID, collection, "", request.fields, request.getParents,
1557 collectproto, response, logout);
1558
1559 if (!response.docInfo.empty()) {
1560
1561 text_t expanded_metavalue
1562 = get_formatted_string(collection, collectproto,
1563 response.docInfo[0], disp, expanded_formatlistptr,
1564 options, logout);
1565
1566 return expanded_metavalue;
1567 }
1568 else {
1569 return metavalue;
1570 }
1571 }
1572 else {
1573
1574 return metavalue;
1575 }
1576}
1577
1578text_t get_collection_meta(const text_t& collection, recptproto* collectproto,
1579 displayclass &disp,
1580 text_t meta_name, ostream& logout) {
1581
1582 ColInfoResponse_t collectinfo;
1583 comerror_t err;
1584 collectproto->get_collectinfo (collection, collectinfo,err,logout);
1585 text_t meta_value = "";
1586 text_t lang;
1587 disp.expandstring("_cgiargl_",lang);
1588 if (lang.empty()) {
1589 lang = "en";
1590 }
1591
1592 if (err == noError) {
1593 meta_value = collectinfo.get_collectionmeta(meta_name, lang);
1594 }
1595 return meta_value;
1596
1597
1598}
1599text_t format_string (const text_t& collection, recptproto* collectproto,
1600 ResultDocInfo_t &docinfo, displayclass &disp,
1601 format_t *formatlistptr, text_tmap &options,
1602 ostream& logout) {
1603
1604 if (formatlistptr == NULL) return "";
1605
1606 switch (formatlistptr->command) {
1607 case comOID:
1608 return docinfo.OID;
1609 case comTopOID:
1610 {
1611 text_t top_id;
1612 get_top(docinfo.OID, top_id);
1613 return top_id;
1614 }
1615 case comRank:
1616 return text_t(docinfo.ranking);
1617 case comText:
1618 return formatlistptr->text;
1619 case comLink:
1620 return options["link"];
1621 case comEndLink:
1622 if (options["link"].empty()) return "";
1623 else return "</a>";
1624 case comHref:
1625 return get_href(options["link"]);
1626 case comIcon:
1627 return options["icon"];
1628 case comNum:
1629 return docinfo.result_num;
1630 case comRel: //if [RelatedDocuments] appears in format string, collect relation data
1631 return get_related_docs(collection, collectproto, docinfo, logout);
1632
1633 case comSummary:
1634 return format_summary(collection, collectproto, docinfo, disp, options, logout);
1635
1636 case comMeta:
1637 {
1638 const text_t& metavalue = get_meta (collection,collectproto, docinfo, disp,formatlistptr->meta,options,logout);
1639 return expand_metadata(metavalue, collection, collectproto, docinfo, disp, options, logout);
1640 }
1641
1642 case comDoc:
1643 return format_text(collection, collectproto, docinfo, disp, options, logout);
1644
1645 case comImage:
1646 return expand_metadata(options["DocImage"], collection, collectproto, docinfo, disp, options, logout);
1647 case comTOC:
1648 return options["DocTOC"];
1649 case comDocumentButtonDetach:
1650 return options["DocumentButtonDetach"];
1651 case comDocumentButtonHighlight:
1652 return options["DocumentButtonHighlight"];
1653 case comDocumentButtonExpandContents:
1654 return options["DocumentButtonExpandContents"];
1655 case comDocumentButtonExpandText:
1656 return options["DocumentButtonExpandText"];
1657 case comHighlight:
1658 if (options["highlight"] == "1") return "<b>";
1659 break;
1660 case comEndHighlight:
1661 if (options["highlight"] == "1") return "</b>";
1662 break;
1663 case comMetadataSpanWrap:
1664 metadata_spanwrap=true; return "";
1665 break;
1666 case comEndMetadataSpanWrap:
1667 metadata_spanwrap=false; return "";
1668 break;
1669 case comIf:
1670 return get_if (collection, collectproto, docinfo, disp,
1671 formatlistptr->decision, formatlistptr->ifptr,
1672 formatlistptr->elseptr, options, logout);
1673 case comOr:
1674 return get_or (collection,collectproto, docinfo, disp, formatlistptr->orptr,
1675 options, logout);
1676 case comDocTermsFreqTotal:
1677 return docinfo.num_terms_matched;
1678 case comCollection:
1679 if (formatlistptr->meta.metaname == g_EmptyText) {
1680 return collection;
1681 }
1682 return get_collection_meta(collection, collectproto, disp, formatlistptr->meta.metaname, logout);
1683
1684 }
1685 return "";
1686}
1687
1688text_t get_formatted_string (const text_t& collection, recptproto* collectproto,
1689 ResultDocInfo_t &docinfo, displayclass &disp,
1690 format_t *formatlistptr, text_tmap &options,
1691 ostream& logout) {
1692
1693 text_t ft;
1694 while (formatlistptr != NULL)
1695 {
1696 ft += format_string (collection, collectproto, docinfo, disp, formatlistptr,
1697 options, logout);
1698 formatlistptr = formatlistptr->nextptr;
1699 }
1700
1701 return ft;
1702}
1703
1704
1705// we have only preloaded the text in DocumentAction. But you may want to get the text in query. so copy what we have done with format_summary and get the text here. probably is quite expensive?
1706text_t format_text (const text_t& collection, recptproto* collectproto,
1707 ResultDocInfo_t &docinfo, displayclass &disp,
1708 text_tmap &options, ostream& logout)
1709{
1710 text_t text;
1711
1712 if(!options["text"].empty()) {
1713 text = options["text"];
1714 }
1715 else {
1716 // get document text here
1717 DocumentRequest_t docrequest;
1718 DocumentResponse_t docresponse;
1719 comerror_t err;
1720 docrequest.OID = docinfo.OID;
1721 collectproto->get_document (collection, docrequest, docresponse, err, logout);
1722 text = docresponse.doc;
1723 }
1724
1725 if (metadata_spanwrap) {
1726 text = spanwrap_metatext(text,docinfo.OID,"Text");
1727 }
1728
1729 return text;
1730}
1731
1732/* FUNCTION NAME: format_summary
1733 * DESC: this is invoked when a [Summary] special metadata is processed.
1734 * RETURNS: a query-biased summary for the document */
1735
1736text_t format_summary (const text_t& collection, recptproto* collectproto,
1737 ResultDocInfo_t &docinfo, displayclass &disp,
1738 text_tmap &options, ostream& logout) {
1739
1740 // GRB: added code here to ensure that the cstr (and other collections)
1741 // uses the document metadata item Summary, rather than compressing
1742 // the text of the document, processed via the methods in
1743 // summarise.cpp
1744
1745 text_t summary;
1746
1747 if (docinfo.metadata.count("Summary") > 0 &&
1748 docinfo.metadata["Summary"].values.size() > 0) {
1749 summary = docinfo.metadata["Summary"].values[0];
1750 }
1751 else {
1752
1753 text_t textToSummarise, query;
1754
1755 if(options["text"].empty()) { // get document text
1756 DocumentRequest_t docrequest;
1757 DocumentResponse_t docresponse;
1758 comerror_t err;
1759 docrequest.OID = docinfo.OID;
1760 collectproto->get_document (collection, docrequest, docresponse, err, logout);
1761 textToSummarise = docresponse.doc;
1762 }
1763 else {
1764 // in practice, this would not happen, because text is only
1765 // loaded with the [Text] command
1766 textToSummarise = options["text"];
1767 }
1768
1769 disp.expandstring("_cgiargq_",query);
1770 summary = summarise(textToSummarise,query,80);
1771 }
1772
1773 if (metadata_spanwrap) {
1774 summary = spanwrap_metatext(summary,docinfo.OID,"Summary");
1775 }
1776
1777 return summary;
1778}
Note: See TracBrowser for help on using the repository browser.