File Coverage

blib/lib/Biblio/Citation/Parser/Utils.pm
Criterion Covered Total %
statement 34 129 26.3
branch 18 64 28.1
condition 3 33 9.0
subroutine 4 9 44.4
pod 7 7 100.0
total 66 242 27.2


line stmt bran cond sub pod time code
1             package Biblio::Citation::Parser::Utils;
2              
3             ######################################################################
4             #
5             # Biblio::Citation::Parser::Utils;
6             #
7             ######################################################################
8             #
9             # This file is part of ParaCite Tools (http://paracite.eprints.org/developers/)
10             #
11             # Copyright (c) 2004 University of Southampton, UK. SO17 1BJ.
12             #
13             # ParaTools is free software; you can redistribute it and/or modify
14             # it under the terms of the GNU General Public License as published by
15             # the Free Software Foundation; either version 2 of the License, or
16             # (at your option) any later version.
17             #
18             # ParaTools is distributed in the hope that it will be useful,
19             # but WITHOUT ANY WARRANTY; without even the implied warranty of
20             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21             # GNU General Public License for more details.
22             #
23             # You should have received a copy of the GNU General Public License
24             # along with ParaTools; if not, write to the Free Software
25             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26             #
27             ######################################################################
28              
29 1     1   765 use strict;
  1         2  
  1         60  
30 1     1   5 use vars qw(@ISA @EXPORT @EXPORT_OK);
  1         1  
  1         1949  
