]> git.lyx.org Git - lyx.git/blob - src/frontends/controllers/biblio.C
small things
[lyx.git] / src / frontends / controllers / biblio.C
1 /* This file is part of
2  * ====================================================== 
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 2001 The LyX Team.
7  *
8  * ======================================================
9  *
10  * \file biblio.C
11  * \author Angus Leeming <a.leeming@ic.ac.uk>
12  */
13
14 #include <config.h>
15
16 #include <vector>
17 #include <algorithm>
18
19 #ifdef __GNUG__
20 #pragma implementation
21 #endif
22
23 #include "LString.h"
24 #include "biblio.h"
25 #include "gettext.h" // for _()
26 #include "helper_funcs.h"
27 #include "support/lstrings.h"
28 #include "support/LAssert.h"
29 #include "support/LRegex.h"
30
31 using std::find;
32 using std::min;
33 using std::vector;
34 using std::sort;
35
36 namespace biblio 
37 {
38
39 namespace {
40
41 using namespace biblio;
42     
43 char const * const citeCommands[] = {
44         "cite", "citet", "citep", "citealt", "citealp", "citeauthor", 
45         "citeyear", "citeyearpar" };
46
47 unsigned int const nCiteCommands =
48         sizeof(citeCommands) / sizeof(char *);
49
50 CiteStyle const citeStyles[] = {
51         CITE, CITET, CITEP, CITEALT, CITEALP,
52         CITEAUTHOR, CITEYEAR, CITEYEARPAR };
53
54 unsigned int const nCiteStyles =
55         sizeof(citeStyles) / sizeof(CiteStyle);
56
57 CiteStyle const citeStylesFull[] = {
58         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
59
60 unsigned int const nCiteStylesFull =
61         sizeof(citeStylesFull) / sizeof(CiteStyle);
62
63 CiteStyle const citeStylesUCase[] = {
64         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
65
66 unsigned int const nCiteStylesUCase =
67         sizeof(citeStylesUCase) / sizeof(CiteStyle);
68  
69
70 // The functions doing the dirty work for the search.
71 vector<string>::const_iterator
72 simpleSearch(InfoMap const & theMap,
73              vector<string> const & keys,
74              string const & expr,
75              vector<string>::const_iterator start,
76              Direction dir,
77              bool caseSensitive)
78 {
79         string tmp = expr;
80         if (!caseSensitive)
81                 tmp = lowercase(tmp);
82
83         vector<string> searchwords = getVectorFromString(tmp, " ");
84
85         // Loop over all keys from start...
86         for (vector<string>::const_iterator it = start;
87              // End condition is direction-dependent.
88              (dir == FORWARD) ? (it<keys.end()) : (it>=keys.begin());
89              // increment is direction-dependent.
90              (dir == FORWARD) ? (++it) : (--it)) {
91
92                 string data = (*it);
93                 InfoMap::const_iterator info = theMap.find(*it);
94                 if (info != theMap.end())
95                         data += " " + info->second;
96                 if (!caseSensitive)
97                         data = lowercase(data);
98
99                 bool found = true;
100
101                 // Loop over all search words...
102                 for (vector<string>::const_iterator sit = searchwords.begin();
103                      sit != searchwords.end(); ++sit) {
104                         if (data.find(*sit) == string::npos) {
105                                 found = false;
106                                 break;
107                         }
108                 }
109                 
110                 if (found) return it;
111         }
112
113         return keys.end();
114 }
115
116  
117 vector<string>::const_iterator
118 regexSearch(InfoMap const & theMap,
119             vector<string> const & keys,
120             string const & expr,
121             vector<string>::const_iterator start,
122             Direction dir)
123 {
124         LRegex reg(expr);
125
126         for (vector<string>::const_iterator it = start;
127              // End condition is direction-dependent.
128              (dir == FORWARD) ? (it<keys.end()) : (it>=keys.begin());
129              // increment is direction-dependent.
130              (dir == FORWARD) ? (++it) : (--it)) {
131
132                 string data = (*it);
133                 InfoMap::const_iterator info = theMap.find(*it);
134                 if (info != theMap.end())
135                         data += " " + info->second;
136
137                 if (reg.exec(data).size() > 0)
138                         return it;
139         }
140
141         return keys.end();
142 }
143
144 string const familyName(string const & name)
145 {
146         // Very simple parser
147         string fname = name;
148
149         string::size_type idx = fname.rfind(".");
150         if (idx != string::npos)
151                 fname = frontStrip(fname.substr(idx+1));
152
153         return fname;
154 }
155
156
157 string const getAbbreviatedAuthor(InfoMap const & map, string const & key)
158 {
159         lyx::Assert(!map.empty());
160
161         InfoMap::const_iterator it = map.find(key);
162
163         string author;
164         if (it != map.end()) {
165                 author = parseBibTeX(it->second, "author");
166                 if (author.empty())
167                         author = parseBibTeX(it->second, "editor");
168
169                 vector<string> authors = getVectorFromString(author, "and");
170
171                 if (!authors.empty()) {
172                         author.erase();
173
174                         for (vector<string>::iterator it = authors.begin();
175                              it != authors.end(); ++it) {
176                                 *it = familyName(strip(*it));
177                         }
178
179                         author = authors[0];
180                         if (authors.size() == 2)
181                                 author += _(" and ") + authors[1];
182                         else if (authors.size() > 2)
183                                 author += _(" et al.");
184                 }
185         }
186
187         if (author.empty())
188                 author = _("Caesar et al.");
189
190         return author;
191 }
192
193
194 string const getYear(InfoMap const & map, string const & key)
195 {
196         lyx::Assert(!map.empty());
197
198         InfoMap::const_iterator it = map.find(key);
199
200         string year;
201
202         if (it != map.end())
203                 year = parseBibTeX(it->second, "year");
204
205         if (year.empty())
206                 year = "50BC";
207
208         return year;
209 }
210
211 } // namespace anon 
212
213
214
215
216
217
218
219 // A functor for use with std::sort, leading to case insensitive sorting
220 struct compareNoCase: public std::binary_function<string, string, bool> 
221 {
222         bool operator()(string const & s1, string const & s2) const {
223                 return compare_no_case(s1, s2) < 0;
224         }
225 };
226
227 vector<string> const getKeys(InfoMap const & map)
228 {
229         vector<string> bibkeys;
230
231         for (InfoMap::const_iterator it = map.begin(); it != map.end(); ++it) {
232                 bibkeys.push_back(it->first);
233         }
234
235         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
236         return bibkeys;
237 }
238
239
240 string const getInfo(InfoMap const & map, string const & key)
241 {
242         lyx::Assert(!map.empty());
243
244         InfoMap::const_iterator it = map.find(key);
245         if (it == map.end()) return string();
246
247         // Search for all possible "required" keys
248         string author = parseBibTeX(it->second, "author");
249         if (author.empty())
250                 author = parseBibTeX(it->second, "editor");
251
252         string year       = parseBibTeX(it->second, "year");
253         string title      = parseBibTeX(it->second, "title");
254         string booktitle  = parseBibTeX(it->second, "booktitle");
255         string chapter    = parseBibTeX(it->second, "chapter");
256         string pages      = parseBibTeX(it->second, "pages");
257
258         string media      = parseBibTeX(it->second, "journal");
259         if (media.empty())
260                 media = parseBibTeX(it->second, "publisher");
261         if (media.empty())
262                 media = parseBibTeX(it->second, "school");
263         if (media.empty())
264                 media = parseBibTeX(it->second, "institution");
265
266         ostringstream result;
267         result << author;
268         if (!year.empty())
269                 result << ", " << year;
270         if (!title.empty())
271                 result << ", " << title;
272         if (!booktitle.empty())
273                 result << ", in " << booktitle;
274         if (!chapter.empty())
275                 result << ", Ch. " << chapter;
276         if (!media.empty())
277                 result << ", " << media;
278         if (!pages.empty())
279                 result << ", pp. " << pages;
280
281         if (result.str().empty()) // not a BibTeX record
282                 result << it->second;
283
284         return result.str().c_str();
285 }
286  
287
288 vector<string>::const_iterator
289 searchKeys(InfoMap const & theMap,
290            vector<string> const & keys,
291            string const & expr,
292            vector<string>::const_iterator start,
293            Search type,
294            Direction dir,
295            bool caseSensitive)
296 {
297         // Preliminary checks
298         if(start < keys.begin() || start >= keys.end())
299                 return keys.end();
300         
301         string search_expr = frontStrip(strip(expr));
302         if (search_expr.empty())
303                 return keys.end();
304
305         if (type == SIMPLE)
306                 return simpleSearch(theMap, keys, search_expr, start, dir,
307                                     caseSensitive);
308
309         return regexSearch(theMap, keys, search_expr, start, dir);
310 }
311
312
313 string const parseBibTeX(string data, string const & findkey)
314 {
315         string keyvalue;
316         
317         for (string::iterator it=data.begin(); it<data.end(); ++it) {
318                 if ((*it) == '\n' || (*it) == '\t')
319                         (*it)= ' ';
320         }
321         
322         data = frontStrip(data);
323         while (!data.empty() && data[0] != '=' && 
324                (data.find(' ') != string::npos || data.find('=') != string::npos)) {
325
326                 string::size_type keypos = min(data.find(' '), data.find('='));
327                 string key = lowercase(data.substr(0, keypos));
328       
329                 data = data.substr(keypos, data.length()-1);
330                 data = frontStrip(strip(data));
331                 if (data.length() > 1 && data[0]=='=') {
332                         data = frontStrip(data.substr(1, data.length()-1));
333                         if (!data.empty()) {
334                                 keypos = 1;
335                                 string value;
336                                 char enclosing;
337
338                                 if (data[0]=='{') {
339                                         enclosing = '}';
340                                 } else if (data[0]=='"') {
341                                         enclosing = '"';
342                                 } else {
343                                         keypos=0;
344                                         enclosing=' ';
345                                 }
346
347                                 if (keypos &&
348                                     data.find(enclosing)!=string::npos &&
349                                     data.length()>1) {
350                                         string tmp = data.substr(keypos,
351                                                                  data.length()-1);
352                                         while (tmp.find('{') != string::npos &&
353                                                tmp.find('}') != string::npos &&
354                                                tmp.find('{') < tmp.find('}') &&
355                                                tmp.find('{') < tmp.find(enclosing)) {
356
357                                                 keypos += tmp.find('{')+1;
358                                                 tmp = data.substr(keypos,
359                                                                   data.length()-1);
360                                                 keypos += tmp.find('}')+1;
361                                                 tmp = data.substr(keypos,
362                                                                   data.length()-1);
363                                         }
364
365                                         if (tmp.find(enclosing)==string::npos)
366                                                 return keyvalue;
367                                         else {
368                                                 keypos += tmp.find(enclosing);
369                                                 tmp = data.substr(keypos,
370                                                                   data.length()-1);
371                                         }
372
373                                         value = data.substr(1, keypos-1);
374
375                                         if (keypos+1<data.length()-1)
376                                                 data = frontStrip(data.substr(keypos+1, data.length()-1));
377                                         else
378                                                 data = "";
379
380                                 } else if (!keypos &&
381                                            (data.find(' ') ||
382                                             data.find(','))) {
383                                         keypos = data.length()-1;
384                                         if (data.find(' ') != string::npos)
385                                                 keypos = data.find(' ');
386                                         if (data.find(',') != string::npos &&
387                                             keypos > data.find(','))
388                                                 keypos = data.find(',');
389
390                                         value = data.substr(0, keypos);
391                   
392                                         if (keypos+1<data.length()-1)
393                                                 data = frontStrip(data.substr(keypos+1, data.length()-1));
394                                         else
395                                                 data = "";
396                                 }
397                                 else
398                                         return keyvalue;
399
400                                 if (findkey == key) {
401                                         keyvalue = value;
402                                         return keyvalue;
403                                 } 
404
405                                 data = frontStrip(frontStrip(data,','));
406                         }
407                 }
408                 else return keyvalue;
409         }
410         return keyvalue;
411 }
412
413
414 CitationStyle const getCitationStyle(string const & command)
415 {
416         if (command.empty()) return CitationStyle();
417     
418         CitationStyle cs;
419         string cmd = command;
420
421         if (cmd[0] == 'C') {
422                 cs.forceUCase = true;
423                 cmd[0] = 'c';
424         }
425
426         size_t n = cmd.size()-1;
427         if (cmd[n] == '*') {
428                 cs.full = true;
429                 cmd = cmd.substr(0,n);
430         }
431
432         char const * const * const last = citeCommands + nCiteCommands;
433         char const * const * const ptr = std::find(citeCommands, last, cmd);
434
435         if (ptr != last) {
436                 size_t idx = ptr - citeCommands;
437                 cs.style = citeStyles[idx];
438         }
439
440         return cs;
441 }
442
443
444 string const getCiteCommand(CiteStyle command, bool full, bool forceUCase)
445 {
446         string cite = citeCommands[command];
447         if (full) {
448                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
449                 if (std::find(citeStylesFull, last, command) != last)
450                         cite += "*";
451         }
452
453         if (forceUCase) {
454                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
455                 if (std::find(citeStylesUCase, last, command) != last)
456                         cite[0] = 'C';
457         }
458
459         return cite;
460 }
461
462         
463 vector<CiteStyle> const getCiteStyles(bool usingNatbib)
464 {
465         unsigned int nStyles = 1;
466         unsigned int start = 0;
467         if (usingNatbib) {
468                 nStyles = nCiteStyles - 1;
469                 start = 1;
470         }
471
472         vector<CiteStyle> styles(nStyles);
473
474         vector<CiteStyle>::size_type i = 0;
475         int j = start;
476         for (; i != styles.size(); ++i, ++j) {
477                 styles[i] = citeStyles[j];
478         }
479
480         return styles;
481 }
482
483
484 vector<string> const
485 getNumericalStrings(string const & key,
486                     InfoMap const & map, vector<CiteStyle> const & styles)
487 {
488         if (map.empty()) {
489                 vector<string> vec(1);
490                 vec[0] = _("No database");
491                 return vec;
492         }
493         
494         vector<string> vec(styles.size());
495
496         string const author = getAbbreviatedAuthor(map, key);
497         string const year   = getYear(map, key);
498         
499         for (vector<string>::size_type i = 0; i != vec.size(); ++i) {
500                 string str;
501
502                 switch (styles[i]) {
503                 case CITE:
504                 case CITEP:
505                         str = "[#ID]";
506                         break;
507                         
508                 case CITET:
509                         str = author + " [#ID]";
510                         break;
511                         
512                 case CITEALT:
513                         str = author + " #ID";
514                         break;
515                         
516                 case CITEALP:
517                         str = "#ID";
518                         break;
519                         
520                 case CITEAUTHOR:
521                         str = author;
522                         break;
523                         
524                 case CITEYEAR:
525                         str = year;
526                         break;
527                         
528                 case CITEYEARPAR:
529                         str = "(" + year + ")";
530                         break;
531                 }
532
533                 vec[i] = str;
534         }
535         
536         return vec;
537 }
538
539
540 vector<string> const
541 getAuthorYearStrings(string const & key,
542                     InfoMap const & map, vector<CiteStyle> const & styles)
543 {
544         if (map.empty()) {
545                 vector<string> vec(1);
546                 vec[0] = _("No database");
547                 return vec;
548         }
549         
550         vector<string> vec(styles.size());
551
552         string const author = getAbbreviatedAuthor(map, key);
553         string const year   = getYear(map, key);
554         
555         for (vector<string>::size_type i = 0; i != vec.size(); ++i) {
556                 string str;
557
558                 switch (styles[i]) {
559                 case CITET:
560                         str = author + " (" + year + ")";
561                         break;
562                         
563                 case CITE:
564                 case CITEP:
565                         str = "(" + author + ", " + year + ")";
566                         break;
567                         
568                 case CITEALT:
569                         str = author + " " + year ;
570                         break;
571                         
572                 case CITEALP:
573                         str = author + ", " + year ;
574                         break;
575                         
576                 case CITEAUTHOR:
577                         str = author;
578                         break;
579                         
580                 case CITEYEAR:
581                         str = year;
582                         break;
583                         
584                 case CITEYEARPAR:
585                         str = "(" + year + ")";
586                         break;
587                 }
588
589                 vec[i] = str;
590         }
591         
592         return vec;
593 }
594
595 } // namespace biblio