diff --git a/data/schemata/bcf.rnc b/data/schemata/bcf.rnc index 71e5e26e8..01d3296a7 100644 --- a/data/schemata/bcf.rnc +++ b/data/schemata/bcf.rnc @@ -845,6 +845,7 @@ map = attribute map_field_set { xsd:string {minLength="1"} }?, attribute map_entry_null { "1" }?, attribute map_append { "1" }?, + attribute map_appendstrict { "1" }?, attribute map_final { "1" }?, attribute map_match { xsd:string {minLength="1"} }?, attribute map_matchi { xsd:string {minLength="1"} }?, diff --git a/data/schemata/bcf.rng b/data/schemata/bcf.rng index 8863af1ed..c68b73313 100644 --- a/data/schemata/bcf.rng +++ b/data/schemata/bcf.rng @@ -2187,6 +2187,11 @@ 1 + + + 1 + + 1 diff --git a/data/schemata/config.rnc b/data/schemata/config.rnc index 54bff65db..a37380d60 100644 --- a/data/schemata/config.rnc +++ b/data/schemata/config.rnc @@ -324,6 +324,7 @@ map = attribute map_field_set { xsd:string {minLength="1"} }?, attribute map_entry_null { "1" }?, attribute map_append { "1" }?, + attribute map_appendstrict { "1" }?, attribute map_final { "1" }?, attribute map_match { xsd:string {minLength="1"} }?, attribute map_matchi { xsd:string {minLength="1"} }?, diff --git a/data/schemata/config.rng b/data/schemata/config.rng index 2f01a7920..f57dba04d 100644 --- a/data/schemata/config.rng +++ b/data/schemata/config.rng @@ -1094,6 +1094,11 @@ 1 + + + 1 + + 1 diff --git a/doc/biber.tex b/doc/biber.tex index c062781f9..b46e0fd8b 100644 --- a/doc/biber.tex +++ b/doc/biber.tex @@ -827,6 +827,7 @@ \subsubsection{The \texttt{sourcemap} option}\label{ref:map} +\textcolor{blue}{map\_entry\_newtype="\textcolor{red}{newentrykeytype}"}+ +\textcolor{blue}{map\_entry\_entrytarget="\textcolor{red}{newentrykey}"}+ +\textcolor{blue}{map\_append="1"}+ + +\textcolor{blue}{map\_appendstrict="1"}+ +\textcolor{blue}{map\_null="1"}+ +\textcolor{blue}{map\_entry\_null="1"}+ +\textcolor{blue}{map\_entry\_clone="\textcolor{red}{clonekey}"}+ @@ -938,9 +939,10 @@ \subsubsection{The \texttt{sourcemap} option}\label{ref:map} \textcolor{blue}{\texttt{map\_final}} is also set on this step, then processing of the parent map stops at this point. If \textcolor{blue}{\texttt{map\_append}} is set, then the value to set is - appended to the current value of \textcolor{red}{\texttt{set-field}}. The - value to set is specified by a mandatory one and only one of the - following attributes: + appended to the current value of \textcolor{red}{\texttt{set-field}}. + \textcolor{blue}{\texttt{map\_appendstrict}} appends only if the + \textcolor{red}{\texttt{set-field}} is not empty. The value to set is + specified by a mandatory one and only one of the following attributes: \begin{itemize} \item \textcolor{blue}{\texttt{map\_field\_value}} --- The \textcolor{red}{\texttt{set-field}} is set to diff --git a/lib/Biber/Input/file/biblatexml.pm b/lib/Biber/Input/file/biblatexml.pm index 86c5ab14b..f8419241e 100644 --- a/lib/Biber/Input/file/biblatexml.pm +++ b/lib/Biber/Input/file/biblatexml.pm @@ -627,8 +627,12 @@ sub create_entry { } } - # If append is set, keep the original value and append the new - my $orig = $step->{map_append} ? $etarget->findvalue($xp_node) : ''; + my $orig = ''; + # If append or appendstrict is set, keep the original value + # and append the new. + if ($step->{map_append} or $step->{map_appendstrict}) { + $orig = $etarget->findvalue($xp_node) || ''; + } if ($step->{map_origentrytype}) { next unless $last_type; @@ -636,7 +640,7 @@ sub create_entry { $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting xpath '$xp_node_s' to '${orig}${last_type}'"); } - unless (_changenode($etarget, $xp_node_s, $orig . $last_type, \$cnerror)) { + unless (_changenode($etarget, $xp_node_s, appendstrict_check($step, $orig, $last_type), \$cnerror)) { biber_warn("Source mapping (type=$level, key=$key): $cnerror"); } } @@ -645,7 +649,7 @@ sub create_entry { if ($logger->is_debug()) {# performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field xpath '$xp_node_s' to '${orig}${last_fieldval}'"); } - unless (_changenode($etarget, $xp_node_s, $orig . $last_fieldval, \$cnerror)) { + unless (_changenode($etarget, $xp_node_s, appendstrict_check($step, $orig, $last_fieldval), \$cnerror)) { biber_warn("Source mapping (type=$level, key=$etargetkey): $cnerror"); } } @@ -654,7 +658,7 @@ sub create_entry { if ($logger->is_debug()) {# performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field xpath '$xp_node_s' to '${orig}${last_field}'"); } - unless (_changenode($etarget, $xp_node_s, $orig . $last_field, \$cnerror)) { + unless (_changenode($etarget, $xp_node_s, appendstrict_check($step, $orig, $last_field), \$cnerror)) { biber_warn("Source mapping (type=$level, key=$etargetkey): $cnerror"); } } @@ -667,7 +671,7 @@ sub create_entry { if ($logger->is_debug()) {# performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field xpath '$xp_node_s' to '${orig}${fv}'"); } - unless (_changenode($etarget, $xp_node_s, $orig . $fv, \$cnerror)) { + unless (_changenode($etarget, $xp_node_s, appendstrict_check($step, $orig, $fv), \$cnerror)) { biber_warn("Source mapping (type=$level, key=$key): $cnerror"); } } diff --git a/lib/Biber/Input/file/bibtex.pm b/lib/Biber/Input/file/bibtex.pm index b640a9351..7eb01807e 100644 --- a/lib/Biber/Input/file/bibtex.pm +++ b/lib/Biber/Input/file/bibtex.pm @@ -708,29 +708,33 @@ sub create_entry { } } - # If append is set, keep the original value and append the new - my $orig = $step->{map_append} ? $etarget->get($field) : ''; + my $orig = ''; + # If append or appendstrict is set, keep the original value + # and append the new. + if ($step->{map_append} or $step->{map_appendstrict}) { + $orig = $etarget->get($field) || ''; + } if ($step->{map_origentrytype}) { next unless $last_type; if ($logger->is_debug()) { # performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field '$field' to '${orig}${last_type}'"); } - $etarget->set($field, encode('UTF-8', NFC($orig . $last_type))); + $etarget->set($field, encode('UTF-8', NFC(appendstrict_check($step, $orig,$last_type)))); } elsif ($step->{map_origfieldval}) { next unless $last_fieldval; if ($logger->is_debug()) { # performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field '$field' to '${orig}${last_fieldval}'"); } - $etarget->set($field, encode('UTF-8', NFC($orig . $last_fieldval))); + $etarget->set($field, encode('UTF-8', NFC(appendstrict_check($step, $orig, $last_fieldval)))); } elsif ($step->{map_origfield}) { next unless $last_field; if ($logger->is_debug()) { # performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field '$field' to '${orig}${last_field}'"); } - $etarget->set($field, encode('UTF-8', NFC($orig . $last_field))); + $etarget->set($field, encode('UTF-8', NFC(appendstrict_check($step, $orig, $last_field)))); } else { my $fv = maploopreplace($step->{map_field_value}, $maploop); @@ -741,7 +745,7 @@ sub create_entry { if ($logger->is_debug()) { # performance tune $logger->debug("Source mapping (type=$level, key=$etargetkey): Setting field '$field' to '${orig}${fv}'"); } - $etarget->set($field, encode('UTF-8', NFC($orig . $fv))); + $etarget->set($field, encode('UTF-8', NFC(appendstrict_check($step, $orig, $fv)))); } } } diff --git a/lib/Biber/Utils.pm b/lib/Biber/Utils.pm index 794bf52cf..84912107b 100644 --- a/lib/Biber/Utils.pm +++ b/lib/Biber/Utils.pm @@ -55,7 +55,7 @@ our @EXPORT = qw{ glob_data_file locate_data_file makenamesid makenameid stringi bcp472locale rangelen match_indices process_comment map_boolean parse_range parse_range_alt maploopreplace get_transliterator call_transliterator normalise_string_bblxml gen_initials join_name_parts - split_xsv date_monthday tzformat expand_option strip_annotation}; + split_xsv date_monthday tzformat expand_option strip_annotation appendstrict_check}; =head1 FUNCTIONS @@ -1657,6 +1657,24 @@ sub tzformat { } } +# Wrapper to enforce map_appendstrict +sub appendstrict_check { + my ($step, $orig, $val) = @_; + # Strict append? + if ($step->{map_appendstrict}) { + if ($orig) { + return $orig . $val; + } + else { # orig is empty, don't append + return ''; + } + } + # Normal append, don't care if orig is empty + else { + return $orig . $val; + } +} + 1; __END__ diff --git a/t/bibtex-aliases.t b/t/bibtex-aliases.t index d72b58982..92193da6b 100644 --- a/t/bibtex-aliases.t +++ b/t/bibtex-aliases.t @@ -4,7 +4,7 @@ use warnings; use utf8; no warnings 'utf8'; -use Test::More tests => 24; +use Test::More tests => 25; use Test::Differences; unified_diff; @@ -92,3 +92,6 @@ is_deeply($bibentries->entry('alias7')->get_field('lista'), ['listaval'], 'Alias # Testing append overwrites eq_or_diff($bibentries->entry('alias7')->get_field('verbb'), 'val2val1', 'Alias - 23' ); eq_or_diff($bibentries->entry('alias7')->get_field('verbc'), 'val3val2val1', 'Alias - 24' ); + +# Testing appendstrict +ok(is_undef($bibentries->entry('alias8')->get_field('verbc')), 'Alias - 25' ); diff --git a/t/tdata/bibtex-aliases.bcf b/t/tdata/bibtex-aliases.bcf index 575c9c678..826052334 100644 --- a/t/tdata/bibtex-aliases.bcf +++ b/t/tdata/bibtex-aliases.bcf @@ -551,7 +551,7 @@ - + @@ -1936,6 +1936,7 @@ alias5 alias6 alias7 + alias8 diff --git a/t/tdata/bibtex-aliases.bib b/t/tdata/bibtex-aliases.bib index 4a1f5e21b..e657eb971 100644 --- a/t/tdata/bibtex-aliases.bib +++ b/t/tdata/bibtex-aliases.bib @@ -55,5 +55,10 @@ @REPORT{alias7 LISTA = {listaval}, VERBA = {val1}, VERBB = {val2}, - VERBC = {val3}, + VERBC = {val3} +} + +@REPORT{alias8, + VERBA = {val2}, + VERBB = {val2} }