]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
Move debug.{cpp,h}, Messages.{cpp,h} and gettext.{cpp,h} to support/.
[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/lstrings.h"
30
31 #include "boost/regex.hpp"
32
33 using std::string;
34 using std::vector;
35 using std::pair;
36 using std::endl;
37 using std::set;
38
39 namespace lyx {
40
41 using support::bformat;
42 using support::compare_no_case;
43 using support::getVectorFromString;
44 using support::ltrim;
45 using support::rtrim;
46
47 //////////////////////////////////////////////////////////////////////
48 //
49 // BibTeXInfo
50 //
51 //////////////////////////////////////////////////////////////////////
52
53 BibTeXInfo::BibTeXInfo()
54         : isBibTeX(true)
55 {}
56
57         
58 bool BibTeXInfo::hasField(docstring const & field) const
59 {
60         return count(field) == 1;
61 }
62
63
64 docstring const & BibTeXInfo::getValueForField(docstring const & field) const
65 {
66         BibTeXInfo::const_iterator it = find(field);
67         if (it != end())
68                 return it->second;
69         static docstring const empty_value = docstring();
70         return empty_value;
71 }
72         
73         
74 docstring const & BibTeXInfo::getValueForField(string const & field) const
75 {
76         return getValueForField(from_ascii(field));
77 }
78
79
80 static docstring familyName(docstring const & name)
81 {
82         if (name.empty())
83                 return docstring();
84
85         // Very simple parser
86         docstring fname = name;
87
88         // possible authorname combinations are:
89         // "Surname, FirstName"
90         // "Surname, F."
91         // "FirstName Surname"
92         // "F. Surname"
93         docstring::size_type idx = fname.find(',');
94         if (idx != docstring::npos)
95                 return ltrim(fname.substr(0, idx));
96         idx = fname.rfind('.');
97         if (idx != docstring::npos && idx + 1 < fname.size())
98                 fname = ltrim(fname.substr(idx + 1));
99         // test if we have a LaTeX Space in front
100         if (fname[0] == '\\')
101                 return fname.substr(2);
102         return rtrim(fname);
103 }
104
105
106 docstring const BibTeXInfo::getAbbreviatedAuthor() const
107 {
108         if (!isBibTeX) 
109                 return docstring();
110  
111         docstring author = getValueForField("author");
112  
113         if (author.empty()) {
114                 author = getValueForField("editor");
115                 if (author.empty())
116                         return bibKey;
117         }
118
119         // OK, we've got some names. Let's format them.
120         // Try to split the author list on " and "
121         vector<docstring> const authors =
122                 getVectorFromString(author, from_ascii(" and "));
123         
124         if (authors.size() == 2)
125                 return bformat(_("%1$s and %2$s"),
126                         familyName(authors[0]), familyName(authors[1]));
127
128         if (authors.size() > 2)
129                 return bformat(_("%1$s et al."), familyName(authors[0]));
130
131         return familyName(authors[0]);
132 }
133
134
135 docstring const BibTeXInfo::getYear() const
136 {
137         if (!isBibTeX) 
138                 return docstring();
139  
140         docstring year = getValueForField("year");
141         if (year.empty())
142                 year = _("No year");
143         return year;
144 }
145
146
147 docstring const BibTeXInfo::getInfo() const
148 {
149         if (!isBibTeX) {
150                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
151                 return it->second;
152         }
153  
154         // FIXME
155         // This could be made a lot better using the entryType
156         // field to customize the output based upon entry type.
157         
158         // Search for all possible "required" fields
159         docstring author = getValueForField("author");
160         if (author.empty())
161                 author = getValueForField("editor");
162  
163         docstring year      = getValueForField("year");
164         docstring title     = getValueForField("title");
165         docstring docLoc    = getValueForField("pages");
166         if (docLoc.empty()) {
167                 docLoc = getValueForField("chapter");
168                 if (!docLoc.empty())
169                         docLoc = from_ascii("Ch. ") + docLoc;
170         }       else {
171                 docLoc = from_ascii("pp. ") + docLoc;
172         }
173
174         docstring media = getValueForField("journal");
175         if (media.empty()) {
176                 media = getValueForField("publisher");
177                 if (media.empty()) {
178                         media = getValueForField("school");
179                         if (media.empty())
180                                 media = getValueForField("institution");
181                 }
182         }
183         docstring volume = getValueForField("volume");
184
185         odocstringstream result;
186         if (!author.empty())
187                 result << author << ", ";
188         if (!title.empty())
189                 result << title;
190         if (!media.empty())
191                 result << ", " << media;
192         if (!year.empty())
193                 result << ", " << year;
194         if (!docLoc.empty())
195                 result << ", " << docLoc;
196
197         docstring const result_str = rtrim(result.str());
198         if (!result_str.empty())
199                 return result_str;
200
201         // This should never happen (or at least be very unusual!)
202         return docstring();
203 }
204
205
206 //////////////////////////////////////////////////////////////////////
207 //
208 // BiblioInfo
209 //
210 //////////////////////////////////////////////////////////////////////
211
212 namespace {
213 // A functor for use with std::sort, leading to case insensitive sorting
214         class compareNoCase: public std::binary_function<docstring, docstring, bool>
215         {
216                 public:
217                         bool operator()(docstring const & s1, docstring const & s2) const {
218                                 return compare_no_case(s1, s2) < 0;
219                         }
220         };
221 } // namespace anon
222
223
224 vector<docstring> const BiblioInfo::getKeys() const
225 {
226         vector<docstring> bibkeys;
227         BiblioInfo::const_iterator it  = begin();
228         for (; it != end(); ++it)
229                 bibkeys.push_back(it->first);
230         std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
231         return bibkeys;
232 }
233
234
235 vector<docstring> const BiblioInfo::getFields() const
236 {
237         vector<docstring> bibfields;
238         set<docstring>::const_iterator it = fieldNames.begin();
239         set<docstring>::const_iterator end = fieldNames.end();
240         for (; it != end; ++it)
241                 bibfields.push_back(*it);
242         std::sort(bibfields.begin(), bibfields.end());
243         return bibfields;
244 }
245
246
247 vector<docstring> const BiblioInfo::getEntries() const
248 {
249         vector<docstring> bibentries;
250         set<docstring>::const_iterator it = entryTypes.begin();
251         set<docstring>::const_iterator end = entryTypes.end();
252         for (; it != end; ++it)
253                 bibentries.push_back(*it);
254         std::sort(bibentries.begin(), bibentries.end());
255         return bibentries;
256 }
257
258
259 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
260 {
261         BiblioInfo::const_iterator it = find(key);
262         if (it == end())
263                 return docstring();
264         BibTeXInfo const & data = it->second;
265         return data.getAbbreviatedAuthor();
266 }
267
268
269 docstring const BiblioInfo::getYear(docstring const & key) const
270 {
271         BiblioInfo::const_iterator it = find(key);
272         if (it == end())
273                 return docstring();
274         BibTeXInfo const & data = it->second;
275         return data.getYear();
276 }
277
278
279 docstring const BiblioInfo::getInfo(docstring const & key) const
280 {
281         BiblioInfo::const_iterator it = find(key);
282         if (it == end())
283                 return docstring();
284         BibTeXInfo const & data = it->second;
285         return data.getInfo();
286 }
287
288
289 vector<docstring> const BiblioInfo::getCiteStrings(
290         docstring const & key, Buffer const & buf) const
291 {
292         biblio::CiteEngine const engine = buf.params().getEngine();
293         if (engine == biblio::ENGINE_NATBIB_NUMERICAL)
294                 return getNumericalStrings(key, buf);
295         else
296                 return getAuthorYearStrings(key, buf);
297 }
298
299
300 vector<docstring> const BiblioInfo::getNumericalStrings(
301         docstring const & key, Buffer const & buf) const
302 {
303         if (empty())
304                 return vector<docstring>();
305
306         docstring const author = getAbbreviatedAuthor(key);
307         docstring const year   = getYear(key);
308         if (author.empty() || year.empty())
309                 return vector<docstring>();
310
311         vector<biblio::CiteStyle> const & styles = 
312                 biblio::getCiteStyles(buf.params().getEngine());
313         
314         vector<docstring> vec(styles.size());
315         for (size_t i = 0; i != vec.size(); ++i) {
316                 docstring str;
317
318                 switch (styles[i]) {
319                         case biblio::CITE:
320                         case biblio::CITEP:
321                                 str = from_ascii("[#ID]");
322                                 break;
323
324                         case biblio::CITET:
325                                 str = author + " [#ID]";
326                                 break;
327
328                         case biblio::CITEALT:
329                                 str = author + " #ID";
330                                 break;
331
332                         case biblio::CITEALP:
333                                 str = from_ascii("#ID");
334                                 break;
335
336                         case biblio::CITEAUTHOR:
337                                 str = author;
338                                 break;
339
340                         case biblio::CITEYEAR:
341                                 str = year;
342                                 break;
343
344                         case biblio::CITEYEARPAR:
345                                 str = '(' + year + ')';
346                                 break;
347                 }
348
349                 vec[i] = str;
350         }
351
352         return vec;
353 }
354
355
356 vector<docstring> const BiblioInfo::getAuthorYearStrings(
357         docstring const & key, Buffer const & buf) const
358 {
359         if (empty())
360                 return vector<docstring>();
361
362         docstring const author = getAbbreviatedAuthor(key);
363         docstring const year   = getYear(key);
364         if (author.empty() || year.empty())
365                 return vector<docstring>();
366
367         vector<biblio::CiteStyle> const & styles = 
368                 getCiteStyles(buf.params().getEngine());
369         
370         vector<docstring> vec(styles.size());
371         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
372                 docstring str;
373
374                 switch (styles[i]) {
375                         case biblio::CITE:
376                 // jurabib only: Author/Annotator
377                 // (i.e. the "before" field, 2nd opt arg)
378                                 str = author + "/<" + _("before") + '>';
379                                 break;
380
381                         case biblio::CITET:
382                                 str = author + " (" + year + ')';
383                                 break;
384
385                         case biblio::CITEP:
386                                 str = '(' + author + ", " + year + ')';
387                                 break;
388
389                         case biblio::CITEALT:
390                                 str = author + ' ' + year ;
391                                 break;
392
393                         case biblio::CITEALP:
394                                 str = author + ", " + year ;
395                                 break;
396
397                         case biblio::CITEAUTHOR:
398                                 str = author;
399                                 break;
400
401                         case biblio::CITEYEAR:
402                                 str = year;
403                                 break;
404
405                         case biblio::CITEYEARPAR:
406                                 str = '(' + year + ')';
407                                 break;
408                 }
409                 vec[i] = str;
410         }
411         return vec;
412 }
413
414
415 void BiblioInfo::fillWithBibKeys(Buffer const * const buf)
416 {       
417         /// if this is a child document and the parent is already loaded
418         /// use the parent's list instead  [ale990412]
419         Buffer const * const tmp = buf->masterBuffer();
420         BOOST_ASSERT(tmp);
421         if (tmp != buf) {
422                 this->fillWithBibKeys(tmp);
423                 return;
424         }
425
426         // Pre-load all child documents.
427         buf->loadChildDocuments();
428
429         for (InsetIterator it = inset_iterator_begin(buf->inset()); it; ++it)
430                 it->fillWithBibKeys(*buf, *this, it);
431 }
432
433
434 namespace biblio {
435
436 //////////////////////////////////////////////////////////////////////
437 //
438 // CitationStyle
439 //
440 //////////////////////////////////////////////////////////////////////
441
442 namespace {
443
444
445 char const * const citeCommands[] = {
446         "cite", "citet", "citep", "citealt", "citealp", "citeauthor",
447         "citeyear", "citeyearpar" };
448
449 unsigned int const nCiteCommands =
450                 sizeof(citeCommands) / sizeof(char *);
451
452 CiteStyle const citeStyles[] = {
453         CITE, CITET, CITEP, CITEALT, CITEALP,
454 CITEAUTHOR, CITEYEAR, CITEYEARPAR };
455
456 unsigned int const nCiteStyles =
457                 sizeof(citeStyles) / sizeof(CiteStyle);
458
459 CiteStyle const citeStylesFull[] = {
460         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
461
462 unsigned int const nCiteStylesFull =
463                 sizeof(citeStylesFull) / sizeof(CiteStyle);
464
465 CiteStyle const citeStylesUCase[] = {
466         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
467
468 unsigned int const nCiteStylesUCase =
469         sizeof(citeStylesUCase) / sizeof(CiteStyle);
470
471 } // namespace anon
472
473
474 CitationStyle::CitationStyle(string const & command)
475         : style(CITE), full(false), forceUCase(false)
476 {
477         if (command.empty())
478                 return;
479
480         string cmd = command;
481         if (cmd[0] == 'C') {
482                 forceUCase = true;
483                 cmd[0] = 'c';
484         }
485
486         string::size_type const n = cmd.size() - 1;
487         if (cmd != "cite" && cmd[n] == '*') {
488                 full = true;
489                 cmd = cmd.substr(0,n);
490         }
491
492         char const * const * const last = citeCommands + nCiteCommands;
493         char const * const * const ptr = std::find(citeCommands, last, cmd);
494
495         if (ptr != last) {
496                 size_t idx = ptr - citeCommands;
497                 style = citeStyles[idx];
498         }
499 }
500
501
502 string const CitationStyle::asLatexStr() const
503 {
504         string cite = citeCommands[style];
505         if (full) {
506                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
507                 if (std::find(citeStylesFull, last, style) != last)
508                         cite += '*';
509         }
510
511         if (forceUCase) {
512                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
513                 if (std::find(citeStylesUCase, last, style) != last)
514                         cite[0] = 'C';
515         }
516
517         return cite;
518 }
519
520
521 vector<CiteStyle> const getCiteStyles(CiteEngine const engine)
522 {
523         unsigned int nStyles = 0;
524         unsigned int start = 0;
525
526         switch (engine) {
527                 case ENGINE_BASIC:
528                         nStyles = 1;
529                         start = 0;
530                         break;
531                 case ENGINE_NATBIB_AUTHORYEAR:
532                 case ENGINE_NATBIB_NUMERICAL:
533                         nStyles = nCiteStyles - 1;
534                         start = 1;
535                         break;
536                 case ENGINE_JURABIB:
537                         nStyles = nCiteStyles;
538                         start = 0;
539                         break;
540         }
541
542         typedef vector<CiteStyle> cite_vec;
543
544         cite_vec styles(nStyles);
545         size_t i = 0;
546         int j = start;
547         for (; i != styles.size(); ++i, ++j)
548                 styles[i] = citeStyles[j];
549
550         return styles;
551 }
552
553 } // namespace biblio
554 } // namespace lyx
555