#include "BiblioInfo.h"
#include "Buffer.h"
#include "BufferParams.h"
-#include "buffer_funcs.h"
#include "Citation.h"
#include "Encoding.h"
-#include "InsetIterator.h"
#include "Language.h"
-#include "output_xhtml.h"
-#include "Paragraph.h"
+#include "xml.h"
#include "TextClass.h"
#include "TocBackend.h"
#include "support/convert.h"
#include "support/debug.h"
#include "support/docstream.h"
+#include "support/FileName.h"
#include "support/gettext.h"
#include "support/lassert.h"
#include "support/lstrings.h"
}
-docstring constructName(docstring const & name, string const scheme)
+docstring constructName(docstring const & name, string const & scheme)
{
// re-constructs a name from name parts according
// to a given scheme
}
-bool multipleAuthors(docstring const author)
+bool multipleAuthors(docstring const & author)
{
return getAuthors(author).size() > 1;
}
//////////////////////////////////////////////////////////////////////
BibTeXInfo::BibTeXInfo(docstring const & key, docstring const & type)
- : is_bibtex_(true), bib_key_(key), entry_type_(type), info_(),
- modifier_(0)
+ : is_bibtex_(true), bib_key_(key), num_bib_key_(0), entry_type_(type),
+ info_(), format_(), modifier_(0)
{}
// in this case, we didn't find a "(",
// so we don't have author (year)
return docstring();
+ if (full) {
+ // Natbib syntax is "Jones et al.(1990)Jones, Baker, and Williams"
+ docstring const fullauthors = trim(rsplit(remainder, ')'));
+ if (!fullauthors.empty())
+ return fullauthors;
+ }
return authors;
}
// These are defined in the styles
string const etal =
- buf ? buf->params().documentClass().getCiteMacro(engine_type, "_etal")
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_etal")
: " et al.";
string const namesep =
- buf ? buf->params().documentClass().getCiteMacro(engine_type, "_namesep")
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_namesep")
: ", ";
string const lastnamesep =
- buf ? buf->params().documentClass().getCiteMacro(engine_type, "_lastnamesep")
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_lastnamesep")
: ", and ";
string const pairnamesep =
- buf ? buf->params().documentClass().getCiteMacro(engine_type, "_pairnamesep")
+ buf ? buf->params().documentClass().getCiteMacro(engine_type, "B_pairnamesep")
: " and ";
string firstnameform =
buf ? buf->params().documentClass().getCiteMacro(engine_type, "!firstnameform")
}
+void BibTeXInfo::getLocators(docstring & doi, docstring & url, docstring & file) const
+{
+ if (is_bibtex_) {
+ // get "doi" entry from citation record
+ doi = operator[]("doi");
+ if (!doi.empty() && !prefixIs(doi,from_ascii("http")))
+ doi = "https://doi.org/" + doi;
+ // get "url" entry from citation record
+ url = operator[]("url");
+ // get "file" entry from citation record
+ file = operator[]("file");
+
+ // Jabref case, field has a format:
+ // Description:Location:Filetype;Description:Location:Filetype...
+ // We will grab only first pdf
+ if (!file.empty()) {
+ docstring ret, filedest, tmp;
+ ret = split(file, tmp, ':');
+ tmp = split(ret, filedest, ':');
+ //TODO howto deal with relative directories?
+ FileName f(to_utf8(filedest));
+ if (f.exists())
+ file = "file:///" + filedest;
+ }
+
+ // kbibtex case, format:
+ // file1.pdf;file2.pdf
+ // We will grab only first pdf
+ docstring kfile;
+ if (file.empty())
+ kfile = operator[]("localfile");
+ if (!kfile.empty()) {
+ docstring filedest, tmp;
+ tmp = split(kfile, filedest, ';');
+ //TODO howto deal with relative directories?
+ FileName f(to_utf8(filedest));
+ if (f.exists())
+ file = "file:///" + filedest;
+ }
+
+ if (!url.empty())
+ return;
+
+ // try biblatex specific fields, see its manual
+ // 3.13.7 "Electronic Publishing Informationl"
+ docstring eprinttype = operator[]("eprinttype");
+ docstring eprint = operator[]("eprint");
+ if (eprint.empty())
+ return;
+
+ if (eprinttype == "arxiv")
+ url = "https://arxiv.org/abs/" + eprint;
+ if (eprinttype == "jstor")
+ url = "https://www.jstor.org/stable/" + eprint;
+ if (eprinttype == "pubmed")
+ url = "http://www.ncbi.nlm.nih.gov/pubmed/" + eprint;
+ if (eprinttype == "hdl")
+ url = "https://hdl.handle.net/" + eprint;
+ if (eprinttype == "googlebooks")
+ url = "http://books.google.com/books?id=" + eprint;
+
+ return;
+ }
+
+ // Here can be handled the bibliography environment. All one could do
+ // here is let LyX scan the entry for URL or HRef insets.
+}
+
+
namespace {
docstring parseOptions(docstring const & format, string & optkey,
bit of work, however.
*/
docstring BibTeXInfo::expandFormat(docstring const & format,
- BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
+ BibTeXInfoList const & xrefs, int & counter, Buffer const & buf,
CiteItem const & ci, bool next, bool second) const
{
// incorrect use of macros could put us in an infinite loop
fmt = from_utf8(val) + fmt.substr(1);
counter += 1;
continue;
- } else if (key[0] == '_') {
- // a translatable bit
+ } else if (prefixIs(key, "B_")) {
+ // a translatable bit (to the Buffer language)
string const val =
buf.params().documentClass().getCiteMacro(engine_type, key);
docstring const trans =
translateIfPossible(from_utf8(val), buf.params().language->code());
ret << trans;
+ } else if (key[0] == '_') {
+ // a translatable bit (to the GUI language)
+ string const val =
+ buf.params().documentClass().getCiteMacro(engine_type, key);
+ docstring const trans =
+ translateIfPossible(from_utf8(val));
+ ret << trans;
} else {
docstring const val =
getValueForKey(key, buf, ci, xrefs, max_keysize);
}
-docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
- Buffer const & buf, CiteItem const & ci) const
+docstring const & BibTeXInfo::getInfo(BibTeXInfoList const & xrefs,
+ Buffer const & buf, CiteItem const & ci, docstring const & format_in) const
{
bool const richtext = ci.richtext;
- if (!richtext && !info_.empty())
+ CiteEngineType const engine_type = buf.params().citeEngineType();
+ DocumentClass const & dc = buf.params().documentClass();
+ docstring const & format = format_in.empty()?
+ from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)))
+ : format_in;
+
+ if (format != format_) {
+ // clear caches since format changed
+ info_.clear();
+ info_richtext_.clear();
+ format_ = format;
+ }
+
+ if (!richtext && !info_.empty()) {
+ info_ = convertLaTeXCommands(processRichtext(info_, false));
return info_;
+ }
if (richtext && !info_richtext_.empty())
return info_richtext_;
return info_;
}
- CiteEngineType const engine_type = buf.params().citeEngineType();
- DocumentClass const & dc = buf.params().documentClass();
- docstring const & format =
- from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
int counter = 0;
info_ = expandFormat(format, xrefs, counter, buf,
ci, false, false);
}
-docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
+docstring const BibTeXInfo::getLabel(BibTeXInfoList const & xrefs,
Buffer const & buf, docstring const & format,
CiteItem const & ci, bool next, bool second) const
{
docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
- CiteItem const & ci, BibTeXInfoList const xrefs, size_t maxsize) const
+ CiteItem const & ci, BibTeXInfoList const & xrefs, size_t maxsize) const
{
// anything less is pointless
LASSERT(maxsize >= 16, maxsize = 16);
docstring ret = operator[](key);
if (ret.empty() && !xrefs.empty()) {
- vector<BibTeXInfo const *>::const_iterator it = xrefs.begin();
- vector<BibTeXInfo const *>::const_iterator en = xrefs.end();
- for (; it != en; ++it) {
- if (*it && !(**it)[key].empty()) {
- ret = (**it)[key];
+ // xr is a (reference to a) BibTeXInfo const *
+ for (auto const & xr : xrefs) {
+ if (xr && !(*xr)[key].empty()) {
+ ret = (*xr)[key];
break;
}
}
ret = ci.textBefore;
else if (key == "textafter")
ret = ci.textAfter;
- else if (key == "curpretext")
- ret = ci.getPretexts()[bib_key_];
- else if (key == "curposttext")
- ret = ci.getPosttexts()[bib_key_];
- else if (key == "year")
+ else if (key == "curpretext") {
+ vector<pair<docstring, docstring>> pres = ci.getPretexts();
+ vector<pair<docstring, docstring>>::iterator it = pres.begin();
+ int numkey = 1;
+ for (; it != pres.end() ; ++it) {
+ if ((*it).first == bib_key_ && numkey == num_bib_key_) {
+ ret = (*it).second;
+ pres.erase(it);
+ break;
+ }
+ if ((*it).first == bib_key_)
+ ++numkey;
+ }
+ } else if (key == "curposttext") {
+ vector<pair<docstring, docstring>> posts = ci.getPosttexts();
+ vector<pair<docstring, docstring>>::iterator it = posts.begin();
+ int numkey = 1;
+ for (; it != posts.end() ; ++it) {
+ if ((*it).first == bib_key_ && numkey == num_bib_key_) {
+ ret = (*it).second;
+ posts.erase(it);
+ break;
+ }
+ if ((*it).first == bib_key_)
+ ++numkey;
+ }
+ } else if (key == "year")
ret = getYear();
}
if (cleanit)
- ret = html::cleanAttr(ret);
+ ret = xml::cleanAttr(ret);
// make sure it is not too big
support::truncateWithEllipsis(ret, maxsize);
namespace {
// A functor for use with sort, leading to case insensitive sorting
-class compareNoCase: public binary_function<docstring, docstring, bool>
-{
-public:
- bool operator()(docstring const & s1, docstring const & s2) const {
- return compare_no_case(s1, s2) < 0;
- }
-};
+bool compareNoCase(const docstring & a, const docstring & b) {
+ return compare_no_case(a, b) < 0;
+}
} // namespace
// XData field can consist of a comma-separated list of keys
vector<docstring> const xdatakeys = getVectorFromString(data["xdata"]);
if (!xdatakeys.empty()) {
- vector<docstring>::const_iterator xit = xdatakeys.begin();
- vector<docstring>::const_iterator xen = xdatakeys.end();
- for (; xit != xen; ++xit) {
- docstring const xdatakey = *xit;
+ for (auto const & xdatakey : xdatakeys) {
result.push_back(xdatakey);
BiblioInfo::const_iterator it = find(xdatakey);
if (it != end()) {
vector<docstring> const BiblioInfo::getKeys() const
{
vector<docstring> bibkeys;
- BiblioInfo::const_iterator it = begin();
- for (; it != end(); ++it)
- bibkeys.push_back(it->first);
- sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
+ for (auto const & bi : *this)
+ bibkeys.push_back(bi.first);
+ sort(bibkeys.begin(), bibkeys.end(), &compareNoCase);
return bibkeys;
}
vector<docstring> const BiblioInfo::getFields() const
{
vector<docstring> bibfields;
- set<docstring>::const_iterator it = field_names_.begin();
- set<docstring>::const_iterator end = field_names_.end();
- for (; it != end; ++it)
- bibfields.push_back(*it);
+ for (auto const & fn : field_names_)
+ bibfields.push_back(fn);
sort(bibfields.begin(), bibfields.end());
return bibfields;
}
vector<docstring> const BiblioInfo::getEntries() const
{
vector<docstring> bibentries;
- set<docstring>::const_iterator it = entry_types_.begin();
- set<docstring>::const_iterator end = entry_types_.end();
- for (; it != end; ++it)
- bibentries.push_back(*it);
+ for (auto const & et : entry_types_)
+ bibentries.push_back(et);
sort(bibentries.begin(), bibentries.end());
return bibentries;
}
return data.citeNumber();
}
+void BiblioInfo::getLocators(docstring const & key, docstring & doi, docstring & url, docstring & file) const
+{
+ BiblioInfo::const_iterator it = find(key);
+ if (it == end())
+ return;
+ BibTeXInfo const & data = it->second;
+ data.getLocators(doi,url,file);
+}
+
docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const
{
docstring const BiblioInfo::getInfo(docstring const & key,
- Buffer const & buf, CiteItem const & ci) const
+ Buffer const & buf, CiteItem const & ci, docstring const & format) const
{
BiblioInfo::const_iterator it = find(key);
if (it == end())
return docstring(_("Bibliography entry not found!"));
BibTeXInfo const & data = it->second;
BibTeXInfoList xrefptrs;
- vector<docstring> const xrefs = getXRefs(data);
for (docstring const & xref : getXRefs(data)) {
BiblioInfo::const_iterator const xrefit = find(xref);
if (xrefit != end())
xrefptrs.push_back(&(xrefit->second));
}
- return data.getInfo(xrefptrs, buf, ci);
+ return data.getInfo(xrefptrs, buf, ci, format);
}
LASSERT(max_size >= 16, max_size = 16);
// we can't display more than 10 of these, anyway
+ // but since we truncate in the middle,
+ // we need to split into two halfs.
bool const too_many_keys = keys.size() > 10;
- if (too_many_keys)
- keys.resize(10);
+ vector<docstring> lkeys;
+ if (too_many_keys) {
+ lkeys.insert(lkeys.end(), keys.end() - 5, keys.end());
+ keys.resize(5);
+ keys.insert(keys.end(), lkeys.begin(), lkeys.end());
+ }
CiteEngineType const engine_type = buf.params().citeEngineType();
DocumentClass const & dc = buf.params().documentClass();
docstring ret = format;
vector<docstring>::const_iterator key = keys.begin();
vector<docstring>::const_iterator ken = keys.end();
+ vector<docstring> handled_keys;
for (int i = 0; key != ken; ++key, ++i) {
+ handled_keys.push_back(*key);
+ int n = 0;
+ for (auto const & k : handled_keys) {
+ if (k == *key)
+ ++n;
+ }
BiblioInfo::const_iterator it = find(*key);
BibTeXInfo empty_data;
empty_data.key(*key);
xrefptrs.push_back(&(xrefit->second));
}
}
+ data.numKey(n);
ret = data.getLabel(xrefptrs, buf, ret, ci, key + 1 != ken, i == 1);
}
- if (too_many_keys)
- ret.push_back(0x2026);//HORIZONTAL ELLIPSIS
- support::truncateWithEllipsis(ret, max_size);
+ support::truncateWithEllipsis(ret, max_size, true);
+
return ret;
}
// FIXME We may want to collect these differently, in the first case,
// so that we might have them in order of appearance.
set<docstring> citekeys;
- shared_ptr<Toc const> toc = buf.tocBackend().toc("citation");
- Toc::const_iterator it = toc->begin();
- Toc::const_iterator const en = toc->end();
- for (; it != en; ++it) {
- if (it->str().empty())
+ Toc const & toc = *buf.tocBackend().toc("citation");
+ for (auto const & t : toc) {
+ if (t.str().empty())
continue;
- vector<docstring> const keys = getVectorFromString(it->str());
+ vector<docstring> const keys = getVectorFromString(t.str());
citekeys.insert(keys.begin(), keys.end());
}
if (citekeys.empty())
// We will now convert it to a list of the BibTeXInfo objects used in
// this document...
vector<BibTeXInfo const *> bi;
- set<docstring>::const_iterator cit = citekeys.begin();
- set<docstring>::const_iterator const cen = citekeys.end();
- for (; cit != cen; ++cit) {
- BiblioInfo::const_iterator const bt = find(*cit);
+ for (auto const & ck : citekeys) {
+ BiblioInfo::const_iterator const bt = find(ck);
if (bt == end() || !bt->second.isBibTeX())
continue;
bi.push_back(&(bt->second));
sort(bi.begin(), bi.end(), lSorter);
// Now we can write the sorted keys
- vector<BibTeXInfo const *>::const_iterator bit = bi.begin();
- vector<BibTeXInfo const *>::const_iterator ben = bi.end();
- for (; bit != ben; ++bit)
- cited_entries_.push_back((*bit)->key());
+ // b is a BibTeXInfo const *
+ for (auto const & b : bi)
+ cited_entries_.push_back(b->key());
}
// modifiers, like "1984a"
map<docstring, BibTeXInfo>::iterator last = bimap_.end();
- vector<docstring>::const_iterator it = cited_entries_.begin();
- vector<docstring>::const_iterator const en = cited_entries_.end();
- for (; it != en; ++it) {
- map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
+ // add letters to years
+ for (auto const & ce : cited_entries_) {
+ map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(ce);
// this shouldn't happen, but...
if (biit == bimap_.end())
// ...fail gracefully, anyway.
}
}
// Set the labels
- it = cited_entries_.begin();
- for (; it != en; ++it) {
- map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(*it);
+ for (auto const & ce : cited_entries_) {
+ map<docstring, BibTeXInfo>::iterator const biit = bimap_.find(ce);
// this shouldn't happen, but...
if (biit == bimap_.end())
// ...fail gracefully, anyway.
docstring const auth = entry.getAuthorOrEditorList(&buf, false);
// we do it this way so as to access the xref, if necessary
// note that this also gives us the modifier
- docstring const year = getYear(*it, buf, true);
+ docstring const year = getYear(ce, buf, true);
if (!auth.empty() && !year.empty())
entry.label(auth + ' ' + year);
else
return cmd;
}
+
+docstring authorsToDocBookAuthorGroup(docstring const & authorsString, XMLStream & xs, Buffer const & buf)
+{
+ // This function closely mimics getAuthorList, but produces DocBook instead of text.
+ // It has been greatly simplified, as the complete list of authors is always produced. No separators are required,
+ // as the output has a database-like shape.
+ // constructName has also been merged within, as it becomes really simple and leads to no copy-paste.
+
+ if (authorsString.empty()) {
+ return docstring();
+ }
+
+ // Split the input list of authors into individual authors.
+ vector<docstring> const authors = getAuthors(authorsString);
+
+ // Retrieve the "et al." variation.
+ string const etal = buf.params().documentClass().getCiteMacro(buf.params().citeEngineType(), "_etal");
+
+ // Output the list of authors.
+ xs << xml::StartTag("authorgroup");
+ xs << xml::CR();
+
+ auto it = authors.cbegin();
+ auto en = authors.cend();
+ for (size_t i = 0; it != en; ++it, ++i) {
+ xs << xml::StartTag("author");
+ xs << xml::CR();
+ xs << xml::StartTag("personname");
+ xs << xml::CR();
+ docstring name = *it;
+
+ // All authors go in a <personname>. If more structure is known, use it; otherwise (just "et al."), print it as such.
+ if (name == "others") {
+ xs << buf.B_(etal);
+ } else {
+ name_parts parts = nameParts(name);
+ if (! parts.prefix.empty()) {
+ xs << xml::StartTag("honorific");
+ xs << parts.prefix;
+ xs << xml::EndTag("honorific");
+ xs << xml::CR();
+ }
+ if (! parts.prename.empty()) {
+ xs << xml::StartTag("firstname");
+ xs << parts.prename;
+ xs << xml::EndTag("firstname");
+ xs << xml::CR();
+ }
+ if (! parts.surname.empty()) {
+ xs << xml::StartTag("surname");
+ xs << parts.surname;
+ xs << xml::EndTag("surname");
+ xs << xml::CR();
+ }
+ if (! parts.suffix.empty()) {
+ xs << xml::StartTag("othername", "role=\"suffix\"");
+ xs << parts.suffix;
+ xs << xml::EndTag("othername");
+ xs << xml::CR();
+ }
+ }
+
+ xs << xml::EndTag("personname");
+ xs << xml::CR();
+ xs << xml::EndTag("author");
+ xs << xml::CR();
+
+ // Could add an affiliation after <personname>, but not stored in BibTeX.
+ }
+ xs << xml::EndTag("authorgroup");
+ xs << xml::CR();
+
+ return docstring();
+}
+
} // namespace lyx