commit 2c4673af58f6ace696f975bc9774824fe78465c8
Author: Juergen Spitzmueller <sp...@lyx.org>
Date:   Sun Sep 18 12:44:12 2016 +0200

    Improve info display for biblatex databases, part II
    
    In addition to the classic crossref, biblatex introduces xdata
    references in order to source-out common data of entries. Entries
    that have "xdata = {somekey}" just inherit all fields from the
    respective @xdata entry, if the field is not already defined in
    the entry itself (just like crossref, with the exception that @xdata
    entries themselves are _never_ output on their own). @xdata entries can
    themselves inherit to other @xdata entries (ad infinitum). So you can,
    for instance, setup an xdata entry for a book series with series name
    that inherits an xdata entry with information of the publisher
    (publisher, address). Any book of that series would just need to refer
    to the series xdata and add the number.
    
    BiblioInfo now checks, in addition to crossrefs, for such xdata
    references and inherits missing fields.
    
    Nte that biblatex also introduces an "xref" field as an alternative to
    crossref. We must not care about that, since the point of xref is that
    it does not inherit fields from the target (just cites that one if a
    given number of refs to it exist)
---
 src/BiblioInfo.cpp |  126 ++++++++++++++++++++++++++++++++++------------------
 src/BiblioInfo.h   |   37 +++++++++------
 2 files changed, 105 insertions(+), 58 deletions(-)

diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp
index c85eabd..421d717 100644
--- a/src/BiblioInfo.cpp
+++ b/src/BiblioInfo.cpp
@@ -352,14 +352,6 @@ docstring const BibTeXInfo::getYear() const
 }
 
 
