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