31              
32             require Exporter;
33              
34             @ISA = qw(Exporter);
35             @EXPORT = qw(&trim_openurl &decompose_openurl &create_openurl);
36              
37             my @validtags = ("sid", "id", "genre", "aulast", "aufirst", "auinit", "auinitm", "coden", "issn", "eissn", "isbn", "title", "stitle", "atitle", "volume", "part", "issue", "spage", "epage", "pages", "artnum", "sici", "bici", "ssn", "quarter", "date", "pid", "url", "subject", "year", "month", "day");
38              
39             =pod
40              
41             =head1 NAME
42              
43             B - OpenURL utility functions
44              
45             =head1 DESCRIPTION
46              
47             This module contains methods for the parsing of reference metadata
48             into OpenURLs. Although we have aimed to make it 1.0 compliant,
49             there may well be errors (please let us know if there are!).
50              
51             =head1 METHODS
52              
53             =over 4
54              
55             =item $openurl_hash = Biblio::Citation::Parser::Utils::trim_openurl($openurl)
56              
57             This method takes a hash of metadata, and returns a
58             hash that contains only valid OpenURL fields.
59              
60             =cut
61              
62             sub trim_openurl
63             {
64 2     2 1 166 my($openurl) = @_;
65 2         8 my $outdata = {};
66 2         5 foreach(@validtags)
67             {
68 64         161 $outdata->{$_} = $openurl->{$_};
69             }
70 2         9 return $outdata;
71             }
72              
73             =pod
74              
75             =item $openurl_hash = Biblio::Citation::Parser::Utils::decompose_openurl($openurl)
76              
77             This method aims to enrich an OpenURL metadata hash
78             by applying various parsing techniques to the fields.
79             It decomposes dates into years, months, and days if
80             possible, fills in the appropriate fields if SICIs are
81             present, and ensures URLs, ISBNs, etc, are valid. It
82             returns a pointer to a hash containing the modified
83             metadata, and an array of errors (if any).
84              
85             =cut
86              
87             sub decompose_openurl
88             {
89 1     1 1 42 my($openurl) = @_;
90 1         3 my @errors = ();
91 1         4 foreach(@validtags)
92             {
93 32 100       67 if (!$openurl->{$_})
94             {
95 23         29 $openurl->{$_} = undef;
96             }
97             }
98             # Do a little rehashing and validation
99            
100             # Split up 'date' if present
101            
102 1 50       6 if ($openurl->{date})
103             {
104 1 50       14 if ($openurl->{date} =~ /^(\d{4})-(\d{2})-(\d{2})$/)
    50          
    50          
105             {
106 0         0 $openurl->{year} = $1;
107 0         0 $openurl->{month} = $2;
108 0         0 $openurl->{day} = $3;
109             }
110             elsif ($openurl->{date} =~ /^(\d{4})-(\d{2})$/)
111             {
112 0         0 $openurl->{year} = $1;
113 0         0 $openurl->{month} = $2;
114             }
115             elsif ($openurl->{date} =~ /^(\d{4})$/)
116             {
117 1         3 $openurl->{year} = $1;
118             }
119             else
120             {
121 0         0 push @errors, "Invalid date: ".$openurl->{date};
122             }
123            
124             }
125              
126             # Parse SICI and merge with hash
127            
128 1 50       5 if ($openurl->{sici})
129             {
130 0         0 my %sici = parse_sici($openurl->{sici});
131 0         0 foreach(("issn", "year", "month", "day"))
132             {
133 0 0 0     0 if (!$openurl->{$_} && $sici{$_})
134             {
135 0         0 $openurl->{$_} = $sici{$_};
136             }
137             }
138 0 0 0     0 if ($sici{locn} && !$openurl->{spage})
139             {
140 0         0 $openurl->{spage} = $sici{locn};
141             }
142             }
143              
144             #
145            
146             # Check genre
147            
148 1 50       5 if ($openurl->{genre})
149             {
150 0 0 0     0 if ($openurl->{genre} ne "journal" &&
      0        
      0        
      0        
      0        
      0        
151             $openurl->{genre} ne "book" &&
152             $openurl->{genre} ne "conference" &&
153             $openurl->{genre} ne "article" &&
154             $openurl->{genre} ne "preprint" &&
155             $openurl->{genre} ne "proceeding" &&
156             $openurl->{genre} ne "bookitem")
157             {
158 0         0 push @errors, "Invalid genre: ".$openurl->{genre};
159 0         0 delete $openurl->{genre};
160             }
161             }
162              
163             # Validate issn
164            
165 1 50       8 if ($openurl->{issn})
166             {
167 0         0 $openurl->{issn} =~ s/-//g;
168 0 0       0 if ($openurl->{issn} =~ /^(\d{4})(\d{4})$/)
169             {
170 0         0 $openurl->{issn} = "$1-$2";
171             }
172 0 0       0 if ($openurl->{issn} !~ /^\d{4}-\d{4}$/)
173             {
174 0         0 push @errors, "Invalid ISSN: ".$openurl->{issn};
175 0         0 delete $openurl->{issn};
176             }
177             }
178            
179             # Validate eissn
180            
181 1 50       6 if ($openurl->{eissn})
182             {
183 0 0       0 if ($openurl->{eissn} !~ //)
184             {
185 0         0 push @errors, "Invalid electronic ISSN: ".$openurl->{eissn};
186 0         0 delete $openurl->{eissn};
187             }
188             }
189            
190             # Validate coden
191            
192 1 50       4 if ($openurl->{coden})
193             {
194 0 0       0 if ($openurl->{coden} !~ //)
195             {
196 0         0 push @errors, "Invalid CODEN: ".$openurl->{coden};
197 0         0 delete $openurl->{coden};
198             }
199             }
200              
201             # Validate ISBN
202            
203 1 50       5 if ($openurl->{isbn})
204             {
205 0         0 $openurl->{isbn} =~ s/-//g;
206 0 0       0 if ($openurl->{isbn} !~ /([\dX]{8})$/)
207             {
208 0         0 push @errors, "Invalid ISBN: ".$openurl->{isbn};
209 0         0 delete $openurl->{isbn};
210             }
211             else
212             {
213             # More complex ISBN check based on Oshiro Naoki's code
214 0         0 my @isbn = split('', $openurl->{isbn});
215 0         0 my @tmp = ();
216 0         0 foreach my $n (@isbn)
217             {
218 0 0       0 $n = 10 if ($n eq "X");
219 0         0 push @tmp, $n;
220             }
221 0 0       0 if (!isbn_check(@tmp))
222             {
223 0         0 push @errors, "Invalid ISBN: ".$openurl->{isbn};
224             }
225             }
226             }
227              
228             # Validate BICI
229            
230 1 50       4 if ($openurl->{bici})
231             {
232 0 0       0 if ($openurl->{bici} !~ //)
233             {
234 0         0 push @errors, "Invalid BICI: ".$openurl->{bici};
235 0         0 delete $openurl->{bici};
236             }
237             }
238              
239             # Split up 'pages' if present
240            
241 1 50       4 if ($openurl->{pages})
242             {
243 1 50       7 if ($openurl->{pages} =~ /^(\d+)-(\d+)$/)
244             {
245 1         3 $openurl->{spage} = $1;
246 1         4 $openurl->{epage} = $2;
247             }
248             else
249             {
250 0         0 push @errors, "Invalid page range: ".$openurl->{pages}
251             }
252             }
253            
254              
255 1 50 33     6 if ($openurl->{ssn} && $openurl->{ssn} !~ /^(winter|spring|summer|fall)$/)
256             {
257 0         0 push @errors, "Invalid season: ".$openurl->{ssn};
258 0         0 delete $openurl->{ssn};
259             }
260            
261 1 50 33     5 if ($openurl->{quarter} && $openurl->{quarter} !~ /^[1234]$/)
262             {
263 0         0 push @errors, "Invalid quarter: ".$openurl->{quarter};
264 0         0 delete $openurl->{quarter};
265             }
266 1 50 33     6 if ($openurl->{url} && $openurl->{url} !~ /^(ht|f)tp/)
267             {
268 0         0 $openurl->{url} = "http://".$openurl->{url};
269             }
270 1         4 return ($openurl, @errors);
271             }
272              
273             =pod
274              
275             =item $openurl = Biblio::Citation::Parser::create_openurl($metadata)
276              
277             This method creates and returns an OpenURL from a metadata hash.
278             No base URLs are prepended to this, so this should be done before
279             using it as a link. URI::OpenURL should be used to generate OpenURLs
280             in place of this function.
281              
282             =cut
283              
284             sub create_openurl
285             {
286 0     0 1   my($data) = @_;
287 0 0         if ($data->{captitle}) { $data->{atitle} = $data->{captitle}; }
  0            
288 0 0         if ($data->{uctitle}) { $data->{atitle} = $data->{uctitle}; }
  0            
289 0           ($data,undef) = decompose_openurl($data);
290 0           my $openurl = "sid=paracite&";
291 0           my(@openurl_keys) = ("sici", "artnum", "spage", "stitle", "part", "date", "aufirst", "pid", "aulast", "auinitm", "volume", "quarter", "issue", "title", "pages", "ssn", "auinit", "sid", "genre", "eissn", "atitle", "id", "isbn", "bici", "issn", "epage", "coden", "url", "subject", "year", "month", "day");
292 0           my %data_hash = %$data;
293 0           foreach my $key (@openurl_keys)
294             {
295 0 0         if ($data_hash{$key})
296             {
297 0 0         if (ref $data_hash{$key} eq "ARRAY")
298             {
299 0           foreach my $el (@{$data_hash{$key}})
  0            
300             {
301 0           $el =~ s/[ ]+/ /g;
302 0           $openurl .= "$key=".Biblio::Citation::Parser::Utils::url_escape($el)."&";
303             }
304             }
305             else
306             {
307 0           $data_hash{$key} =~ s/[ ]+/ /g;
308 0           $openurl .= "$key=".Biblio::Citation::Parser::Utils::url_escape($data_hash{$key})."&";
309             }
310             }
311             }
312              
313 0           chop $openurl;
314 0           return $openurl;
315             }
316              
317             =pod
318              
319             =item $valid_isbn = Biblio::Citation::Parser::Utils::isbn_check(@isbn_chars)
320              
321             This is a simple function that takes an array of ISBN characters, and returns true if it is a valid ISBN.
322              
323             =cut
324              
325             sub isbn_check
326             {
327 0     0 1   my(@isbn)=@_;
328 0           my $i;
329              
330 0           for ($i=0; $i<$#isbn; $i++) {
331 0           $isbn[$i+1]+=$isbn[$i];
332             }
333              
334 0           for ($i=0; $i<$#isbn; $i++) {
335 0           $isbn[$i+1]+=$isbn[$i];
336             }
337              
338 0           return (($isbn[$#isbn]%11)==0);
339             }
340              
341             =pod
342              
343             =item $sici_hash = Biblio::Citation::Parser::Utils::parse_sici($sici)
344              
345             This function takes a SICI string, and returns
346             a hash of information parsed from it, including
347             date information, issn numbers, etc.
348              
349             =cut
350              
351             sub parse_sici
352             {
353 0     0 1   my($sici) = @_;
354 0           my %out = ();
355 0           ($out{item}, $out{contrib}, $out{control}) = ($sici =~ /^(.*)<(.*)>(.*)$/);
356 0           ($out{issn}, $out{chron}, $out{enum}) = ($out{item} =~ /^(\d{4}-\d{4})\((.+)\)(.+)/);
357 0           ($out{site}, $out{title}, $out{locn}) = (split ":", $out{contrib});
358 0           ($out{csi}, $out{dpi}, $out{mfi}, $out{version}, $out{check}) = ($out{control} =~ /^(.+)\.(.+)\.(.+);(.+)-(.+)$/);
359 0           ($out{year}, $out{month}, $out{day}, $out{seryear}, $out{seryear}, $out{sermonth}, $out{serday}) = ($out{chron} =~ /^(\d{4})?(\d{2})?(\d{2})?(\/(\d{4})?(\d{2})?(\d{2})?)?/);
360 0           $out{enum} = [split ":", $out{enum}];
361 0           return %out;
362             }
363              
364             =pod
365              
366             =item $bici_hash = Biblio::Citation::Parser::Utils::parse_bici($bici)
367              
368             This is not yet implemented, but will eventually
369             be the BICI alternative for parse_sici.
370              
371             =cut
372              
373             sub parse_bici
374             {
375 0     0 1   my($bici) = @_;
376            
377 0           my %out = ();
378 0           return %out;
379             }
380              
381             =pod
382              
383             =item $escaped_url = ParaTools::Utils::url_escape($string)
384              
385             Simple function to convert a string into an encoded
386             URL (i.e. spaces to %20, etc). Takes the unencoded
387             URL as a parameter, and returns the encoded version.
388              
389             =cut
390              
391             sub url_escape
392             {
393 0     0 1   my( $url ) = @_;
394 0           $url =~ s/
395 0           $url =~ s/>/%3E/g;
396 0           $url =~ s/#/%23/g;
397 0           $url =~ s/;/%3B/g;
398 0           $url =~ s/&/%26/g;
399 0           my $uri = URI->new( $url );
400 0           my $out = $uri->as_string;
401 0           return $out;
402             }
403              
404             1;
405              
406             __END__