]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
7251649f5a8bee6b07d1612ab6d8e108c50a8592
[lyx.git] / src / BiblioInfo.cpp
1 /**
2  * \file BiblioInfo.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Herbert Voß
8  * \author Richard Heck
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "BiblioInfo.h"
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "buffer_funcs.h"
19 #include "InsetIterator.h"
20 #include "Paragraph.h"
21
22 #include "insets/Inset.h"
23 #include "insets/InsetBibitem.h"
24 #include "insets/InsetBibtex.h"
25 #include "insets/InsetInclude.h"
26
27 #include "support/docstream.h"
28 #include "support/gettext.h"
29 #include "support/lassert.h"
30 #include "support/lstrings.h"
31 #include "support/textutils.h"
32
33 #include "boost/regex.hpp"
34
35 using namespace std;
36 using namespace lyx::support;
37
38
39 namespace lyx {
40
41 //////////////////////////////////////////////////////////////////////
42 //
43 // BibTeXInfo
44 //
45 //////////////////////////////////////////////////////////////////////
46
47 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
48         : is_bibtex_(true), bib_key_(key), entry_type_(type)
49 {}
50
51
52 bool BibTeXInfo::hasField(docstring const & field) const
53 {
54         return count(field) == 1;
55 }
56
57
58 docstring const & BibTeXInfo::operator[](docstring const & field) const
59 {
60         BibTeXInfo::const_iterator it = find(field);
61         if (it != end())
62                 return it->second;
63         static docstring const empty_value = docstring();
64         return empty_value;
65 }
66         
67         
68 docstring const & BibTeXInfo::operator[](string const & field) const
69 {
70         return operator[](from_ascii(field));
71 }
72
73
74 docstring familyName(docstring const & name)
75 {
76         if (name.empty())
77                 return docstring();
78
79         // first we look for a comma, and take the last name to be everything
80         // preceding the right-most one, so that we also get the "jr" part.
81         docstring::size_type idx = name.rfind(',');
82         if (idx != docstring::npos)
83                 return ltrim(name.substr(0, idx));
84
85         // OK, so now we want to look for the last name. We're going to
86         // include the "von" part. This isn't perfect.
87         // Split on spaces, to get various tokens.
88         vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
89         // If we only get two, assume the last one is the last name
90         if (pieces.size() <= 2)
91                 return pieces.back();
92
93         // Now we look for the first token that begins with a lower case letter.
94         vector<docstring>::const_iterator it = pieces.begin();
95         vector<docstring>::const_iterator en = pieces.end();
96         for (; it != en; ++it) {
97                 if ((*it).size() == 0)
98                         continue;
99                 char_type const c = (*it)[0];
100                 if (isLower(c))
101                         break;
102         }
103
104         if (it == en) // we never found a "von"
105                 return pieces.back();
106
107         // reconstruct what we need to return
108         docstring retval;
109         bool first = true;
110         for (; it != en; ++it) {
111                 if (!first)
112                         retval += " ";
113                 else 
114                         first = false;
115                 retval += *it;
116         }
117         return retval;
118 }
119
120 docstring const BibTeXInfo::getAbbreviatedAuthor() const
121 {
122         if (!is_bibtex_) {
123                 docstring const opt = trim(operator[]("label"));
124                 if (opt.empty())
125                         return docstring();
126
127                 docstring authors;
128                 split(opt, authors, '(');
129                 return authors;
130         }
131
132         docstring author = operator[]("author");
133         if (author.empty()) {
134                 author = operator[]("editor");
135                 if (author.empty())
136                         return bib_key_;
137         }
138
139         // OK, we've got some names. Let's format them.
140         // Try to split the author list on " and "
141         vector<docstring> const authors =
142                 getVectorFromString(author, from_ascii(" and "));
143
144         if (authors.size() == 2)
145                 return bformat(_("%1$s and %2$s"),
146                         familyName(authors[0]), familyName(authors[1]));
147
148         if (authors.size() > 2)
149                 return bformat(_("%1$s et al."), familyName(authors[0]));
150
151         return familyName(authors[0]);
152 }
153
154
155 docstring const BibTeXInfo::getYear() const
156 {
157         if (!is_bibtex_) {
158                 docstring const opt = trim(operator[]("label"));
159                 if (opt.empty())
160                         return docstring();
161
162                 docstring authors;
163                 docstring const tmp = split(opt, authors, '(');
164                 docstring year;
165                 split(tmp, year, ')');
166                 return year;
167         }
168
169         docstring year = operator[]("year");
170         if (year.empty())
171                 year = _("No year");
172         return year;
173 }
174
175
176 docstring const BibTeXInfo::getInfo() const
177 {
178         if (!info_.empty())
179                 return info_;
180
181         if (!is_bibtex_) {
182                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
183                 info_ = it->second;
184                 return info_;
185         }
186  
187         // FIXME
188         // This could be made a lot better using the entryType
189         // field to customize the output based upon entry type.
190         
191         // Search for all possible "required" fields
192         docstring author = operator[]("author");
193         if (author.empty())
194                 author = operator[]("editor");
195  
196         docstring year   = operator[]("year");
197         docstring title  = operator[]("title");
198         docstring docLoc = operator[]("pages");
199         if (docLoc.empty()) {
200                 docLoc = operator[]("chapter");
201                 if (!docLoc.empty())
202                         docLoc = from_ascii("Ch. ") + docLoc;
203         }       else {
204                 docLoc = from_ascii("pp. ") + docLoc;
205         }
206
207         docstring media = operator[]("journal");
208         if (media.empty()) {
209                 media = operator[]("publisher");
210                 if (media.empty()) {
211                         media = operator[]("school");
212                         if (media.empty())
213                                 media = operator[]("institution");
214                 }
215         }
216         docstring volume = operator[]("volume");
217
218         odocstringstream result;
219         if (!author.empty())
220                 result << author << ", ";
221         if (!title.empty())
222                 result << title;
223         if (!media.empty())
224                 result << ", " << media;
225         if (!year.empty())
226                 result << ", " << year;
227         if (!docLoc.empty())
228                 result << ", " << docLoc;
229
230         docstring const result_str = rtrim(result.str());
231         if (!result_str.empty()) {
232                 info_ = result_str;
233                 return info_;
234         }
235
236         // This should never happen (or at least be very unusual!)
237         return docstring();
238 }
239
240
241 //////////////////////////////////////////////////////////////////////
242 //
243 // BiblioInfo
244 //
245 //////////////////////////////////////////////////////////////////////
246
247 namespace {
248 // A functor for use with sort, leading to case insensitive sorting
249         class compareNoCase: public binary_function<docstring, docstring, bool>
250         {
251                 public:
252                         bool operator()(docstring const & s1, docstring const & s2) const {
253                                 return compare_no_case(s1, s2) < 0;
254                         }
255         };
256 } // namespace anon
257
258
259 vector<docstring> const BiblioInfo::getKeys() const
260 {
261         vector<docstring> bibkeys;
262         BiblioInfo::const_iterator it  = begin();
263         for (; it != end(); ++it)
264                 bibkeys.push_back(it->first);
265         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
266         return bibkeys;
267 }
268
269
270 vector<docstring> const BiblioInfo::getFields() const
271 {
272         vector<docstring> bibfields;
273         set<docstring>::const_iterator it = field_names_.begin();
274         set<docstring>::const_iterator end = field_names_.end();
275         for (; it != end; ++it)
276                 bibfields.push_back(*it);
277         sort(bibfields.begin(), bibfields.end());
278         return bibfields;
279 }
280
281
282 vector<docstring> const BiblioInfo::getEntries() const
283 {
284         vector<docstring> bibentries;
285         set<docstring>::const_iterator it = entry_types_.begin();
286         set<docstring>::const_iterator end = entry_types_.end();
287         for (; it != end; ++it)
288                 bibentries.push_back(*it);
289         sort(bibentries.begin(), bibentries.end());
290         return bibentries;
291 }
292
293
294 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
295 {
296         BiblioInfo::const_iterator it = find(key);
297         if (it == end())
298                 return docstring();
299         BibTeXInfo const & data = it->second;
300         return data.getAbbreviatedAuthor();
301 }
302
303
304 docstring const BiblioInfo::getYear(docstring const & key) const
305 {
306         BiblioInfo::const_iterator it = find(key);
307         if (it == end())
308                 return docstring();
309         BibTeXInfo const & data = it->second;
310         return data.getYear();
311 }
312
313
314 docstring const BiblioInfo::getInfo(docstring const & key) const
315 {
316         BiblioInfo::const_iterator it = find(key);
317         if (it == end())
318                 return docstring();
319         BibTeXInfo const & data = it->second;
320         return data.getInfo();
321 }
322
323
324 vector<docstring> const BiblioInfo::getCiteStrings(
325         docstring const & key, Buffer const & buf) const
326 {
327         CiteEngine const engine = buf.params().citeEngine();
328         if (engine == ENGINE_BASIC || engine == ENGINE_NATBIB_NUMERICAL)
329                 return getNumericalStrings(key, buf);
330         else
331                 return getAuthorYearStrings(key, buf);
332 }
333
334
335 vector<docstring> const BiblioInfo::getNumericalStrings(
336         docstring const & key, Buffer const & buf) const
337 {
338         if (empty())
339                 return vector<docstring>();
340
341         docstring const author = getAbbreviatedAuthor(key);
342         docstring const year   = getYear(key);
343         if (author.empty() || year.empty())
344                 return vector<docstring>();
345
346         vector<CiteStyle> const & styles = citeStyles(buf.params().citeEngine());
347         
348         vector<docstring> vec(styles.size());
349         for (size_t i = 0; i != vec.size(); ++i) {
350                 docstring str;
351
352                 switch (styles[i]) {
353                         case CITE:
354                         case CITEP:
355                                 str = from_ascii("[#ID]");
356                                 break;
357
358                         case NOCITE:
359                                 str = _("Add to bibliography only.");
360                                 break;
361
362                         case CITET:
363                                 str = author + " [#ID]";
364                                 break;
365
366                         case CITEALT:
367                                 str = author + " #ID";
368                                 break;
369
370                         case CITEALP:
371                                 str = from_ascii("#ID");
372                                 break;
373
374                         case CITEAUTHOR:
375                                 str = author;
376                                 break;
377
378                         case CITEYEAR:
379                                 str = year;
380                                 break;
381
382                         case CITEYEARPAR:
383                                 str = '(' + year + ')';
384                                 break;
385                 }
386
387                 vec[i] = str;
388         }
389
390         return vec;
391 }
392
393
394 vector<docstring> const BiblioInfo::getAuthorYearStrings(
395         docstring const & key, Buffer const & buf) const
396 {
397         if (empty())
398                 return vector<docstring>();
399
400         docstring const author = getAbbreviatedAuthor(key);
401         docstring const year   = getYear(key);
402         if (author.empty() || year.empty())
403                 return vector<docstring>();
404
405         vector<CiteStyle> const & styles = citeStyles(buf.params().citeEngine());
406         
407         vector<docstring> vec(styles.size());
408         for (size_t i = 0; i != vec.size(); ++i) {
409                 docstring str;
410
411                 switch (styles[i]) {
412                         case CITE:
413                 // jurabib only: Author/Annotator
414                 // (i.e. the "before" field, 2nd opt arg)
415                                 str = author + "/<" + _("before") + '>';
416                                 break;
417
418                         case NOCITE:
419                                 str = _("Add to bibliography only.");
420                                 break;
421
422                         case CITET:
423                                 str = author + " (" + year + ')';
424                                 break;
425
426                         case CITEP:
427                                 str = '(' + author + ", " + year + ')';
428                                 break;
429
430                         case CITEALT:
431                                 str = author + ' ' + year ;
432                                 break;
433
434                         case CITEALP:
435                                 str = author + ", " + year ;
436                                 break;
437
438                         case CITEAUTHOR:
439                                 str = author;
440                                 break;
441
442                         case CITEYEAR:
443                                 str = year;
444                                 break;
445
446                         case CITEYEARPAR:
447                                 str = '(' + year + ')';
448                                 break;
449                 }
450                 vec[i] = str;
451         }
452         return vec;
453 }
454
455
456 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
457 {
458         bimap_.insert(info.begin(), info.end());
459 }
460
461
462 //////////////////////////////////////////////////////////////////////
463 //
464 // CitationStyle
465 //
466 //////////////////////////////////////////////////////////////////////
467
468 namespace {
469
470
471 char const * const citeCommands[] = {
472         "cite", "citet", "citep", "citealt", "citealp",
473         "citeauthor", "citeyear", "citeyearpar", "nocite" };
474
475 unsigned int const nCiteCommands =
476                 sizeof(citeCommands) / sizeof(char *);
477
478 CiteStyle const citeStylesArray[] = {
479         CITE, CITET, CITEP, CITEALT, CITEALP, 
480         CITEAUTHOR, CITEYEAR, CITEYEARPAR, NOCITE };
481
482 unsigned int const nCiteStyles =
483                 sizeof(citeStylesArray) / sizeof(CiteStyle);
484
485 CiteStyle const citeStylesFull[] = {
486         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
487
488 unsigned int const nCiteStylesFull =
489                 sizeof(citeStylesFull) / sizeof(CiteStyle);
490
491 CiteStyle const citeStylesUCase[] = {
492         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
493
494 unsigned int const nCiteStylesUCase =
495         sizeof(citeStylesUCase) / sizeof(CiteStyle);
496
497 } // namespace anon
498
499
500 CitationStyle citationStyleFromString(string const & command)
501 {
502         CitationStyle s;
503         if (command.empty())
504                 return s;
505
506         string cmd = command;
507         if (cmd[0] == 'C') {
508                 s.forceUpperCase = true;
509                 cmd[0] = 'c';
510         }
511
512         size_t const n = cmd.size() - 1;
513         if (cmd != "cite" && cmd[n] == '*') {
514                 s.full = true;
515                 cmd = cmd.substr(0, n);
516         }
517
518         char const * const * const last = citeCommands + nCiteCommands;
519         char const * const * const ptr = find(citeCommands, last, cmd);
520
521         if (ptr != last) {
522                 size_t idx = ptr - citeCommands;
523                 s.style = citeStylesArray[idx];
524         }
525         return s;
526 }
527
528
529 string citationStyleToString(const CitationStyle & s)
530 {
531         string cite = citeCommands[s.style];
532         if (s.full) {
533                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
534                 if (find(citeStylesFull, last, s.style) != last)
535                         cite += '*';
536         }
537
538         if (s.forceUpperCase) {
539                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
540                 if (find(citeStylesUCase, last, s.style) != last)
541                         cite[0] = 'C';
542         }
543
544         return cite;
545 }
546
547 vector<CiteStyle> citeStyles(CiteEngine engine)
548 {
549         unsigned int nStyles = 0;
550         unsigned int start = 0;
551
552         switch (engine) {
553                 case ENGINE_BASIC:
554                         nStyles = 2;
555                         start = 0;
556                         break;
557                 case ENGINE_NATBIB_AUTHORYEAR:
558                 case ENGINE_NATBIB_NUMERICAL:
559                         nStyles = nCiteStyles - 1;
560                         start = 1;
561                         break;
562                 case ENGINE_JURABIB:
563                         nStyles = nCiteStyles;
564                         start = 0;
565                         break;
566         }
567
568         vector<CiteStyle> styles(nStyles);
569         size_t i = 0;
570         int j = start;
571         for (; i != styles.size(); ++i, ++j)
572                 styles[i] = citeStylesArray[j];
573
574         return styles;
575 }
576
577 } // namespace lyx
578