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