]> git.lyx.org Git - lyx.git/blob - src/BiblioInfo.cpp
src/*.cpp: reformatting to increase consistency
[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 "Encoding.h"
20 #include "InsetIterator.h"
21 #include "Language.h"
22 #include "Paragraph.h"
23 #include "TextClass.h"
24 #include "TocBackend.h"
25
26 #include "support/convert.h"
27 #include "support/debug.h"
28 #include "support/docstream.h"
29 #include "support/gettext.h"
30 #include "support/lassert.h"
31 #include "support/lstrings.h"
32 #include "support/regex.h"
33 #include "support/textutils.h"
34
35 #include <set>
36
37 using namespace std;
38 using namespace lyx::support;
39
40
41 namespace lyx {
42
43 namespace {
44
45 // gets the "family name" from an author-type string
46 docstring familyName(docstring const & name)
47 {
48         if (name.empty())
49                 return docstring();
50
51         // first we look for a comma, and take the last name to be everything
52         // preceding the right-most one, so that we also get the "jr" part.
53         docstring::size_type idx = name.rfind(',');
54         if (idx != docstring::npos)
55                 return ltrim(name.substr(0, idx));
56
57         // OK, so now we want to look for the last name. We're going to
58         // include the "von" part. This isn't perfect.
59         // Split on spaces, to get various tokens.
60         vector<docstring> pieces = getVectorFromString(name, from_ascii(" "));
61         // If we only get two, assume the last one is the last name
62         if (pieces.size() <= 2)
63                 return pieces.back();
64
65         // Now we look for the first token that begins with a lower case letter.
66         vector<docstring>::const_iterator it = pieces.begin();
67         vector<docstring>::const_iterator en = pieces.end();
68         for (; it != en; ++it) {
69                 if ((*it).empty())
70                         continue;
71                 char_type const c = (*it)[0];
72                 if (isLower(c))
73                         break;
74         }
75
76         if (it == en) // we never found a "von"
77                 return pieces.back();
78
79         // reconstruct what we need to return
80         docstring retval;
81         bool first = true;
82         for (; it != en; ++it) {
83                 if (!first)
84                         retval += " ";
85                 else
86                         first = false;
87                 retval += *it;
88         }
89         return retval;
90 }
91
92
93 // converts a string containing LaTeX commands into unicode
94 // for display.
95 docstring convertLaTeXCommands(docstring const & str)
96 {
97         docstring val = str;
98         docstring ret;
99
100         bool scanning_cmd = false;
101         bool scanning_math = false;
102         bool escaped = false; // used to catch \$, etc.
103         while (!val.empty()) {
104                 char_type const ch = val[0];
105
106                 // if we're scanning math, we output everything until we
107                 // find an unescaped $, at which point we break out.
108                 if (scanning_math) {
109                         if (escaped)
110                                 escaped = false;
111                         else if (ch == '\\')
112                                 escaped = true;
113                         else if (ch == '$')
114                                 scanning_math = false;
115                         ret += ch;
116                         val = val.substr(1);
117                         continue;
118                 }
119
120                 // if we're scanning a command name, then we just
121                 // discard characters until we hit something that
122                 // isn't alpha.
123                 if (scanning_cmd) {
124                         if (isAlphaASCII(ch)) {
125                                 val = val.substr(1);
126                                 escaped = false;
127                                 continue;
128                         }
129                         // so we're done with this command.
130                         // now we fall through and check this character.
131                         scanning_cmd = false;
132                 }
133
134                 // was the last character a \? If so, then this is something like:
135                 // \\ or \$, so we'll just output it. That's probably not always right...
136                 if (escaped) {
137                         // exception: output \, as THIN SPACE
138                         if (ch == ',')
139                                 ret.push_back(0x2009);
140                         else
141                                 ret += ch;
142                         val = val.substr(1);
143                         escaped = false;
144                         continue;
145                 }
146
147                 if (ch == '$') {
148                         ret += ch;
149                         val = val.substr(1);
150                         scanning_math = true;
151                         continue;
152                 }
153
154                 // we just ignore braces
155                 if (ch == '{' || ch == '}') {
156                         val = val.substr(1);
157                         continue;
158                 }
159
160                 // we're going to check things that look like commands, so if
161                 // this doesn't, just output it.
162                 if (ch != '\\') {
163                         ret += ch;
164                         val = val.substr(1);
165                         continue;
166                 }
167
168                 // ok, could be a command of some sort
169                 // let's see if it corresponds to some unicode
170                 // unicodesymbols has things in the form: \"{u},
171                 // whereas we may see things like: \"u. So we'll
172                 // look for that and change it, if necessary.
173                 // FIXME: This is a sort of mini-tex2lyx.
174                 //        Use the real tex2lyx instead!
175                 static lyx::regex const reg("^\\\\\\W\\w");
176                 if (lyx::regex_search(to_utf8(val), reg)) {
177                         val.insert(3, from_ascii("}"));
178                         val.insert(2, from_ascii("{"));
179                 }
180                 bool termination;
181                 docstring rem;
182                 docstring const cnvtd = Encodings::fromLaTeXCommand(val,
183                                 Encodings::TEXT_CMD, termination, rem);
184                 if (!cnvtd.empty()) {
185                         // it did, so we'll take that bit and proceed with what's left
186                         ret += cnvtd;
187                         val = rem;
188                         continue;
189                 }
190                 // it's a command of some sort
191                 scanning_cmd = true;
192                 escaped = true;
193                 val = val.substr(1);
194         }
195         return ret;
196 }
197
198 } // anon namespace
199
200
201 //////////////////////////////////////////////////////////////////////
202 //
203 // BibTeXInfo
204 //
205 //////////////////////////////////////////////////////////////////////
206
207 BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
208         : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
209           modifier_(0)
210 {}
211
212
213 docstring const BibTeXInfo::getAbbreviatedAuthor(bool jurabib_style) const
214 {
215         if (!is_bibtex_) {
216                 docstring const opt = label();
217                 if (opt.empty())
218                         return docstring();
219
220                 docstring authors;
221                 docstring const remainder = trim(split(opt, authors, '('));
222                 if (remainder.empty())
223                         // in this case, we didn't find a "(",
224                         // so we don't have author (year)
225                         return docstring();
226                 return authors;
227         }
228
229         docstring author = convertLaTeXCommands(operator[]("author"));
230         if (author.empty()) {
231                 author = convertLaTeXCommands(operator[]("editor"));
232                 if (author.empty())
233                         return author;
234         }
235
236         // FIXME Move this to a separate routine that can
237         // be called from elsewhere.
238         //
239         // OK, we've got some names. Let's format them.
240         // Try to split the author list on " and "
241         vector<docstring> const authors =
242                 getVectorFromString(author, from_ascii(" and "));
243
244         if (jurabib_style && (authors.size() == 2 || authors.size() == 3)) {
245                 docstring shortauthor = familyName(authors[0])
246                         + "/" + familyName(authors[1]);
247                 if (authors.size() == 3)
248                         shortauthor += "/" + familyName(authors[2]);
249                 return shortauthor;
250         }
251
252         if (authors.size() == 2)
253                 return bformat(_("%1$s and %2$s"),
254                         familyName(authors[0]), familyName(authors[1]));
255
256         if (authors.size() > 2)
257                 return bformat(_("%1$s et al."), familyName(authors[0]));
258
259         return familyName(authors[0]);
260 }
261
262
263 docstring const BibTeXInfo::getYear() const
264 {
265         if (is_bibtex_)
266                 return operator[]("year");
267
268         docstring const opt = label();
269         if (opt.empty())
270                 return docstring();
271
272         docstring authors;
273         docstring tmp = split(opt, authors, '(');
274         if (tmp.empty())
275                 // we don't have author (year)
276                 return docstring();
277         docstring year;
278         tmp = split(tmp, year, ')');
279         return year;
280 }
281
282
283 docstring const BibTeXInfo::getXRef() const
284 {
285         if (!is_bibtex_)
286                 return docstring();
287         return operator[]("crossref");
288 }
289
290
291 namespace {
292
293 string parseOptions(string const & format, string & optkey,
294                     string & ifpart, string & elsepart);
295
296 // Calls parseOptions to deal with an embedded option, such as:
297 //   {%number%[[, no.~%number%]]}
298 // which must appear at the start of format. ifelsepart gets the
299 // whole of the option, and we return what's left after the option.
300 // we return format if there is an error.
301 string parseEmbeddedOption(string const & format, string & ifelsepart)
302 {
303         LASSERT(format[0] == '{' && format[1] == '%', return format);
304         string optkey;
305         string ifpart;
306         string elsepart;
307         string const rest = parseOptions(format, optkey, ifpart, elsepart);
308         if (format == rest) { // parse error
309                 LYXERR0("ERROR! Couldn't parse `" << format <<"'.");
310                 return format;
311         }
312         LASSERT(rest.size() <= format.size(), /* */);
313         ifelsepart = format.substr(0, format.size() - rest.size());
314                 return rest;
315 }
316
317
318 // Gets a "clause" from a format string, where the clause is
319 // delimited by '[[' and ']]'. Returns what is left after the
320 // clause is removed, and returns format if there is an error.
321 string getClause(string const & format, string & clause)
322 {
323         string fmt = format;
324         // remove '[['
325         fmt = fmt.substr(2);
326         // we'll remove characters from the front of fmt as we
327         // deal with them
328         while (!fmt.empty()) {
329                 if (fmt[0] == ']' && fmt.size() > 1 && fmt[1] == ']') {
330                         // that's the end
331                         fmt = fmt.substr(2);
332                         break;
333                 }
334                 // check for an embedded option
335                 if (fmt[0] == '{' && fmt.size() > 1 && fmt[1] == '%') {
336                         string part;
337                         string const rest = parseEmbeddedOption(fmt, part);
338                         if (fmt == rest) {
339                                 LYXERR0("ERROR! Couldn't parse embedded option in `" << format <<"'.");
340                                 return format;
341                         }
342                         clause += part;
343                         fmt = rest;
344                 } else { // it's just a normal character
345                                 clause += fmt[0];
346                                 fmt = fmt.substr(1);
347                 }
348         }
349         return fmt;
350 }
351
352
353 // parse an options string, which must appear at the start of the
354 // format parameter. puts the parsed bits in optkey, ifpart, and
355 // elsepart and returns what's left after the option is removed.
356 // if there's an error, it returns format itself.
357 string parseOptions(string const & format, string & optkey,
358                     string & ifpart, string & elsepart)
359 {
360         LASSERT(format[0] == '{' && format[1] == '%', return format);
361         // strip '{%'
362         string fmt = format.substr(2);
363         size_t pos = fmt.find('%'); // end of key
364         if (pos == string::npos) {
365                 LYXERR0("Error parsing  `" << format <<"'. Can't find end of key.");
366                 return format;
367         }
368         optkey = fmt.substr(0,pos);
369         fmt = fmt.substr(pos + 1);
370         // [[format]] should be next
371         if (fmt[0] != '[' || fmt[1] != '[') {
372                 LYXERR0("Error parsing  `" << format <<"'. Can't find '[[' after key.");
373                 return format;
374         }
375
376         string curfmt = fmt;
377         fmt = getClause(curfmt, ifpart);
378         if (fmt == curfmt) {
379                 LYXERR0("Error parsing  `" << format <<"'. Couldn't get if clause.");
380                 return format;
381         }
382
383         if (fmt[0] == '}') // we're done, no else clause
384                 return fmt.substr(1);
385
386         // else part should follow
387         if (fmt[0] != '[' || fmt[1] != '[') {
388                 LYXERR0("Error parsing  `" << format <<"'. Can't find else clause.");
389                 return format;
390         }
391
392         curfmt = fmt;
393         fmt = getClause(curfmt, elsepart);
394         // we should be done
395         if (fmt == curfmt || fmt[0] != '}') {
396                 LYXERR0("Error parsing  `" << format <<"'. Can't find end of option.");
397                 return format;
398         }
399         return fmt.substr(1);
400 }
401
402
403 } // anon namespace
404
405
406 docstring BibTeXInfo::expandFormat(string const & format,
407                 BibTeXInfo const * const xref, int & counter, Buffer const & buf,
408                 bool richtext, docstring before, docstring after, docstring dialog, bool next) const
409 {
410         // incorrect use of macros could put us in an infinite loop
411         static int max_passes = 5000;
412         docstring ret; // return value
413         string key;
414         bool scanning_key = false;
415         bool scanning_rich = false;
416
417         CiteEngineType const engine_type = buf.params().citeEngineType();
418         string fmt = format;
419         // we'll remove characters from the front of fmt as we
420         // deal with them
421         while (!fmt.empty()) {
422                 if (counter++ > max_passes) {
423                         LYXERR0("Recursion limit reached while parsing `"
424                                 << format << "'.");
425                         return _("ERROR!");
426                 }
427
428                 char_type thischar = fmt[0];
429                 if (thischar == '%') {
430                         // beginning or end of key
431                         if (scanning_key) {
432                                 // end of key
433                                 scanning_key = false;
434                                 // so we replace the key with its value, which may be empty
435                                 if (key[0] == '!') {
436                                         // macro
437                                         // FIXME: instead of passing the buf, just past the macros
438                                         // FIXME: and the language code
439                                         string const val =
440                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
441                                         fmt = val + fmt.substr(1);
442                                         continue;
443                                 } else if (key[0] == '_') {
444                                         // a translatable bit
445                                         string const val =
446                                                 buf.params().documentClass().getCiteMacro(engine_type, key);
447                                         docstring const trans =
448                                                 translateIfPossible(from_utf8(val), buf.params().language->code());
449                                         ret += trans;
450                                 } else {
451                                         docstring const val = getValueForKey(key, before, after, dialog, xref);
452                                         ret += val;
453                                 }
454                         } else {
455                                 // beginning of key
456                                 key.clear();
457                                 scanning_key = true;
458                         }
459                 }
460                 else if (thischar == '{') {
461                         // beginning of option?
462                         if (scanning_key) {
463                                 LYXERR0("ERROR: Found `{' when scanning key in `" << format << "'.");
464                                 return _("ERROR!");
465                         }
466                         if (fmt.size() > 1) {
467                                 if (fmt[1] == '%') {
468                                         // it is the beginning of an optional format
469                                         string optkey;
470                                         string ifpart;
471                                         string elsepart;
472                                         string const newfmt =
473                                                 parseOptions(fmt, optkey, ifpart, elsepart);
474                                         if (newfmt == fmt) // parse error
475                                                 return _("ERROR!");
476                                         fmt = newfmt;
477                                         docstring const val = getValueForKey(optkey, before, after, dialog, xref);
478                                         if (optkey == "next" && next)
479                                                 ret += from_utf8(ifpart); // without expansion
480                                         else if (!val.empty())
481                                                 ret += expandFormat(ifpart, xref, counter, buf,
482                                                         richtext, before, after, dialog, next);
483                                         else if (!elsepart.empty())
484                                                 ret += expandFormat(elsepart, xref, counter, buf,
485                                                         richtext, before, after, dialog, next);
486                                         // fmt will have been shortened for us already
487                                         continue;
488                                 }
489                                 if (fmt[1] == '!') {
490                                         // beginning of rich text
491                                         scanning_rich = true;
492                                         fmt = fmt.substr(2);
493                                         continue;
494                                 }
495                         }
496                         // we are here if '{' was not followed by % or !.
497                         // So it's just a character.
498                         ret += thischar;
499                 }
500                 else if (scanning_rich && thischar == '!'
501                          && fmt.size() > 1 && fmt[1] == '}') {
502                         // end of rich text
503                         scanning_rich = false;
504                         fmt = fmt.substr(2);
505                         continue;
506                 }
507                 else if (scanning_key)
508                         key += char(thischar);
509                 else if (richtext) {
510                         if (scanning_rich)
511                                 ret += thischar;
512                         else {
513                                 // we need to escape '<' and '>'
514                                 if (thischar == '<')
515                                         ret += "&lt;";
516                                 else if (thischar == '>')
517                                         ret += "&gt;";
518                                 else
519                                         ret += thischar;
520                         }
521                 }       else if (!scanning_rich /* && !richtext */)
522                         ret += thischar;
523                 // else the character is discarded, which will happen only if
524                 // richtext == false and we are scanning rich text
525                 fmt = fmt.substr(1);
526         } // for loop
527         if (scanning_key) {
528                 LYXERR0("Never found end of key in `" << format << "'!");
529                 return _("ERROR!");
530         }
531         if (scanning_rich) {
532                 LYXERR0("Never found end of rich text in `" << format << "'!");
533                 return _("ERROR!");
534         }
535         return ret;
536 }
537
538
539 docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
540         Buffer const & buf, bool richtext) const
541 {
542         if (!info_.empty())
543                 return info_;
544
545         if (!is_bibtex_) {
546                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
547                 info_ = it->second;
548                 return info_;
549         }
550
551         CiteEngineType const engine_type = buf.params().citeEngineType();
552         DocumentClass const & dc = buf.params().documentClass();
553         string const & format = dc.getCiteFormat(engine_type, to_utf8(entry_type_));
554         int counter = 0;
555         info_ = expandFormat(format, xref, counter, buf, richtext);
556
557         if (!info_.empty())
558                 info_ = convertLaTeXCommands(info_);
559         return info_;
560 }
561
562
563 docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
564         Buffer const & buf, string const & format, bool richtext,
565         docstring before, docstring after, docstring dialog, bool next) const
566 {
567         docstring loclabel;
568
569         /*
570         if (!is_bibtex_) {
571                 BibTeXInfo::const_iterator it = find(from_ascii("ref"));
572                 label_ = it->second;
573                 return label_;
574         }
575         */
576
577         int counter = 0;
578         loclabel = expandFormat(format, xref, counter, buf, richtext,
579                 before, after, dialog, next);
580
581         if (!loclabel.empty())
582                 loclabel = convertLaTeXCommands(loclabel);
583         return loclabel;
584 }
585
586
587 docstring const & BibTeXInfo::operator[](docstring const & field) const
588 {
589         BibTeXInfo::const_iterator it = find(field);
590         if (it != end())
591                 return it->second;
592         static docstring const empty_value = docstring();
593         return empty_value;
594 }
595
596
597 docstring const & BibTeXInfo::operator[](string const & field) const
598 {
599         return operator[](from_ascii(field));
600 }
601
602
603 docstring BibTeXInfo::getValueForKey(string const & key,
604         docstring const & before, docstring const & after, docstring const & dialog,
605         BibTeXInfo const * const xref) const
606 {
607         docstring ret = operator[](key);
608         if (ret.empty() && xref)
609                 ret = (*xref)[key];
610         if (!ret.empty())
611                 return ret;
612         // some special keys
613         // FIXME: dialog, textbefore and textafter have nothing to do with this
614         if (key == "dialog")
615                 return dialog;
616         else if (key == "entrytype")
617                 return entry_type_;
618         else if (key == "key")
619                 return bib_key_;
620         else if (key == "label")
621                 return label_;
622         else if (key == "abbrvauthor")
623                 // Special key to provide abbreviated author names.
624                 return getAbbreviatedAuthor();
625         else if (key == "shortauthor")
626                 // When shortauthor is not defined, jurabib automatically
627                 // provides jurabib-style abbreviated author names. We do
628                 // this as well.
629                 return getAbbreviatedAuthor(true);
630         else if (key == "shorttitle") {
631                 // When shorttitle is not defined, jurabib uses for `article'
632                 // and `periodical' entries the form `journal volume [year]'
633                 // and for other types of entries it uses the `title' field.
634                 if (entry_type_ == "article" || entry_type_ == "periodical")
635                         return operator[]("journal") + " " + operator[]("volume")
636                                 + " [" + operator[]("year") + "]";
637                 else
638                         return operator[]("title");
639         } else if (key == "textbefore")
640                 return before;
641         else if (key == "textafter")
642                 return after;
643         else if (key == "year")
644                 return getYear();
645         return ret;
646 }
647
648
649 //////////////////////////////////////////////////////////////////////
650 //
651 // BiblioInfo
652 //
653 //////////////////////////////////////////////////////////////////////
654
655 namespace {
656
657 // A functor for use with sort, leading to case insensitive sorting
658 class compareNoCase: public binary_function<docstring, docstring, bool>
659 {
660 public:
661         bool operator()(docstring const & s1, docstring const & s2) const {
662                 return compare_no_case(s1, s2) < 0;
663         }
664 };
665
666 } // namespace anon
667
668
669 vector<docstring> const BiblioInfo::getKeys() const
670 {
671         vector<docstring> bibkeys;
672         BiblioInfo::const_iterator it  = begin();
673         for (; it != end(); ++it)
674                 bibkeys.push_back(it->first);
675         sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
676         return bibkeys;
677 }
678
679
680 vector<docstring> const BiblioInfo::getFields() const
681 {
682         vector<docstring> bibfields;
683         set<docstring>::const_iterator it = field_names_.begin();
684         set<docstring>::const_iterator end = field_names_.end();
685         for (; it != end; ++it)
686                 bibfields.push_back(*it);
687         sort(bibfields.begin(), bibfields.end());
688         return bibfields;
689 }
690
691
692 vector<docstring> const BiblioInfo::getEntries() const
693 {
694         vector<docstring> bibentries;
695         set<docstring>::const_iterator it = entry_types_.begin();
696         set<docstring>::const_iterator end = entry_types_.end();
697         for (; it != end; ++it)
698                 bibentries.push_back(*it);
699         sort(bibentries.begin(), bibentries.end());
700         return bibentries;
701 }
702
703
704 docstring const BiblioInfo::getAbbreviatedAuthor(docstring const & key) const
705 {
706         BiblioInfo::const_iterator it = find(key);
707         if (it == end())
708                 return docstring();
709         BibTeXInfo const & data = it->second;
710         return data.getAbbreviatedAuthor();
711 }
712
713
714 docstring const BiblioInfo::getCiteNumber(docstring const & key) const
715 {
716         BiblioInfo::const_iterator it = find(key);
717         if (it == end())
718                 return docstring();
719         BibTeXInfo const & data = it->second;
720         return data.citeNumber();
721 }
722
723
724 docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
725 {
726         BiblioInfo::const_iterator it = find(key);
727         if (it == end())
728                 return docstring();
729         BibTeXInfo const & data = it->second;
730         docstring year = data.getYear();
731         if (year.empty()) {
732                 // let's try the crossref
733                 docstring const xref = data.getXRef();
734                 if (xref.empty())
735                         return _("No year"); // no luck
736                 BiblioInfo::const_iterator const xrefit = find(xref);
737                 if (xrefit == end())
738                         return _("No year"); // no luck again
739                 BibTeXInfo const & xref_data = xrefit->second;
740                 year = xref_data.getYear();
741         }
742         if (use_modifier && data.modifier() != 0)
743                 year += data.modifier();
744         return year;
745 }
746
747
748 docstring const BiblioInfo::getInfo(docstring const & key,
749         Buffer const & buf, bool richtext) const
750 {
751         BiblioInfo::const_iterator it = find(key);
752         if (it == end())
753                 return docstring(_("Bibliography entry not found!"));
754         BibTeXInfo const & data = it->second;
755         BibTeXInfo const * xrefptr = 0;
756         docstring const xref = data.getXRef();
757         if (!xref.empty()) {
758                 BiblioInfo::const_iterator const xrefit = find(xref);
759                 if (xrefit != end())
760                         xrefptr = &(xrefit->second);
761         }
762         return data.getInfo(xrefptr, buf, richtext);
763 }
764
765
766 docstring const BiblioInfo::getLabel(vector<docstring> const & keys,
767         Buffer const & buf, string const & style, bool richtext,
768         docstring const & before, docstring const & after, docstring const & dialog) const
769 {
770         CiteEngineType const engine_type = buf.params().citeEngineType();
771         DocumentClass const & dc = buf.params().documentClass();
772         string const & format = dc.getCiteFormat(engine_type, style, "cite");
773         docstring ret = from_utf8(format);
774         vector<docstring>::const_iterator key = keys.begin();
775         vector<docstring>::const_iterator ken = keys.end();
776         for (; key != ken; ++key) {
777                 BiblioInfo::const_iterator it = find(*key);
778                 BibTeXInfo empty_data;
779                 empty_data.key(*key);
780                 BibTeXInfo & data = empty_data;
781                 BibTeXInfo const * xrefptr = 0;
782                 if (it != end()) {
783                         data = it->second;
784                         docstring const xref = data.getXRef();
785                         if (!xref.empty()) {
786                                 BiblioInfo::const_iterator const xrefit = find(xref);
787                                 if (xrefit != end())
788                                         xrefptr = &(xrefit->second);
789                         }
790                 }
791                 ret = data.getLabel(xrefptr, buf, to_utf8(ret), richtext,
792                         before, after, dialog, key+1 != ken);
793         }
794         return ret;
795 }
796
797
798 bool BiblioInfo::isBibtex(docstring const & key) const
799 {
800         BiblioInfo::const_iterator it = find(key);
801         if (it == end())
802                 return false;
803         return it->second.isBibTeX();
804 }
805
806
807 vector<docstring> const BiblioInfo::getCiteStrings(
808         vector<docstring> const & keys, vector<CitationStyle> const & styles,
809         Buffer const & buf, bool richtext, docstring const & before,
810         docstring const & after, docstring const & dialog) const
811 {
812         if (empty())
813                 return vector<docstring>();
814
815         string style;
816         vector<docstring> vec(styles.size());
817         for (size_t i = 0; i != vec.size(); ++i) {
818                 style = styles[i].cmd;
819                 vec[i] = getLabel(keys, buf, style, richtext, before, after, dialog);
820         }
821
822         return vec;
823 }
824
825
826 void BiblioInfo::mergeBiblioInfo(BiblioInfo const & info)
827 {
828         bimap_.insert(info.begin(), info.end());
829         field_names_.insert(info.field_names_.begin(), info.field_names_.end());
830         entry_types_.insert(info.entry_types_.begin(), info.entry_types_.end());
831 }
832
833
834 namespace {
835
836 // used in xhtml to sort a list of BibTeXInfo objects
837 bool lSorter(BibTeXInfo const * lhs, BibTeXInfo const * rhs)
838 {
839         docstring const lauth = lhs->getAbbreviatedAuthor();
840         docstring const rauth = rhs->getAbbreviatedAuthor();
841         docstring const lyear = lhs->getYear();
842         docstring const ryear = rhs->getYear();
843         docstring const ltitl = lhs->operator[]("title");
844         docstring const rtitl = rhs->operator[]("title");
845         return  (lauth < rauth)
846                 || (lauth == rauth && lyear < ryear)
847                 || (lauth == rauth && lyear == ryear && ltitl < rtitl);
848 }
849
850 }
851
852
853 void BiblioInfo::collectCitedEntries(Buffer const & buf)
854 {
855         cited_entries_.clear();
856         // We are going to collect all the citation keys used in the document,
857         // getting them from the TOC.
858         // FIXME We may want to collect these differently, in the first case,
859         // so that we might have them in order of appearance.
860         set<docstring> citekeys;
861         Toc const & toc = buf.tocBackend().toc("citation");
862         Toc::const_iterator it = toc.begin();
863         Toc::const_iterator const en = toc.end();
864         for (; it != en; ++it) {
865                 if (it->str().empty())
866                         continue;
867                 vector<docstring> const keys = getVectorFromString(it->str());
868                 citekeys.insert(keys.begin(), keys.end());
869         }
870         if (citekeys.empty())
871                 return;
872
873         // We have a set of the keys used in this document.
874         // We will now convert it to a list of the BibTeXInfo objects used in
875         // this document...
876         vector<BibTeXInfo const *> bi;
877         set<docstring>::const_iterator cit = citekeys.begin();
878         set<docstring>::const_iterator const cen = citekeys.end();
879         for (; cit != cen; ++cit) {
880                 BiblioInfo::const_iterator const bt = find(*cit);
881                 if (bt == end() || !bt->second.isBibTeX())
882                         continue;
883                 bi.push_back(&(bt->second));
884         }
885         // ...and sort it.
886         sort(bi.begin(), bi.end(), lSorter);
887
888         // Now we can write the sorted keys
889         vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
890         vector<BibTeXInfo const *>::const_iterator ben = bi.end();
891         for (; bit != ben; ++bit)
892                 cited_entries_.push_back((*bit)->key());
893 }
894
895
896 void BiblioInfo::makeCitationLabels(Buffer const & buf)
897 {
898         collectCitedEntries(buf);
899         CiteEngineType const engine_type = buf.params().citeEngineType();
900         bool const numbers = (engine_type == ENGINE_TYPE_NUMERICAL);
901
902         int keynumber = 0;
903         char modifier = 0;
904         // used to remember the last one we saw
905         // we'll be comparing entries to see if we need to add
906         // modifiers, like "1984a"
907         map<docstring, BibTeXInfo>::iterator last;
908
909         vector<docstring>::const_iterator it = cited_entries_.begin();
910         vector<docstring>::const_iterator const en = cited_entries_.end();
911         for (; it != en; ++it) {
912                 map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
913                 // this shouldn't happen, but...
914                 if (biit == bimap_.end())
915                         // ...fail gracefully, anyway.
916                         continue;
917                 BibTeXInfo & entry = biit->second;
918                 if (numbers) {
919                         docstring const num = convert<docstring>(++keynumber);
920                         entry.setCiteNumber(num);
921                 } else {
922                         if (it != cited_entries_.begin()
923                             && entry.getAbbreviatedAuthor() == last->second.getAbbreviatedAuthor()
924                             // we access the year via getYear() so as to get it from the xref,
925                             // if we need to do so
926                             && getYear(entry.key()) == getYear(last->second.key())) {
927                                 if (modifier == 0) {
928                                         // so the last one should have been 'a'
929                                         last->second.setModifier('a');
930                                         modifier = 'b';
931                                 } else if (modifier == 'z')
932                                         modifier = 'A';
933                                 else
934                                         modifier++;
935                         } else {
936                                 modifier = 0;
937                         }
938                         entry.setModifier(modifier);
939                         // remember the last one
940                         last = biit;
941                 }
942         }
943 }
944
945
946 //////////////////////////////////////////////////////////////////////
947 //
948 // CitationStyle
949 //
950 //////////////////////////////////////////////////////////////////////
951
952
953 CitationStyle citationStyleFromString(string const & command)
954 {
955         CitationStyle cs;
956         if (command.empty())
957                 return cs;
958
959         string cmd = command;
960         if (cmd[0] == 'C') {
961                 cs.forceUpperCase = true;
962                 cmd[0] = 'c';
963         }
964
965         size_t const n = cmd.size() - 1;
966         if (cmd[n] == '*') {
967                 cs.fullAuthorList = true;
968                 cmd = cmd.substr(0, n);
969         }
970
971         cs.cmd = cmd;
972         return cs;
973 }
974
975
976 string citationStyleToString(const CitationStyle & cs)
977 {
978         string cmd = cs.cmd;
979         if (cs.forceUpperCase)
980                 cmd[0] = 'C';
981         if (cs.fullAuthorList)
982                 cmd += '*';
983         return cmd;
984 }
985
986 } // namespace lyx
987