-docstring const BibTeXInfo::getXRef() const
-{
-       if (!is_bibtex_)
-               return docstring();
-       return operator[]("crossref");
-}
-
-
 namespace {
 
 docstring parseOptions(docstring const & format, string & optkey,
@@ -485,7 +477,7 @@ I can tell, but it still feels like a hack. Fixing this 
would require quite a
 bit of work, however.
 */
 docstring BibTeXInfo::expandFormat(docstring const & format,
-               BibTeXInfo const * const xref, int & counter, Buffer const & 
buf,
+               BibTeXInfoList const xrefs, int & counter, Buffer const & buf,
                docstring before, docstring after, docstring dialog, bool next) 
const
 {
        // incorrect use of macros could put us in an infinite loop
@@ -534,7 +526,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
                                        ret << trans;
                                } else {
                                        docstring const val =
-                                               getValueForKey(key, buf, 
before, after, dialog, xref, max_keysize);
+                                               getValueForKey(key, buf, 
before, after, dialog, xrefs, max_keysize);
                                        if (!scanning_rich)
                                                ret << from_ascii("{!<span 
class=\"bib-" + key + "\">!}");
                                        ret << val;
@@ -565,16 +557,16 @@ docstring BibTeXInfo::expandFormat(docstring const & 
format,
                                                return _("ERROR!");
                                        fmt = newfmt;
                                        docstring const val =
-                                               getValueForKey(optkey, buf, 
before, after, dialog, xref);
+                                               getValueForKey(optkey, buf, 
before, after, dialog, xrefs);
                                        if (optkey == "next" && next)
                                                ret << ifpart; // without 
expansion
                                        else if (!val.empty()) {
                                                int newcounter = 0;
-                                               ret << expandFormat(ifpart, 
xref, newcounter, buf,
+                                               ret << expandFormat(ifpart, 
xrefs, newcounter, buf,
                                                        before, after, dialog, 
next);
                                        } else if (!elsepart.empty()) {
                                                int newcounter = 0;
-                                               ret << expandFormat(elsepart, 
xref, newcounter, buf,
+                                               ret << expandFormat(elsepart, 
xrefs, newcounter, buf,
                                                        before, after, dialog, 
next);
                                        }
                                        // fmt will have been shortened for us 
already
@@ -623,7 +615,7 @@ docstring BibTeXInfo::expandFormat(docstring const & format,
 }
 
 
-docstring const & BibTeXInfo::getInfo(BibTeXInfo const * const xref,
+docstring const & BibTeXInfo::getInfo(BibTeXInfoList const xrefs,
        Buffer const & buf, bool richtext) const
 {
        if (!richtext && !info_.empty())
@@ -642,7 +634,7 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfo const * 
const xref,
        docstring const & format =
                from_utf8(dc.getCiteFormat(engine_type, to_utf8(entry_type_)));
        int counter = 0;
-       info_ = expandFormat(format, xref, counter, buf,
+       info_ = expandFormat(format, xrefs, counter, buf,
                docstring(), docstring(), docstring(), false);
 
        if (info_.empty()) {
@@ -660,7 +652,7 @@ docstring const & BibTeXInfo::getInfo(BibTeXInfo const * 
const xref,
 }
 
 
-docstring const BibTeXInfo::getLabel(BibTeXInfo const * const xref,
+docstring const BibTeXInfo::getLabel(BibTeXInfoList const xrefs,
        Buffer const & buf, docstring const & format, bool richtext,
        docstring const & before, docstring const & after, 
        docstring const & dialog, bool next) const
@@ -668,7 +660,7 @@ docstring const BibTeXInfo::getLabel(BibTeXInfo const * 
const xref,
        docstring loclabel;
 
        int counter = 0;
-       loclabel = expandFormat(format, xref, counter, buf,
+       loclabel = expandFormat(format, xrefs, counter, buf,
                before, after, dialog, next);
 
        if (!loclabel.empty() && !next) {
@@ -698,7 +690,7 @@ docstring const & BibTeXInfo::operator[](string const & 
field) const
 
 docstring BibTeXInfo::getValueForKey(string const & oldkey, Buffer const & buf,
        docstring const & before, docstring const & after, docstring const & 
dialog,
-       BibTeXInfo const * const xref, size_t maxsize) const
+       BibTeXInfoList const xrefs, size_t maxsize) const
 {
        // anything less is pointless
        LASSERT(maxsize >= 16, maxsize = 16);
@@ -710,8 +702,16 @@ docstring BibTeXInfo::getValueForKey(string const & 
oldkey, Buffer const & buf,
        }
 
        docstring ret = operator[](key);
-       if (ret.empty() && xref)
-               ret = (*xref)[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];
+                               break;
+                       }
+               }
+       }
        if (ret.empty()) {
                // some special keys
                // FIXME: dialog, textbefore and textafter have nothing to do 
with this
@@ -751,7 +751,7 @@ docstring BibTeXInfo::getValueForKey(string const & oldkey, 
Buffer const & buf,
                        docstring const & format =
                                from_utf8(dc.getCiteFormat(engine_type, 
to_utf8(entry_type_)));
                        int counter = 0;
-                       ret = expandFormat(format, xref, counter, buf,
+                       ret = expandFormat(format, xrefs, counter, buf,
                                docstring(), docstring(), docstring(), false);
                } else if (key == "textbefore")
                        ret = before;
@@ -790,6 +790,30 @@ public:
 } // namespace anon
 
 
+vector<docstring> const BiblioInfo::getXRefs(BibTeXInfo const & data, bool 
const nested) const
+{
+       vector<docstring> result;
+       if (!data.isBibTeX())
+               return result;
+       // Legacy crossref field. This is not nestable.
+       if (!nested && !data["crossref"].empty())
+               result.push_back(data["crossref"]);
+       // Biblatex's xdata field. Infinitely nestable.
+       docstring const xdatakey = data["xdata"];
+       if (!xdatakey.empty()) {
+               result.push_back(xdatakey);
+               BiblioInfo::const_iterator it = find(xdatakey);
+               if (it != end()) {
+                       BibTeXInfo const & xdata = it->second;
+                       vector<docstring> const nxdata = getXRefs(xdata, true);
+                       if (!nxdata.empty())
+                               result.insert(result.end(), nxdata.begin(), 
nxdata.end());
+               }
+       }
+       return result;
+}
+
+
 vector<docstring> const BiblioInfo::getKeys() const
 {
        vector<docstring> bibkeys;
@@ -853,17 +877,23 @@ docstring const BiblioInfo::getYear(docstring const & 
key, bool use_modifier) co
        BibTeXInfo const & data = it->second;
        docstring year = data.getYear();
        if (year.empty()) {
-               // let's try the crossref
-               docstring const xref = data.getXRef();
-               if (xref.empty())
+               // let's try the crossrefs
+               vector<docstring> const xrefs = getXRefs(data);
+               if (xrefs.empty())
                        // no luck
                        return docstring();
-               BiblioInfo::const_iterator const xrefit = find(xref);
-               if (xrefit == end())
-                       // no luck again
-                       return docstring();
-               BibTeXInfo const & xref_data = xrefit->second;
-               year = xref_data.getYear();
+               vector<docstring>::const_iterator it = xrefs.begin();
+               vector<docstring>::const_iterator en = xrefs.end();
+               for (; it != en; ++it) {
+                       BiblioInfo::const_iterator const xrefit = find(*it);
+                       if (xrefit == end())
+                               continue;
+                       BibTeXInfo const & xref_data = xrefit->second;
+                       year = xref_data.getYear();
+                       if (!year.empty())
+                               // success!
+                               break;
+               }
        }
        if (use_modifier && data.modifier() != 0)
                year += data.modifier();
@@ -887,14 +917,18 @@ docstring const BiblioInfo::getInfo(docstring const & key,
        if (it == end())
                return docstring(_("Bibliography entry not found!"));
        BibTeXInfo const & data = it->second;
-       BibTeXInfo const * xrefptr = 0;
-       docstring const xref = data.getXRef();
-       if (!xref.empty()) {
-               BiblioInfo::const_iterator const xrefit = find(xref);
-               if (xrefit != end())
-                       xrefptr = &(xrefit->second);
+       BibTeXInfoList xrefptrs;
+       vector<docstring> const xrefs = getXRefs(data);
+       if (!xrefs.empty()) {
+               vector<docstring>::const_iterator it = xrefs.begin();
+               vector<docstring>::const_iterator en = xrefs.end();
+               for (; it != en; ++it) {
+                       BiblioInfo::const_iterator const xrefit = find(*it);
+                       if (xrefit != end())
+                               xrefptrs.push_back(&(xrefit->second));
+               }
        }
-       return data.getInfo(xrefptr, buf, richtext);
+       return data.getInfo(xrefptrs, buf, richtext);
 }
 
 
@@ -922,17 +956,21 @@ docstring const BiblioInfo::getLabel(vector<docstring> 
keys,
                BibTeXInfo empty_data;
                empty_data.key(*key);
                BibTeXInfo & data = empty_data;
-               BibTeXInfo const * xrefptr = 0;
+               vector<BibTeXInfo const *> xrefptrs;
                if (it != end()) {
                        data = it->second;
-                       docstring const xref = data.getXRef();
-                       if (!xref.empty()) {
-                               BiblioInfo::const_iterator const xrefit = 
find(xref);
-                               if (xrefit != end())
-                                       xrefptr = &(xrefit->second);
+                       vector<docstring> const xrefs = getXRefs(data);
+                       if (!xrefs.empty()) {
+                               vector<docstring>::const_iterator it = 
xrefs.begin();
+                               vector<docstring>::const_iterator en = 
xrefs.end();
+                               for (; it != en; ++it) {
+                                       BiblioInfo::const_iterator const xrefit 
= find(*it);
+                                       if (xrefit != end())
+                                               
xrefptrs.push_back(&(xrefit->second));
+                               }
                        }
                }
-               ret = data.getLabel(xrefptr, buf, ret, for_xhtml,
+               ret = data.getLabel(xrefptrs, buf, ret, for_xhtml,
                        before, after, dialog, key + 1 != ken);
        }
 
diff --git a/src/BiblioInfo.h b/src/BiblioInfo.h
index 43b949a..5d485c9 100644
--- a/src/BiblioInfo.h
+++ b/src/BiblioInfo.h
@@ -45,6 +45,8 @@ public:
        /// and the values are the associated field values.
        typedef std::map<docstring, docstring>::const_iterator const_iterator;
        ///
+       typedef std::vector<BibTeXInfo const *> const BibTeXInfoList;
+       ///
        BibTeXInfo() : is_bibtex_(true), modifier_(0) {}
        /// argument sets isBibTeX_, so should be false only if it's coming
        /// from a bibliography environment
@@ -58,14 +60,12 @@ public:
            Buffer const * buf = 0, bool jurabib_style = false) const;
        ///
        docstring const getYear() const;
-       ///
-       docstring const getXRef() const;
        /// \return formatted BibTeX data suitable for framing.
-       /// \param pointer to crossref information
-       docstring const & getInfo(BibTeXInfo const * const xref,
+       /// \param vector of pointers to crossref/xdata information
+       docstring const & getInfo(BibTeXInfoList const xrefs,
                        Buffer const & buf, bool richtext) const;
        /// \return formatted BibTeX data for a citation label
-       docstring const getLabel(BibTeXInfo const * const xref,
+       docstring const getLabel(BibTeXInfoList const xrefs,
                Buffer const & buf, docstring const & format, bool richtext,
                const docstring & before, const docstring & after,
                const docstring & dialog, bool next = false) const;
@@ -111,11 +111,11 @@ public:
        bool isBibTeX() const { return is_bibtex_; }
 private:
        /// like operator[], except, if the field is empty, it will attempt
-       /// to get the data from xref BibTeXInfo object, which would normally
-       /// be the one referenced in the crossref field.
+       /// to get the data from xref BibTeXInfo objects, which would normally
+       /// be the one referenced in the crossref or xdata field.
        docstring getValueForKey(std::string const & key, Buffer const & buf,
                docstring const & before, docstring const & after, docstring 
const & dialog,
-               BibTeXInfo const * const xref, size_t maxsize = 4096) const;
+               BibTeXInfoList const xrefs, size_t maxsize = 4096) const;
        /// replace %keys% in a format string with their values
        /// called from getInfo()
        /// format strings may contain:
@@ -133,7 +133,7 @@ private:
        /// moreover, keys that look like "%_key%" are treated as translatable
        /// so that things like "pp." and "vol." can be translated.
        docstring expandFormat(docstring const & fmt,
-               BibTeXInfo const * const xref, int & counter,
+               BibTeXInfoList const xrefs, int & counter,
                Buffer const & buf, docstring before = docstring(),
                docstring after = docstring(), docstring dialog = docstring(),
                bool next = false) const;
@@ -166,8 +166,13 @@ private:
 /// from BibTeX or from bibliography environments.
 class BiblioInfo {
 public:
+       ///
+       typedef std::vector<BibTeXInfo const *> BibTeXInfoList;
        /// bibliography key --> data for that key
        typedef std::map<docstring, BibTeXInfo>::const_iterator const_iterator;
+       /// Get a vector with all external data (crossref, xdata)
+       std::vector<docstring> const getXRefs(BibTeXInfo const & data,
+                                             bool const nested = false) const;
        /// \return a sorted vector of bibliography keys
        std::vector<docstring> const getKeys() const;
        /// \return a sorted vector of present BibTeX fields
@@ -179,15 +184,19 @@ public:
        /// \return the year from the bibtex data record for \param key
        /// if \param use_modifier is true, then we will also append any
        /// modifier for this entry (e.g., 1998b).
-       /// Note that this will get the year from the crossref if it's
-       /// not present in the record itself.
+       /// If no legacy year field is present, check for date (common in
+       /// biblatex) and extract the year from there.
+       /// Note further that this will get the year from the crossref or xdata
+       /// if it's not present in the record itself.
        docstring const getYear(docstring const & key,
                        bool use_modifier = false) const;
        /// \return the year from the bibtex data record for \param key
        /// if \param use_modifier is true, then we will also append any
        /// modifier for this entry (e.g., 1998b).
-       /// Note that this will get the year from the crossref if it's
-       /// not present in the record itself.
+       /// If no legacy year field is present, check for date (common in
+       /// biblatex) and extract the year from there.
+       /// Note further that this will get the year from the crossref or xdata
+       /// if it's not present in the record itself.
        /// If no year is found, \return "No year" translated to the buffer
        /// language.
        docstring const getYear(docstring const & key, Buffer const & buf,
@@ -196,7 +205,7 @@ public:
        docstring const getCiteNumber(docstring const & key) const;
        /// \return formatted BibTeX data associated with a given key.
        /// Empty if no info exists.
-       /// Note that this will retrieve data from the crossref as needed.
+       /// Note that this will retrieve data from the crossref or xdata as 
needed.
        /// If \param richtext is true, then it will output any richtext tags
        /// marked in the citation format and escape < and > elsewhere.
        docstring const getInfo(docstring const & key, Buffer const & buf,

Reply via email to