File Coverage

blib/lib/Biblio/EndnoteStyle.pm
Criterion Covered Total %
statement 78 98 79.5
branch 23 38 60.5
condition 6 12 50.0
subroutine 12 16 75.0
pod 3 3 100.0
total 122 167 73.0


line stmt bran cond sub pod time code
1             # $Id: EndnoteStyle.pm,v 1.12 2016/11/28 22:18:22 mike Exp $
2              
3             package Biblio::EndnoteStyle;
4              
5 1     1   41059 use 5.006;
  1         5  
6 1     1   8 use strict;
  1         3  
  1         64  
7 1     1   6 use warnings;
  1         7  
  1         1119  
8              
9             our $VERSION = 0.06;
10              
11             =encoding utf8
12              
13             =head1 NAME
14              
15             Biblio::EndnoteStyle - reference formatting using Endnote-like templates
16              
17             =head1 SYNOPSIS
18              
19             use Biblio::EndnoteStyle;
20             $style = new Biblio::EndnoteStyle();
21             ($text, $errmsg) = $style->format($template, \%fields);
22              
23             =head1 DESCRIPTION
24              
25             This small module provides a way of formatting bibliographic
26             references using style templates similar to those used by the popular
27             reference management software Endnote (http://www.endnote.com/). The
28             API is embarrassingly simple: a formatter object is made using the
29             class's constructor, the C method; C may then be
30             repeatedly called on this object, using the same or different
31             templates.
32              
33             (The sole purpose of the object is to cache compiled templates so that
34             multiple C invocations are more efficient than they would
35             otherwise be. Apart from that, the API might just as well have been a
36             single function.)
37              
38             =head1 METHODS
39              
40             =head2 new()
41              
42             $style = new Biblio::EndnoteStyle();
43              
44             Creates a new formatter object. Takes no arguments.
45              
46             =cut
47              
48             # The object is vacuous except that it knows its class, so that
49             # subclasses can be made that override some of the methods.
50             #
51             sub new {
52 1     1 1 19 my $class = shift();
53              
54 1         8 return bless {
55             debug => 0,
56             compiled => {}, # cache of compiled templates
57             }, $class;
58             }
59              
60              
61             =head2 debug()
62              
63             $olddebug = $style->debug(1);
64              
65             Turns debugging on or off and returns the old debugging status. If an
66             argument is provided, then debugging is turned either on or off
67             according to whether then argument is true or false. In any case, the
68             old value of the debugging status is returned, so that a call with no
69             argument is a side-effect-free inquiry.
70              
71             When debugging is turned on, compiled templates are dumped to standard
72             error. It is not pretty.
73              
74             =cut
75              
76             sub debug {
77 0     0 1 0 my $this = shift();
78 0         0 my($val) = @_;
79              
80 0         0 my $old = $this->{debug};
81 0 0       0 $this->{debug} = $val if defined $val;
82 0         0 return $old;
83             }
84              
85              
86             =head2 format()
87              
88             ($text, $errmsg) = $style->format($template, \%fields);
89              
90             Formats a reference, consisting of a hash of fields, according to an
91             Endnote-like template. The template is a string essentially the same
92             as those used in Endnote, as documented in the Endnote X User Guide at
93             http://www.endnote.com/support/helpdocs/EndNoteXWinManual.pdf
94             pages 390ff. In particular, pages 415-210 have details of the recipe
95             format. Because the templates used in this module are plain text, a
96             few special characters are used:
97              
98             =over 4
99              
100             =item ¬
101              
102             Link adjacent words. This is the "non-breaking space"
103             described on page 418 of the EndNote X
104              
105             =item |
106              
107             Forced Separation of elements that would otherwise be dependent.
108              
109             =item ^
110              
111             Separator for singular/plural aternatives.
112              
113             =cut `
114              
115             =item `
116              
117             Used to prevent literal text from being interpreted as a fieldname.
118              
119             =back
120              
121             The hash of fields is passed by reference: keys are fieldnames, and
122             the corresponding values are the data. PLEASE NOTE AN IMPORTANT
123             DIFFERENCE. Keys that do not appear in the hash at all are not
124             considered to be fields, so that if they appear in the template, they
125             will be interpreted as literal text; keys that appear in the hash but
126             whose values are undefined or empty are considered to be fields with
127             no value, and will be formatted as empty with dependent text omitted.
128             So for example:
129              
130             $style->format(";Author: ", { Author => "Taylor" }) eq ":Taylor: "
131             $style->format(";Author: ", { Author => "" }) eq ";"
132             $style->format(";Author: ", { xAuthor => "" }) eq ";Author: "
133              
134             C returns two values: the formatted reference and an
135             error-message. The error message is defined if and only if the
136             formatted reference is not.
137              
138             =cut
139              
140             sub format {
141 9     9 1 4067 my $this = shift();
142 9         15 my($text, $data) = @_;
143              
144             #use Data::Dumper; print Dumper($data);
145 9         27 my $template = $this->{compiled}->{$text};
146 9 100       33 if (!defined $template) {
147 8         10 my $errmsg;
148             ($template, $errmsg) =
149 8         30 Biblio::EndnoteStyle::Template->new($text, $this->{debug});
150 8 50       23 return (undef, $errmsg) if !defined $template;
151             #print "template '$text'\n", $template->render();
152 8         26 $this->{compiled}->{$text} = $template;
153             }
154              
155 9         28 return $template->format($data);
156             }
157              
158              
159             package Biblio::EndnoteStyle::Template;
160              
161             sub new {
162 8     8   16 my $class = shift();
163 8         11 my($text, $debug) = @_;
164              
165 8         12 my @sequences;
166 8         22 while ($text ne "") {
167 12 50       73 if ($text =~ s/^(\s*[^\s|]*\s?)//) {
168 12         37 my $sequence = $1;
169 12         39 my $obj = Biblio::EndnoteStyle::Sequence->new($sequence);
170 12         19 push @sequences, $obj;
171 12         47 $text =~ s/^\|//;
172             } else {
173 0         0 die "unparseable template fragment '$text'";
174             }
175             }
176              
177 8         41 my $this = bless {
178             text => $text,
179             sequences => \@sequences,
180             }, $class;
181 8 50       21 print STDERR $this->render() if $debug;
182              
183 8         20 return $this;
184             }
185              
186             sub render {
187 0     0   0 my $this = shift();
188              
189 0         0 return join("", map { $_->render() . "\n" } @{ $this->{sequences} });
  0         0  
  0         0  
190             }
191              
192             sub format {
193 9     9   12 my $this = shift();
194 9         11 my($data) = @_;
195              
196 9         13 my $result = "";
197 9         11 foreach my $sequence (@{ $this->{sequences} }) {
  9         33  
198 14         32 my($substr, $errmsg) = $sequence->format($data);
199 14 50       33 return (undef, $errmsg) if !defined $substr;
200 14         26 $result .= $substr;
201             }
202              
203 9         33 return $result;
204             }
205              
206              
207             # ----------------------------------------------------------------------------
208              
209             package Biblio::EndnoteStyle::Sequence;
210              
211 15     15   74 sub WORD { 290168 }
212 47     47   138 sub LITERAL { 120368 }
213             sub typename {
214 0     0   0 my($type) = @_;
215 0 0       0 return "WORD" if $type == WORD;
216 0 0       0 return "LITERAL" if $type == LITERAL;
217 0         0 return "???";
218             }
219              
220             sub new {
221 12     12   18 my $class = shift();
222 12         21 my($text) = @_;
223              
224 1     1   9 use Carp;
  1         2  
  1         1696  
225 12 50       34 confess("new($class) with text undefined") if !defined $text;
226 12         15 my $tail = $text;
227 12         26 $tail =~ s/¬/ /g;
228 12         14 my @tokens;
229 12         67 while ($tail =~ s/(.*?)([``a-z_0-9]+)//i) {
230 8         28 my($head, $word) = ($1, $2);
231 8 50       30 push @tokens, [ LITERAL, $head ] if $head ne "";
232 8 100       28 if ($word =~ s/^`(.*)`$/$1/) {
233 1         5 push @tokens, [ LITERAL, $word ];
234             } else {
235 7         14 push @tokens, [ WORD, $word ];
236             }
237             }
238 12 100       38 push @tokens, [ LITERAL, $tail ] if $tail ne "";
239              
240 12         59 return bless {
241             text => $text,
242             tokens => \@tokens,
243             }, $class;
244             }
245              
246             sub render {
247 0     0   0 my $this = shift();
248              
249             return (sprintf("%24s: ", ("'" . $this->{text} . "'")) .
250             join(", ", map {
251 0         0 my($type, $val) = @$_;
252 0         0 typename($type) . " '$val'";
253 0         0 } @{ $this->{tokens} }));
  0         0  
254             }
255              
256             sub format {
257 14     14   17 my $this = shift();
258 14         19 my($data) = @_;
259              
260 14         17 my $gotField = 0;
261 14         18 my $result = "";
262 14         14 foreach my $token (@{ $this->{tokens} }) {
  14         32  
263 27         58 my($type, $val) = @$token;
264 27 100       44 if ($type == LITERAL) {
    50          
265 19         41 $result .= $val;
266             } elsif ($type != WORD) {
267 0         0 die "unexpected token type '$type'";
268             } else {
269 8         15 my $dval = $data->{$val};
270 8 100       22 $dval = $data->{lc($val)} if !defined $dval;
271             $dval = "" if !defined $dval && (exists $data->{$val} ||
272 8 50 33     36 exists $data->{lc($val)});
      66        
273 8 100 66     74 if (!defined $dval) {
    100          
274             # The word is not a fieldname at all: treat as a literal
275             #print "!defined \$dval\n";
276 1         4 $result .= $val;
277             } elsif (!$gotField && $dval eq "") {
278             #print "\$dval is empty\n";
279             # Field is empty, so whole dependent sequence is omitted
280 5         28 return "";
281             } else {
282             #print "$dval eq '$dval'\n";
283 2         4 $gotField = 1;
284             # Loathesome but useful special case
285 2 50 33     14 $dval = "http://$dval" if $val eq "URL" && $dval !~ /^[a-z]+:/;
286 2         8 $result .= $dval;
287             }
288             }
289             }
290              
291 9         26 return $result;
292             }
293              
294              
295             =head1 AUTHOR
296              
297             Mike Taylor, Emike@miketaylor.org.ukE
298              
299             =head1 COPYRIGHT AND LICENCE
300              
301             Copyright (C) 2007 by Mike Taylor.
302              
303             This library is free software; you can redistribute it and/or modify
304             it under the same terms as Perl itself, either Perl version 5.8.4 or,
305             at your option, any later version of Perl 5 you may have available.
306              
307             =cut
308              
309              
310             1;