File Coverage

blib/lib/Summerset/IsbnConverter.pm
Criterion Covered Total %
statement 58 60 96.6
branch 20 22 90.9
condition 8 9 88.8
subroutine 10 10 100.0
pod 6 6 100.0
total 102 107 95.3


line stmt bran cond sub pod time code
1             package Summerset::IsbnConverter;
2             require Exporter;
3             our @ISA = qw/Exporter/;
4             our @EXPORT = qw/convertToIsbn10 convertToIsbn13 generateIsbn10CheckDigit generateIsbn13CheckDigit validateIsbn10 validateIsbn13/;
5             our %EXPORT_TAGS = (all => [qw/convertToIsbn10 convertToIsbn13 generateIsbn10CheckDigit generateIsbn13CheckDigit validateIsbn10 validateIsbn13/]);
6              
7 1     1   21072 use 5.006;
  1         5  
  1         41  
8 1     1   6 use strict;
  1         2  
  1         52  
9 1     1   5 use warnings FATAL => 'all';
  1         9  
  1         1235  
10              
11             =head1 NAME
12              
13             Summerset::IsbnConverter - Converts ISBN10 format to ISBN13 format and vice versa.
14             This module also contains simple methods to validate ISBN10 and ISBN13 strings.
15              
16             =head1 VERSION
17              
18             Version 1.00
19              
20             =cut
21              
22             our $VERSION = '1.00';
23              
24              
25             =head1 SYNOPSIS
26              
27             Simple module to convert ISBN10 to ISBN13 format and vice versa.
28              
29              
30             use Summerset::IsbnConverter;
31              
32             my $isbn10 = '0395040892';
33             my $isbn13 = Summerset::IsbnConverter::convertToIsbn13($isbn10);
34            
35             print $isnb13; # will print -> 9780395040898
36            
37              
38             =head1 EXPORT
39              
40             convertToIsbn10
41             convertToIsbn13
42             generateIsbn10CheckDigit
43             generateIsbn13CheckDigit
44             validateIsbn10
45             validateIsbn13
46              
47             =head1 SUBROUTINES/METHODS
48              
49             =head2 convertToIsbn10
50              
51             Converts an ISBN13 format string, into ISBN10 format.
52              
53             Requires a single ISBN13. This ISBN must begin with the 978 prefix.
54             Other ISBN13's are not convertable to ISBN10. The ISBN13 may contain
55             hyphens or other formatting characters/whitespace. The ISBN13 string may not
56             end with any whitespace.
57              
58             Returns a string in ISBN10 format.
59              
60             Returns undef on errors (eg, invalid ISBN13 provided).
61              
62             =cut
63             sub convertToIsbn10 {
64 2     2 1 9 my $input = &_replaceNonDigitCharacters(shift);
65            
66             # make sure input was valid AND begins with 978 prefix
67 2 100 66     6 return undef unless &validateIsbn13($input) && $input =~ /^978/;
68            
69             # remove the prefix, then the check digit
70 1         6 $input =~ s/^978//;
71 1         4 $input =~ s/\d$//;
72            
73             # return ISBN10 with the recalculated checkdigit
74 1         4 return $input . &generateIsbn10CheckDigit($input);
75             }
76              
77             =head2 convertToIsbn13
78              
79             Converts an ISBN10 format string, into ISBN13 format.
80              
81             Requires a single ISBN10. The ISBN10 string may contain
82             hyphens or other formatting characters/whitespace. The ISBN13 string may not
83             end with any whitespace.
84              
85             Returns a string in ISBN13 format.
86              
87             Returns undef on errors (eg, invalid ISBN10 provided).
88              
89             =cut
90             sub convertToIsbn13 {
91 2     2 1 8 my $input = &_replaceNonDigitCharacters(shift);
92            
93             # make sure input was a valid ISBN10
94 2 100       6 return undef unless &validateIsbn10($input);
95            
96             # remove the checkdigit, which can be an x
97 1         5 $input =~ s/(\d|x)$//i;
98 1         3 $input = '978' . $input; # add the isbn13 '978' prefix
99            
100             # return ISBN13 with the recalculated checkdigit
101 1         3 return $input . &generateIsbn13CheckDigit($input);
102            
103             }
104              
105             =head2 generateIsbn10CheckDigit
106              
107             Takes a single 9 character string as input.
108              
109             Returns a single character check digit. This is typically a numeric digit, however
110             this can occasionally be the letter 'X'.
111              
112             Returns undef on invalid input.
113              
114             =cut
115             sub generateIsbn10CheckDigit{
116 9     9 1 21 my $input = shift;
117            
118             # requires a 9 digit scalar as input
119 9 50       34 if ($input =~ /^\d{9}$/){
120 9         51 my @exploded_input = split(//, $input);
121            
122 9         15 my $sum = 0;
123 9         11 my $multiplier = 10;
124            
125             # algorithm --> http://en.wikipedia.org/wiki/International_Standard_Book_Number
126 9         26 for (my $i = 0; $i < scalar(@exploded_input); $i++){
127 81         120 $sum += $exploded_input[$i] * $multiplier;;
128 81         162 --$multiplier;
129             }
130            
131 9         17 my $return_value = 11 - ($sum % 11);
132            
133 9 100       55 return $return_value == 10 ? 'X' : $return_value;
134             }
135             else{
136             # error occured
137 0         0 return undef;
138             }
139             }
140              
141             =head2 generateIsbn13CheckDigit
142              
143             Takes a single 12 character string as input.
144              
145             Returns a single character check digit. This value will be a numeric digit.
146              
147             Returns undef on invalid input.
148              
149             =cut
150             sub generateIsbn13CheckDigit{
151 4     4 1 10 my $input = shift;
152            
153             # requires a 12 digit scalar as input
154 4 50       17 if ($input =~ /^\d{12}$/){
155 4         30 my @exploded_input = split(//, $input);
156            
157 4         10 my $sum = 0;
158            
159             # algorithm --> http://en.wikipedia.org/wiki/International_Standard_Book_Number
160 4         16 for (my $i = 0; $i < scalar(@exploded_input); $i++){
161 48 100       80 my $multiplier = $i % 2 == 0 ? 1 : 3;
162 48         129 $sum += $exploded_input[$i] * $multiplier;
163             }
164 4         7 my $return_value = 10 - ($sum % 10);
165 4         27 return $return_value;
166             }
167             else{
168             # error occured
169 0         0 return undef;
170             }
171             }
172              
173             =head2 validateIsbn10
174              
175             Takes a single ISBN10 string as input. The input ISBN10 may contain extra whitespace
176             or formatting characters.
177              
178             Returns a boolean value to indicate whether validation succeeded.
179              
180             Returns undef on invalid input.
181              
182             =cut
183             sub validateIsbn10 {
184 6   100 6 1 17 my $input = &_replaceNonDigitCharacters(shift) || '';
185            
186             # 10 digits of input -- we use $1 below, so that's why we use a strange regex
187 6 100       27 if ($input =~ /^(\d{9})(\d|X)$/i){
188            
189 4         11 my $check_digit = &generateIsbn10CheckDigit($1);
190            
191             # validate the check digit of input, against one we calculate
192             # we have to use string comparison here, because ISBN10 check digits can be 'X'
193 4 100       9 if (&generateIsbn10CheckDigit($1) eq $2){
194 3         18 return 1;
195             }
196             else{
197 1         5 return 0;
198             }
199             }
200             else{
201 2         12 return 0;
202             }
203             }
204              
205             =head2 validateIsbn13
206              
207             Takes a single ISBN13 string as input. The input ISBN13 may contain extra whitespace
208             or formatting characters.
209              
210             Returns a boolean value to indicate whether validation succeeded.
211              
212             Returns undef on invalid input.
213              
214             =cut
215             sub validateIsbn13 {
216 5   100 5 1 16 my $input = &_replaceNonDigitCharacters(shift) || '';
217            
218 5 100       24 if ($input =~ /^(\d{3})(\d{9})(\d)$/){
219            
220             # validate the check digit of input, against one we calculate
221 3 100       21 if (&generateIsbn13CheckDigit("${1}${2}") == $3){
222 2         20 return 1;
223             }
224             else{
225 1         7 return 0;
226             }
227             }
228             else{
229 2         14 return 0;
230             }
231             }
232              
233              
234             # removes any non-digit characters from a single input.
235             # does not affect 'X' at the end of an ISBN10
236             sub _replaceNonDigitCharacters {
237 18   100 18   335 my $input = shift || '';
238            
239             # hack: I forgot about the possible 'X' at the end of isbn10
240             # we just check the input, and if it ends with an x.. we'll append it to our return value
241 18         21 my $suffix_char = '';
242 18 100       62 if ($input =~ /x$/i){
243 2         5 $suffix_char = 'X';
244             }
245            
246             # strip any non-digit characters
247 18         80 $input =~ s/\D//g;
248            
249             # hack: add 'X' back on the end, if required
250 18         27 $input .= $suffix_char;
251            
252 18         76 return $input;
253             }
254              
255             =head1 AUTHOR
256              
257             Derek J. Curtis, C<< >>
258              
259             =head1 BUGS
260              
261             Please report any bugs or feature requests to C, or through
262             the web interface at L. I will be notified, and then you'll
263             automatically be notified of progress on your bug as I make changes.
264              
265              
266              
267              
268             =head1 SUPPORT
269              
270             You can find documentation for this module with the perldoc command.
271              
272             perldoc Summerset::IsbnConverter
273              
274              
275             You can also look for information at:
276              
277             =over 4
278              
279             =item * RT: CPAN's request tracker (report bugs here)
280              
281             L
282              
283             =item * AnnoCPAN: Annotated CPAN documentation
284              
285             L
286              
287             =item * CPAN Ratings
288              
289             L
290              
291             =item * Search CPAN
292              
293             L
294              
295             =back
296              
297              
298             =head1 ACKNOWLEDGEMENTS
299              
300              
301             =head1 LICENSE AND COPYRIGHT
302              
303             Copyright 2012 Derek J. Curtis.
304              
305             This program is free software; you can redistribute it and/or modify it
306             under the terms of the the Artistic License (2.0). You may obtain a
307             copy of the full license at:
308              
309             L
310              
311             Any use, modification, and distribution of the Standard or Modified
312             Versions is governed by this Artistic License. By using, modifying or
313             distributing the Package, you accept this license. Do not use, modify,
314             or distribute the Package, if you do not accept this license.
315              
316             If your Modified Version has been derived from a Modified Version made
317             by someone other than you, you are nevertheless required to ensure that
318             your Modified Version complies with the requirements of this license.
319              
320             This license does not grant you the right to use any trademark, service
321             mark, tradename, or logo of the Copyright Holder.
322              
323             This license includes the non-exclusive, worldwide, free-of-charge
324             patent license to make, have made, use, offer to sell, sell, import and
325             otherwise transfer the Package with respect to any patent claims
326             licensable by the Copyright Holder that are necessarily infringed by the
327             Package. If you institute patent litigation (including a cross-claim or
328             counterclaim) against any party alleging that the Package constitutes
329             direct or contributory patent infringement, then this Artistic License
330             to you shall terminate on the date that such litigation is filed.
331              
332             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
333             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
334             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
335             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
336             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
337             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
338             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
339             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
340              
341              
342             =cut
343              
344             1; # End of Summerset::IsbnConverter