3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Angus Leeming
10 * Full author contact details are available in file CREDITS.
15 #include "BiblioInfo.h"
17 #include "BufferParams.h"
18 #include "buffer_funcs.h"
20 #include "InsetIterator.h"
21 #include "Paragraph.h"
23 #include "insets/Inset.h"
24 #include "insets/InsetBibitem.h"
25 #include "insets/InsetBibtex.h"
26 #include "insets/InsetInclude.h"
28 #include "support/docstream.h"
29 #include "support/gettext.h"
30 #include "support/lassert.h"
31 #include "support/lstrings.h"
32 #include "support/textutils.h"
34 #include "boost/regex.hpp"
37 using namespace lyx::support
;
44 // gets the "family name" from an author-type string
45 docstring
familyName(docstring
const & name
)
50 // first we look for a comma, and take the last name to be everything
51 // preceding the right-most one, so that we also get the "jr" part.
52 docstring::size_type idx
= name
.rfind(',');
53 if (idx
!= docstring::npos
)
54 return ltrim(name
.substr(0, idx
));
56 // OK, so now we want to look for the last name. We're going to
57 // include the "von" part. This isn't perfect.
58 // Split on spaces, to get various tokens.
59 vector
<docstring
> pieces
= getVectorFromString(name
, from_ascii(" "));
60 // If we only get two, assume the last one is the last name
61 if (pieces
.size() <= 2)
64 // Now we look for the first token that begins with a lower case letter.
65 vector
<docstring
>::const_iterator it
= pieces
.begin();
66 vector
<docstring
>::const_iterator en
= pieces
.end();
67 for (; it
!= en
; ++it
) {
68 if ((*it
).size() == 0)
70 char_type
const c
= (*it
)[0];
75 if (it
== en
) // we never found a "von"
78 // reconstruct what we need to return
81 for (; it
!= en
; ++it
) {
91 // converts a string containing LaTeX commands into unicode
93 docstring
convertLaTeXCommands(docstring
const & str
)
98 bool scanning_cmd
= false;
99 bool scanning_math
= false;
100 bool escaped
= false; // used to catch \$, etc.
102 char_type
const ch
= val
[0];
104 // if we're scanning math, we output everything until we
105 // find an unescaped $, at which point we break out.
112 scanning_math
= false;
118 // if we're scanning a command name, then we just
119 // discard characters until we hit something that
122 if (isAlphaASCII(ch
)) {
127 // so we're done with this command.
128 // now we fall through and check this character.
129 scanning_cmd
= false;
132 // was the last character a \? If so, then this is something like: \\,
133 // or \$, so we'll just output it. That's probably not always right...
144 scanning_math
= true;
148 // we just ignore braces
149 if (ch
== '{' || ch
== '}') {
154 // we're going to check things that look like commands, so if
155 // this doesn't, just output it.
162 // ok, could be a command of some sort
163 // let's see if it corresponds to some unicode
164 // unicodesymbols has things in the form: \"{u},
165 // whereas we may see things like: \"u. So we'll
166 // look for that and change it, if necessary.
167 static boost::regex
const reg("^\\\\\\W\\w");
168 if (boost::regex_search(to_utf8(val
), reg
)) {
169 val
.insert(3, from_ascii("}"));
170 val
.insert(2, from_ascii("{"));
173 docstring
const cnvtd
= Encodings::fromLaTeXCommand(val
, rem
,
174 Encodings::TEXT_CMD
);
175 if (!cnvtd
.empty()) {
176 // it did, so we'll take that bit and proceed with what's left
181 // it's a command of some sort
192 //////////////////////////////////////////////////////////////////////
196 //////////////////////////////////////////////////////////////////////
198 BibTeXInfo::BibTeXInfo(docstring
const & key
, docstring
const & type
)
199 : is_bibtex_(true), bib_key_(key
), entry_type_(type
), info_()
203 bool BibTeXInfo::hasField(docstring
const & field
) const
205 return count(field
) == 1;
209 docstring
const BibTeXInfo::getAbbreviatedAuthor() const
212 docstring
const opt
= label();
217 split(opt
, authors
, '(');
221 docstring author
= convertLaTeXCommands(operator[]("author"));
222 if (author
.empty()) {
223 author
= convertLaTeXCommands(operator[]("editor"));
228 // OK, we've got some names. Let's format them.
229 // Try to split the author list on " and "
230 vector
<docstring
> const authors
=
231 getVectorFromString(author
, from_ascii(" and "));
233 if (authors
.size() == 2)
234 return bformat(_("%1$s and %2$s"),
235 familyName(authors
[0]), familyName(authors
[1]));
237 if (authors
.size() > 2)
238 return bformat(_("%1$s et al."), familyName(authors
[0]));
240 return familyName(authors
[0]);
244 docstring
const BibTeXInfo::getYear() const
247 return operator[]("year");
249 docstring
const opt
= label();
254 docstring
const tmp
= split(opt
, authors
, '(');
256 split(tmp
, year
, ')');
261 docstring
const BibTeXInfo::getXRef() const
265 return operator[]("crossref");
269 docstring
const & BibTeXInfo::getInfo(BibTeXInfo
const * const xref
) const
275 BibTeXInfo::const_iterator it
= find(from_ascii("ref"));
281 // This could be made a lot better using the entry_type_
282 // field to customize the output based upon entry type.
284 // Search for all possible "required" fields
285 docstring author
= getValueForKey("author", xref
);
287 author
= getValueForKey("editor", xref
);
289 docstring year
= getValueForKey("year", xref
);
290 docstring title
= getValueForKey("title", xref
);
291 docstring docLoc
= getValueForKey("pages", xref
);
292 if (docLoc
.empty()) {
293 docLoc
= getValueForKey("chapter", xref
);
295 docLoc
= _("Ch. ") + docLoc
;
297 docLoc
= _("pp. ") + docLoc
;
300 docstring media
= getValueForKey("journal", xref
);
302 media
= getValueForKey("publisher", xref
);
304 media
= getValueForKey("school", xref
);
306 media
= getValueForKey("institution");
309 docstring volume
= getValueForKey("volume", xref
);
311 odocstringstream result
;
313 result
<< author
<< ", ";
317 result
<< ", " << media
;
319 result
<< " (" << year
<< ")";
321 result
<< ", " << docLoc
;
323 docstring
const result_str
= rtrim(result
.str());
324 if (!result_str
.empty()) {
325 info_
= convertLaTeXCommands(result_str
);
329 // This should never happen (or at least be very unusual!)
330 static docstring e
= docstring();
335 docstring
const & BibTeXInfo::operator[](docstring
const & field
) const
337 BibTeXInfo::const_iterator it
= find(field
);
340 static docstring
const empty_value
= docstring();
345 docstring
const & BibTeXInfo::operator[](string
const & field
) const
347 return operator[](from_ascii(field
));
351 docstring
BibTeXInfo::getValueForKey(string
const & key
,
352 BibTeXInfo
const * const xref
) const
354 docstring
const ret
= operator[](key
);
355 if (!ret
.empty() || !xref
)
361 //////////////////////////////////////////////////////////////////////
365 //////////////////////////////////////////////////////////////////////
368 // A functor for use with sort, leading to case insensitive sorting
369 class compareNoCase
: public binary_function
<docstring
, docstring
, bool>
372 bool operator()(docstring
const & s1
, docstring
const & s2
) const {
373 return compare_no_case(s1
, s2
) < 0;
379 vector
<docstring
> const BiblioInfo::getKeys() const
381 vector
<docstring
> bibkeys
;
382 BiblioInfo::const_iterator it
= begin();
383 for (; it
!= end(); ++it
)
384 bibkeys
.push_back(it
->first
);
385 sort(bibkeys
.begin(), bibkeys
.end(), compareNoCase());
390 vector
<docstring
> const BiblioInfo::getFields() const
392 vector
<docstring
> bibfields
;
393 set
<docstring
>::const_iterator it
= field_names_
.begin();
394 set
<docstring
>::const_iterator end
= field_names_
.end();
395 for (; it
!= end
; ++it
)
396 bibfields
.push_back(*it
);
397 sort(bibfields
.begin(), bibfields
.end());
402 vector
<docstring
> const BiblioInfo::getEntries() const
404 vector
<docstring
> bibentries
;
405 set
<docstring
>::const_iterator it
= entry_types_
.begin();
406 set
<docstring
>::const_iterator end
= entry_types_
.end();
407 for (; it
!= end
; ++it
)
408 bibentries
.push_back(*it
);
409 sort(bibentries
.begin(), bibentries
.end());
414 docstring
const BiblioInfo::getAbbreviatedAuthor(docstring
const & key
) const
416 BiblioInfo::const_iterator it
= find(key
);
419 BibTeXInfo
const & data
= it
->second
;
420 return data
.getAbbreviatedAuthor();
424 docstring
const BiblioInfo::getYear(docstring
const & key
) const
426 BiblioInfo::const_iterator it
= find(key
);
429 BibTeXInfo
const & data
= it
->second
;
430 docstring year
= data
.getYear();
433 // let's try the crossref
434 docstring
const xref
= data
.getXRef();
436 return _("No year"); // no luck
437 BiblioInfo::const_iterator
const xrefit
= find(xref
);
439 return _("No year"); // no luck again
440 BibTeXInfo
const & xref_data
= xrefit
->second
;
441 return xref_data
.getYear();
442 return data
.getYear();
446 docstring
const BiblioInfo::getInfo(docstring
const & key
) const
448 BiblioInfo::const_iterator it
= find(key
);
451 BibTeXInfo
const & data
= it
->second
;
452 BibTeXInfo
const * xrefptr
= 0;
453 docstring
const xref
= data
.getXRef();
455 BiblioInfo::const_iterator
const xrefit
= find(xref
);
457 xrefptr
= &(xrefit
->second
);
459 return data
.getInfo(xrefptr
);
463 vector
<docstring
> const BiblioInfo::getCiteStrings(
464 docstring
const & key
, Buffer
const & buf
) const
466 CiteEngine
const engine
= buf
.params().citeEngine();
467 if (engine
== ENGINE_BASIC
|| engine
== ENGINE_NATBIB_NUMERICAL
)
468 return getNumericalStrings(key
, buf
);
470 return getAuthorYearStrings(key
, buf
);
474 vector
<docstring
> const BiblioInfo::getNumericalStrings(
475 docstring
const & key
, Buffer
const & buf
) const
478 return vector
<docstring
>();
480 docstring
const author
= getAbbreviatedAuthor(key
);
481 docstring
const year
= getYear(key
);
482 if (author
.empty() || year
.empty())
483 return vector
<docstring
>();
485 vector
<CiteStyle
> const & styles
= citeStyles(buf
.params().citeEngine());
487 vector
<docstring
> vec(styles
.size());
488 for (size_t i
= 0; i
!= vec
.size(); ++i
) {
494 str
= from_ascii("[#ID]");
498 str
= _("Add to bibliography only.");
502 str
= author
+ " [#ID]";
506 str
= author
+ " #ID";
510 str
= from_ascii("#ID");
522 str
= '(' + year
+ ')';
533 vector
<docstring
> const BiblioInfo::getAuthorYearStrings(
534 docstring
const & key
, Buffer
const & buf
) const
537 return vector
<docstring
>();
539 docstring
const author
= getAbbreviatedAuthor(key
);
540 docstring
const year
= getYear(key
);
541 if (author
.empty() || year
.empty())
542 return vector
<docstring
>();
544 vector
<CiteStyle
> const & styles
= citeStyles(buf
.params().citeEngine());
546 vector
<docstring
> vec(styles
.size());
547 for (size_t i
= 0; i
!= vec
.size(); ++i
) {
552 // jurabib only: Author/Annotator
553 // (i.e. the "before" field, 2nd opt arg)
554 str
= author
+ "/<" + _("before") + '>';
558 str
= _("Add to bibliography only.");
562 str
= author
+ " (" + year
+ ')';
566 str
= '(' + author
+ ", " + year
+ ')';
570 str
= author
+ ' ' + year
;
574 str
= author
+ ", " + year
;
586 str
= '(' + year
+ ')';
595 void BiblioInfo::mergeBiblioInfo(BiblioInfo
const & info
)
597 bimap_
.insert(info
.begin(), info
.end());
601 //////////////////////////////////////////////////////////////////////
605 //////////////////////////////////////////////////////////////////////
610 char const * const citeCommands
[] = {
611 "cite", "citet", "citep", "citealt", "citealp",
612 "citeauthor", "citeyear", "citeyearpar", "nocite" };
614 unsigned int const nCiteCommands
=
615 sizeof(citeCommands
) / sizeof(char *);
617 CiteStyle
const citeStylesArray
[] = {
618 CITE
, CITET
, CITEP
, CITEALT
, CITEALP
,
619 CITEAUTHOR
, CITEYEAR
, CITEYEARPAR
, NOCITE
};
621 unsigned int const nCiteStyles
=
622 sizeof(citeStylesArray
) / sizeof(CiteStyle
);
624 CiteStyle
const citeStylesFull
[] = {
625 CITET
, CITEP
, CITEALT
, CITEALP
, CITEAUTHOR
};
627 unsigned int const nCiteStylesFull
=
628 sizeof(citeStylesFull
) / sizeof(CiteStyle
);
630 CiteStyle
const citeStylesUCase
[] = {
631 CITET
, CITEP
, CITEALT
, CITEALP
, CITEAUTHOR
};
633 unsigned int const nCiteStylesUCase
=
634 sizeof(citeStylesUCase
) / sizeof(CiteStyle
);
639 CitationStyle
citationStyleFromString(string
const & command
)
645 string cmd
= command
;
647 s
.forceUpperCase
= true;
651 size_t const n
= cmd
.size() - 1;
652 if (cmd
!= "cite" && cmd
[n
] == '*') {
654 cmd
= cmd
.substr(0, n
);
657 char const * const * const last
= citeCommands
+ nCiteCommands
;
658 char const * const * const ptr
= find(citeCommands
, last
, cmd
);
661 size_t idx
= ptr
- citeCommands
;
662 s
.style
= citeStylesArray
[idx
];
668 string
citationStyleToString(const CitationStyle
& s
)
670 string cite
= citeCommands
[s
.style
];
672 CiteStyle
const * last
= citeStylesFull
+ nCiteStylesFull
;
673 if (std::find(citeStylesFull
, last
, s
.style
) != last
)
677 if (s
.forceUpperCase
) {
678 CiteStyle
const * last
= citeStylesUCase
+ nCiteStylesUCase
;
679 if (std::find(citeStylesUCase
, last
, s
.style
) != last
)
686 vector
<CiteStyle
> citeStyles(CiteEngine engine
)
688 unsigned int nStyles
= 0;
689 unsigned int start
= 0;
696 case ENGINE_NATBIB_AUTHORYEAR
:
697 case ENGINE_NATBIB_NUMERICAL
:
698 nStyles
= nCiteStyles
- 1;
702 nStyles
= nCiteStyles
;
707 vector
<CiteStyle
> styles(nStyles
);
710 for (; i
!= styles
.size(); ++i
, ++j
)
711 styles
[i
] = citeStylesArray
[j
];