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

Last change on this file since 19058 was 19058, checked in by davidb, 15 years ago

Fixed bug that causes Local Library Server to crash with an {If} statement with two chars in it (that did not include any 'op').

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