File Coverage

blib/lib/BibTeX/Parser/Author.pm
Criterion Covered Total %
statement 103 151 68.2
branch 44 80 55.0
condition 2 18 11.1
subroutine 15 17 88.2
pod 7 7 100.0
total 171 273 62.6


line stmt bran cond sub pod time code
1             package BibTeX::Parser::Author;
2             {
3             $BibTeX::Parser::Author::VERSION = '1.03';
4             }
5              
6 18     18   140757 use warnings;
  18         54  
  18         598  
7 18     18   99 use strict;
  18         36  
  18         337  
8              
9 18     18   992 use BibTeX::Parser;
  18         49  
  18         597  
10              
11              
12             use overload
13 18     18   23125 '""' => \&to_string;
  18         18485  
  18         137  
14              
15              
16              
17             sub new {
18 68     68 1 19147 my $class = shift;
19              
20 68 100       188 if (@_) {
21 65         191 my $self = [ $class->split(@_) ];
22 65         318 return bless $self, $class;
23             } else {
24 3         11 return bless [], $class;
25             }
26             }
27              
28             sub _get_or_set_field {
29 406     406   683 my ($self, $field, $value) = @_;
30 406 100       736 if (defined $value) {
31 4         18 $self->[$field] = $value;
32             } else {
33 402         1478 return $self->[$field];
34             }
35             }
36              
37              
38             sub first {
39 140     140 1 15256 shift->_get_or_set_field(0, @_);
40             }
41              
42              
43             sub von {
44 86     86 1 233 shift->_get_or_set_field(1, @_);
45             }
46              
47              
48             sub last {
49 94     94 1 1360 shift->_get_or_set_field(2, @_);
50             }
51              
52              
53             sub jr {
54 86     86 1 223 shift->_get_or_set_field(3, @_);
55             }
56              
57              
58             # Take a string and create an array [first, von, last, jr]
59             sub split {
60 98     98 1 11581 my ($self_or_class, $name) = @_;
61            
62             # remove whitespace at start and end of string
63 98         661 $name =~ s/^\s*(.*)\s*$/$1/s;
64              
65              
66              
67 98 100       299 if (!length($name)) {
68 6         22 return (undef, undef, undef, undef);
69             }
70            
71 92         268 my @comma_separated =
72             BibTeX::Parser::_split_braced_string($name,
73             '\s*,\s*');
74 92 50       261 if (scalar(@comma_separated) == 0) {
75             # Error?
76 0         0 return (undef, undef, undef, undef);
77             }
78              
79 92         163 my $first=undef;
80 92         132 my $von=undef;
81 92         155 my $last=undef;
82 92         126 my $jr=undef;
83            
84 92 100       216 if (scalar(@comma_separated) == 1) {
85             # First von Last form
86 65         157 my @tokens =
87             BibTeX::Parser::_split_braced_string($name, '\s+');
88 65 50       165 if (!scalar (@tokens)) {
89 0         0 return (undef, undef, undef, undef);
90             }
91 65         224 my ($start_von, $start_last) = _getStartVonLast (@tokens);
92 65 100       184 if ($start_von >0) {
93 55         201 $first = join(' ', splice(@tokens,0,$start_von));
94             }
95 65 100       186 if (($start_last-$start_von) >0) {
96 9         43 $von = join(' ', splice(@tokens,0,$start_last-$start_von));
97             }
98 65         137 $last = join(' ',@tokens);
99 65         336 return ($first, $von, $last, $jr);
100             }
101             # Now we work with von Last, [Jr,] First form
102 27 100       61 if (scalar @comma_separated == 2) { # no jr
103 21         52 my @tokens=
104             BibTeX::Parser::_split_braced_string($comma_separated[1], '\s+');
105 21         60 $first = join(' ', @tokens);
106             } else { # jr is present
107 6         16 my @tokens=
108             BibTeX::Parser::_split_braced_string($comma_separated[1], '\s+');
109 6         17 $jr = join(' ', @tokens);
110 6         17 @tokens=
111             BibTeX::Parser::_split_braced_string($comma_separated[2], '\s+');
112 6         16 $first = join(' ', @tokens);
113             }
114 27         70 my @tokens =
115             BibTeX::Parser::_split_braced_string($comma_separated[0], '\s+');
116 27         58 my $start_last = _getStartLast(@tokens);
117 27 100       57 if ($start_last > 0) {
118 10         25 $von=join(' ', splice(@tokens,0,$start_last));
119             }
120 27         68 $last = join(' ',@tokens);
121 27         139 return ($first, $von, $last, $jr);
122              
123             }
124              
125             # Return the index of the first von element and the first lastname
126             # element. If no von element, von=last
127              
128             sub _getStartVonLast {
129 65     65   133 my $length=scalar(@_);
130 65 100       156 if ($length==1) {
131 8         20 return (0,0);
132             }
133 57         89 my $start_von=-1;
134 57         102 my $start_last=$length-1;
135 57         158 for (my $i=0; $i<$length; $i++) {
136 130 100       273 if (_is_von_token($_[$i])) {
137 14         40 $start_von=$i;
138 14         27 last;
139             }
140             }
141 57 100       165 if ($start_von== -1) { # no von part
142 43         116 return($length-1, $length-1);
143             }
144 14 100       38 if ($start_von== $length-1) { # all parts but last are upper case?
145 5         15 return($length-1, $length-1);
146             }
147 9         29 for (my $i=$start_von+1; $i<$length; $i++) {
148 13 100       26 if (!_is_von_token($_[$i])) {
149 9         18 $start_last=$i;
150 9         14 last;
151             }
152             }
153 9         37 return($start_von, $start_last);
154             }
155              
156              
157             # Return the index of the first lastname
158             # element provided no first name elements are present
159              
160             sub _getStartLast {
161 27     27   50 my $length=scalar(@_);
162 27 100       59 if ($length==1) {
163 15         28 return 0;
164             }
165 12         18 my $start_last=$length-1;
166 12         33 for (my $i=0; $i<$length; $i++) {
167 22 100       62 if (!_is_von_token($_[$i])) {
168 12         18 $start_last=$i;
169 12         17 last;
170             }
171             }
172 12         20 return $start_last;
173             }
174              
175              
176             sub _split_name_parts {
177 0     0   0 my $name = shift;
178              
179 0 0       0 if ( $name !~ /\{/ ) {
180 0         0 return split /\s+/, $name;
181             } else {
182 0         0 my @parts;
183 0         0 my $cur_token = '';
184 0         0 while ( scalar( $name =~ /\G ( [^\s\{]* ) ( \s+ | \{ | \s* $ ) /xgc ) ) {
185 0         0 $cur_token .= $1;
186 0 0       0 if ( $2 =~ /\{/ ) {
187 0 0       0 if ( scalar( $name =~ /\G([^\}]*)\}/gc ) ) {
188 0         0 $cur_token .= "{$1}";
189             } else {
190 0         0 die "Unmatched brace in name '$name'";
191             }
192             } else {
193 0 0       0 if ( $cur_token =~ /^{(.*)}$/ ) {
194 0         0 $cur_token = $1;
195             }
196 0         0 push @parts, $cur_token;
197 0         0 $cur_token = '';
198             }
199             }
200 0         0 return @parts;
201             }
202              
203             }
204              
205              
206             sub _get_single_author_from_tokens {
207 0     0   0 my (@tokens) = @_;
208 0 0       0 if (@tokens == 0) {
    0          
    0          
209 0         0 return (undef, undef, undef, undef);
210             } elsif (@tokens == 1) { # name without comma
211 0 0       0 if ( $tokens[0] =~ /(^|\s)[[:lower:]]/) { # name has von part or has only lowercase names
212 0         0 my @name_parts = _split_name_parts $tokens[0];
213              
214 0         0 my $first;
215 0   0     0 while (@name_parts && ucfirst($name_parts[0]) eq $name_parts[0] ) {
216 0 0       0 $first .= $first ? ' ' . shift @name_parts : shift @name_parts;
217             }
218              
219 0         0 my $von;
220             # von part are lowercase words
221 0   0     0 while ( @name_parts && lc($name_parts[0]) eq $name_parts[0] ) {
222 0 0       0 $von .= $von ? ' ' . shift @name_parts : shift @name_parts;
223             }
224              
225 0 0       0 if (@name_parts) {
226 0         0 return ($first, $von, join(" ", @name_parts), undef);
227             } else {
228 0         0 return (undef, undef, $tokens[0], undef);
229             }
230             } else {
231 0 0 0     0 if ( $tokens[0] !~ /\{/ && $tokens[0] =~ /^((.*)\s+)?\b(\S+)$/) {
232 0         0 return ($2, undef, $3, undef);
233             } else {
234 0         0 my @name_parts = _split_name_parts $tokens[0];
235 0         0 return ($name_parts[0], undef, $name_parts[1], undef);
236             }
237             }
238              
239             } elsif (@tokens == 2) {
240 0         0 my @von_last_parts = _split_name_parts $tokens[0];
241 0         0 my $von;
242             # von part are lowercase words
243 0   0     0 while ( @von_last_parts && lc($von_last_parts[0]) eq $von_last_parts[0] ) {
244 0 0       0 $von .= $von ? ' ' . shift @von_last_parts : shift @von_last_parts;
245             }
246 0         0 return ($tokens[1], $von, join(" ", @von_last_parts), undef);
247             } else {
248 0         0 my @von_last_parts = _split_name_parts $tokens[0];
249 0         0 my $von;
250             # von part are lowercase words
251 0   0     0 while ( @von_last_parts && lc($von_last_parts[0]) eq $von_last_parts[0] ) {
252 0 0       0 $von .= $von ? ' ' . shift @von_last_parts : shift @von_last_parts;
253             }
254 0         0 return ($tokens[2], $von, join(" ", @von_last_parts), $tokens[1]);
255             }
256              
257             }
258              
259              
260              
261              
262             sub to_string {
263 46     46 1 2386 my $self = shift;
264              
265 46 50       127 if ($self->jr) {
266 0 0       0 return ($self->von ? $self->von . " " : '') . " " . $self->last . ", " . $self->jr . ", " . $self->first;
267             } else {
268 46 50       119 return ($self->von ? $self->von . " " : '') . $self->last . ($self->first ? ", " . $self->first : '');
    50          
269             }
270             }
271              
272              
273             # Return 1 if the first letter on brace level 0 is lowercase
274             sub _is_von_token {
275 170     170   375 my $string = shift;
276 170         571 while ($string =~
277             s/^(\\[[:alpha:]]+\{|\{|\\[[:^alpha:]]?|[[:^alpha:]])//) {
278 8 100       36 if ($1 eq '{' ) {
279 5         9 my $numbraces=1;
280 5   66     21 while ($numbraces !=0 && length($string)) {
281 66         99 my $symbol = substr($string, 0, 1);
282 66 50       133 if ($symbol eq '{') {
    100          
283 0         0 $numbraces ++;
284             } elsif ($symbol eq '}') {
285 5         8 $numbraces --;
286             }
287 66         176 $string = substr($string,1);
288             }
289             }
290             }
291              
292 170 100       353 if (length $string ) {
293 165         345 my $symbol = substr($string, 0, 1);
294 165 100       401 if (lc($symbol) eq $symbol) {
295 25         79 return 1;
296             } else {
297 140         417 return 0;
298             }
299             } else {
300 5         13 return 1;
301             }
302              
303             }
304              
305             1; # End of BibTeX::Entry
306              
307             __END__