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