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

Last change on this file since 15418 was 15418, checked in by mdewsnip, 16 years ago

(Untangling colservr/recpt) Split recpt/OIDtools into two: lib/OIDtools.cpp/h contains the purely string-based functions (may be used by the colservr), and recpt/recptprototools.cpp/h contains the functions requiring a call to the colservr (get_info() etc.).

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