line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Business::LCCN; |
2
|
5
|
|
|
5
|
|
25742
|
use 5.6.1; |
|
5
|
|
|
|
|
17
|
|
|
5
|
|
|
|
|
243
|
|
3
|
5
|
|
|
5
|
|
29
|
use Carp qw( carp ); |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
312
|
|
4
|
5
|
|
|
5
|
|
7725
|
use Moose; |
|
5
|
|
|
|
|
3351684
|
|
|
5
|
|
|
|
|
45
|
|
5
|
5
|
|
|
5
|
|
40568
|
use Moose::Util::TypeConstraints; |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
65
|
|
6
|
5
|
|
|
5
|
|
12022
|
use Scalar::Util qw( blessed ); |
|
5
|
|
|
|
|
14
|
|
|
5
|
|
|
|
|
333
|
|
7
|
5
|
|
|
5
|
|
6427
|
use URI; |
|
5
|
|
|
|
|
26675
|
|
|
5
|
|
|
|
|
169
|
|
8
|
5
|
|
|
5
|
|
49
|
use strict; |
|
5
|
|
|
|
|
10
|
|
|
5
|
|
|
|
|
163
|
|
9
|
5
|
|
|
5
|
|
30
|
use warnings; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
669
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Business::LCCN - Work with Library of Congress Control Number (LCCN) codes |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 VERSION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Version 1.01 |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=cut |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
our $VERSION = '1.01'; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 SYNOPSIS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
Work with Library of Congress Control Number (LCCN) codes. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use Business::LCCN; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $lccn = Business::LCCN->new('he 68001993 /HE/r692'); |
30
|
|
|
|
|
|
|
if ($lccn) { |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# parse LCCN (common fields) |
33
|
|
|
|
|
|
|
print 'Prefix ', $lccn->prefix, "\n"; # "he" |
34
|
|
|
|
|
|
|
print 'Prefix field ', $lccn->prefix_encoded, "\n"; # "he " |
35
|
|
|
|
|
|
|
print 'Year cataloged ', $lccn->year_cataloged, "\n"; # 1968 |
36
|
|
|
|
|
|
|
print 'Year field ', $lccn->year_encoded, "\n"; # "68" |
37
|
|
|
|
|
|
|
print 'Serial ', $lccn->serial, "\n"; # "001993" |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# stringify LCCN: |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# canonical format: "he 68001993 /HE/r692" |
42
|
|
|
|
|
|
|
print 'Canonical ', $lccn->canonical, "\n"; |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# simple normalized format: "he68001993" |
45
|
|
|
|
|
|
|
print 'Normalized ', $lccn->normalized,"\n"; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# info: URI: "info:lccn:he68001993" |
48
|
|
|
|
|
|
|
print 'Info URI ', $lccn->info_uri, "\n"; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# lccn.loc.gov permalink: "http://lccn.loc.gov/he68001993" |
51
|
|
|
|
|
|
|
print 'Permalink ', $lccn->permalink,"\n"; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# parse LCCN (uncommon fields) |
54
|
|
|
|
|
|
|
print 'LCCN Type ', $lccn->lccn_structure, "\n"; # "A" or "B" |
55
|
|
|
|
|
|
|
print 'Suffix field ', $lccn->suffix_encoded, \n"; # "/HE" |
56
|
|
|
|
|
|
|
print 'Suffix parts ', $lccn->suffix_alphabetic_identifiers, |
57
|
|
|
|
|
|
|
"\n"; # ("HE") |
58
|
|
|
|
|
|
|
print 'Rev year', $lccn->revision_year, "\n"; # 1969 |
59
|
|
|
|
|
|
|
print 'Rev year field ',$lccn->revision_year_encoded, |
60
|
|
|
|
|
|
|
"\n"; # "69" |
61
|
|
|
|
|
|
|
print 'Rev number ', $lccn->revision_number,"\n"; # 2 |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
} else { |
64
|
|
|
|
|
|
|
print " Error : Invalid LCCN \n "; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=cut |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
use overload |
70
|
5
|
|
|
|
|
53
|
'==' => \&_overload_equality, |
71
|
|
|
|
|
|
|
'eq' => \&_overload_equality, |
72
|
5
|
|
|
5
|
|
30
|
'""' => \&_overload_string; |
|
5
|
|
|
|
|
12
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
subtype 'LCCN_Year' => as 'Int' => where { $_ >= 1898 }; |
75
|
|
|
|
|
|
|
subtype 'LCCN_Serial' => as 'Str' => where {m/^\d{6}$/}; |
76
|
|
|
|
|
|
|
enum 'LCCN_Structure' => qw( A B ); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# normalize syntax at http://www.loc.gov/marc/lccn-namespace.html |
79
|
|
|
|
|
|
|
subtype 'LCCN_Normalized' => as 'Str' => |
80
|
|
|
|
|
|
|
where {m/^(?:[a-z](?:[a-z](?:[a-z]|\d{2})?|\d\d)?|\d\d)?\d{8}$/}; |
81
|
|
|
|
|
|
|
subtype 'URI' => as 'Object' => where { $_->isa('URI') }; |
82
|
|
|
|
|
|
|
coerce 'URI' => from 'Str' => via { URI->new($_) }; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
has 'original' => ( is => 'ro', isa => 'Maybe[Str]', required => 1 ); |
85
|
|
|
|
|
|
|
has 'lccn_structure' => |
86
|
|
|
|
|
|
|
( is => 'ro', isa => 'LCCN_Structure', required => 1 ); |
87
|
|
|
|
|
|
|
has 'year_encoded' => ( is => 'ro', isa => 'Str', required => 1 ); |
88
|
|
|
|
|
|
|
has 'year_cataloged' => |
89
|
|
|
|
|
|
|
( is => 'ro', isa => 'Maybe[LCCN_Year]', required => 0 ); |
90
|
|
|
|
|
|
|
has 'prefix' => ( is => 'ro', isa => 'Str', required => 1 ); |
91
|
|
|
|
|
|
|
has 'prefix_encoded' => ( is => 'ro', isa => 'Str', required => 1 ); |
92
|
|
|
|
|
|
|
has 'serial' => ( is => 'ro', isa => 'LCCN_Serial', required => 1 ); |
93
|
|
|
|
|
|
|
has 'suffix_encoded' => |
94
|
|
|
|
|
|
|
( is => 'ro', isa => 'Str', required => 1, default => '' ); |
95
|
|
|
|
|
|
|
has 'suffix_alphabetic_identifiers' => ( |
96
|
|
|
|
|
|
|
is => 'ro', |
97
|
|
|
|
|
|
|
isa => 'ArrayRef[Str]', |
98
|
|
|
|
|
|
|
lazy => 1, |
99
|
|
|
|
|
|
|
default => sub { _suffix_alphabetic_identifiers(@_) }, |
100
|
|
|
|
|
|
|
); |
101
|
|
|
|
|
|
|
has 'revision_year' => ( is => 'ro', isa => 'Maybe[Int]', required => 0 ); |
102
|
|
|
|
|
|
|
has 'revision_year_encoded' => |
103
|
|
|
|
|
|
|
( is => 'ro', isa => 'Str', required => 1, default => '' ); |
104
|
|
|
|
|
|
|
has 'revision_number' => ( is => 'ro', isa => 'Maybe[Int]', required => 0 ); |
105
|
|
|
|
|
|
|
has 'canonical' => ( is => 'ro', |
106
|
|
|
|
|
|
|
isa => 'Str', |
107
|
|
|
|
|
|
|
lazy => 1, |
108
|
|
|
|
|
|
|
default => sub { _canonical(@_) }, |
109
|
|
|
|
|
|
|
); |
110
|
|
|
|
|
|
|
has 'normalized' => ( is => 'ro', |
111
|
|
|
|
|
|
|
isa => 'LCCN_Normalized', |
112
|
|
|
|
|
|
|
lazy => 1, |
113
|
|
|
|
|
|
|
default => sub { _normalized(@_) }, |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
has 'permalink' => ( is => 'ro', |
116
|
|
|
|
|
|
|
isa => 'URI', |
117
|
|
|
|
|
|
|
lazy => 1, |
118
|
|
|
|
|
|
|
default => sub { _permalink(@_) } |
119
|
|
|
|
|
|
|
); |
120
|
|
|
|
|
|
|
has 'info_uri' => ( is => 'ro', |
121
|
|
|
|
|
|
|
isa => 'URI', |
122
|
|
|
|
|
|
|
lazy => 1, |
123
|
|
|
|
|
|
|
default => sub { _info_uri(@_) } |
124
|
|
|
|
|
|
|
); |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
around 'new' => sub { |
127
|
|
|
|
|
|
|
my ( $next, $self, $input, $options ) = @_; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
unless ( $options and ref $options and ref $options eq 'HASH' ) { |
130
|
|
|
|
|
|
|
$options = {}; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
my $emit_warnings = !$options->{no_warnings}; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
if ( !defined $input ) { |
135
|
|
|
|
|
|
|
carp q{Received an undefined value as LCCN input.} if $emit_warnings; |
136
|
|
|
|
|
|
|
return; |
137
|
|
|
|
|
|
|
} elsif ( !length $input ) { |
138
|
|
|
|
|
|
|
carp q{Received an empty string as LCCN input.} if $emit_warnings; |
139
|
|
|
|
|
|
|
return; |
140
|
|
|
|
|
|
|
} else { |
141
|
|
|
|
|
|
|
my %out = ( original => $input ); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# clean up any leading or trailing whitespace |
144
|
|
|
|
|
|
|
$input =~ s/^\s+|\s+$//g; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# accept permalinks |
147
|
|
|
|
|
|
|
$input =~ s{^http://lccn.loc.gov/}{}; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# accept info: uris |
150
|
|
|
|
|
|
|
$input =~ s{^info:lccn/}{}; |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# try LCCN structure B |
153
|
|
|
|
|
|
|
if ($input =~ m{ |
154
|
|
|
|
|
|
|
^ |
155
|
|
|
|
|
|
|
([a-zA-Z\s]{0,2}) # 2-letter alphabetic prefix |
156
|
|
|
|
|
|
|
\s? # space, not officially allowed |
157
|
|
|
|
|
|
|
([2-9]\d\d\d) # 4-letter year |
158
|
|
|
|
|
|
|
(?: |
159
|
|
|
|
|
|
|
-(\d{1,6}) # hyphen plus 1-6 digit serial number |
160
|
|
|
|
|
|
|
| # or... |
161
|
|
|
|
|
|
|
(\d{6}) # 6 digit serial number |
162
|
|
|
|
|
|
|
) |
163
|
|
|
|
|
|
|
$ }x |
164
|
|
|
|
|
|
|
) { |
165
|
|
|
|
|
|
|
$out{lccn_structure} = 'B'; |
166
|
|
|
|
|
|
|
$out{prefix_encoded} = $1; |
167
|
|
|
|
|
|
|
$out{year_encoded} = $2; |
168
|
|
|
|
|
|
|
$out{serial} = ( defined $3 ? $3 : $4 ); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
$out{year_cataloged} = $out{year_encoded}; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# try LCCN structure A |
173
|
|
|
|
|
|
|
} elsif ( |
174
|
|
|
|
|
|
|
$input =~ m{ |
175
|
|
|
|
|
|
|
^ |
176
|
|
|
|
|
|
|
([a-zA-Z\s]{0,3}) # 3-letter alphabetic prefix |
177
|
|
|
|
|
|
|
(\d\d) # 2-letter year |
178
|
|
|
|
|
|
|
(?: |
179
|
|
|
|
|
|
|
-(\d{1,6}) # hyphen plus 1-6 digit serial number |
180
|
|
|
|
|
|
|
| # or... |
181
|
|
|
|
|
|
|
(\d{6}) # 6 digit serial number |
182
|
|
|
|
|
|
|
) |
183
|
|
|
|
|
|
|
(?: |
184
|
|
|
|
|
|
|
(?:\s|(?!\d)) # blank for supplement |
185
|
|
|
|
|
|
|
(/[A-Z]{1,3})* # suffix/alphabetic identifiers |
186
|
|
|
|
|
|
|
(?://? |
187
|
|
|
|
|
|
|
r(\d\d) # revision year encoded |
188
|
|
|
|
|
|
|
(\d*))? # revision number |
189
|
|
|
|
|
|
|
)? |
190
|
|
|
|
|
|
|
$ }x |
191
|
|
|
|
|
|
|
) { |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
$out{lccn_structure} = 'A'; |
194
|
|
|
|
|
|
|
$out{prefix_encoded} = $1; |
195
|
|
|
|
|
|
|
$out{year_encoded} = $2; |
196
|
|
|
|
|
|
|
$out{serial} = ( defined $3 ? $3 : $4 ); |
197
|
|
|
|
|
|
|
$out{suffix_encoded} = ( defined($5) ? $5 : '' ); |
198
|
|
|
|
|
|
|
$out{revision_year_encoded} = $6; |
199
|
|
|
|
|
|
|
$out{revision_number} = ( $7 || undef ); |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# per http://www.loc.gov/marc/marbi/dp/dp84.html and |
202
|
|
|
|
|
|
|
# http://en.wikipedia.org/wiki/Library_of_Congress_Control_Number, |
203
|
|
|
|
|
|
|
# the first LCCNs were assigned in 1898, and there were fewer than |
204
|
|
|
|
|
|
|
# 8000 LCCns issued each of those years |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
if ( $out{year_encoded} eq '98' ) { |
207
|
|
|
|
|
|
|
if ( $out{serial} < 3000 ) { |
208
|
|
|
|
|
|
|
$out{year_cataloged} = 1898; |
209
|
|
|
|
|
|
|
} else { |
210
|
|
|
|
|
|
|
$out{year_cataloged} = 1998; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} elsif ( $out{year_encoded} eq '99' ) { |
213
|
|
|
|
|
|
|
if ( $out{serial} < 6000 ) { |
214
|
|
|
|
|
|
|
$out{year_cataloged} = 1899; |
215
|
|
|
|
|
|
|
} else { |
216
|
|
|
|
|
|
|
$out{year_cataloged} = 1999; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} elsif ( $out{year_encoded} eq '00' ) { |
219
|
|
|
|
|
|
|
if ( $out{serial} < 8000 ) { |
220
|
|
|
|
|
|
|
$out{year_cataloged} = 1900; |
221
|
|
|
|
|
|
|
} else { |
222
|
|
|
|
|
|
|
$out{year_cataloged} = 2000; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
} elsif ( $out{year_encoded} eq '50' ) { |
225
|
|
|
|
|
|
|
$out{lccn_externally_created_flag} = 1; # zzz |
226
|
|
|
|
|
|
|
} elsif ( $out{year_encoded} =~ m/^7\d$/ ) { |
227
|
|
|
|
|
|
|
if ( _verify_7_checksum( $out{year_encoded}, $out{serial} ) ) |
228
|
|
|
|
|
|
|
{ |
229
|
|
|
|
|
|
|
$out{lccn_structure_series} = 7; |
230
|
|
|
|
|
|
|
} else { |
231
|
|
|
|
|
|
|
$out{year_cataloged} = $out{year_encoded} + 1900; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} else { |
234
|
|
|
|
|
|
|
$out{year_cataloged} = $out{year_encoded} + 1900; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
if ( defined $out{revision_year_encoded} |
238
|
|
|
|
|
|
|
and length $out{revision_year_encoded} ) { |
239
|
|
|
|
|
|
|
if ( $out{revision_year_encoded} == 98 |
240
|
|
|
|
|
|
|
or $out{revision_year_encoded} == 99 ) { |
241
|
|
|
|
|
|
|
$out{revision_year} = $out{revision_year_encoded} + 1800; |
242
|
|
|
|
|
|
|
} else { |
243
|
|
|
|
|
|
|
$out{revision_year} = $out{revision_year_encoded} + 1900; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
} else { |
248
|
|
|
|
|
|
|
if ( $input !~ m/\d\d/ ) { |
249
|
|
|
|
|
|
|
carp |
250
|
|
|
|
|
|
|
qq{LCCN input "$input" doesn't contain enough numbers. Please check the input and try again.} |
251
|
|
|
|
|
|
|
if $emit_warnings; |
252
|
|
|
|
|
|
|
} elsif ( $input =~ m/^\s*(0(?:01|10))\b/ ) { |
253
|
|
|
|
|
|
|
carp |
254
|
|
|
|
|
|
|
qq{LCCN input "$input" starts with "$1", suggesting you've copied in part of a MARC record. Please remove MARC record formatting from the LCCN.} |
255
|
|
|
|
|
|
|
if $emit_warnings; |
256
|
|
|
|
|
|
|
} elsif ( $input =~ m/^\s*(\$[ab])\b/ ) { |
257
|
|
|
|
|
|
|
carp |
258
|
|
|
|
|
|
|
qq{LCCN $input "input" starts with "$1", suggesting you've copied in part of a MARC record. Please remove MARC record formatting from the LCCN.} |
259
|
|
|
|
|
|
|
if $emit_warnings; |
260
|
|
|
|
|
|
|
} elsif ( $input =~ m/#/ ) { |
261
|
|
|
|
|
|
|
carp |
262
|
|
|
|
|
|
|
qq{LCCN input "$input" contains "#" characters, which are sometimes used as placeholders for spaces Please remove the "#" characters from the LCCN input.} |
263
|
|
|
|
|
|
|
if $emit_warnings; |
264
|
|
|
|
|
|
|
} elsif ( $input =~ m/^\s*(_[a-z])\b\s*/ ) { |
265
|
|
|
|
|
|
|
carp |
266
|
|
|
|
|
|
|
qq{LCCN input "$input" starts with "$1", which may be MARC formatting. Please remove any such formatting from the LCCN.} |
267
|
|
|
|
|
|
|
if $emit_warnings; |
268
|
|
|
|
|
|
|
} else { |
269
|
|
|
|
|
|
|
carp qq{LCCN input "$input" cannot be parsed.} |
270
|
|
|
|
|
|
|
if $emit_warnings; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
return; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
my $req_prefix_length = ( $out{lccn_structure} eq 'A' ? 3 : 2 ); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# fixup serial |
279
|
|
|
|
|
|
|
$out{serial} = sprintf '%06i', $out{serial}; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# fixup prefix |
282
|
|
|
|
|
|
|
if ( defined $out{prefix_encoded} ) { |
283
|
|
|
|
|
|
|
$out{prefix_encoded} =~ s/^\s+|\s+$//; |
284
|
|
|
|
|
|
|
$out{prefix_encoded} = lc $out{prefix_encoded}; |
285
|
|
|
|
|
|
|
unless ( length $out{prefix_encoded} == $req_prefix_length ) { |
286
|
|
|
|
|
|
|
$out{prefix_encoded} .= ' ' |
287
|
|
|
|
|
|
|
x ( $req_prefix_length - length $out{prefix_encoded} ); |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$out{prefix} = $out{prefix_encoded}; |
291
|
|
|
|
|
|
|
$out{prefix} =~ s/\s+//g; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# fixup suffix |
295
|
|
|
|
|
|
|
if ( !defined $out{suffix_encoded} ) { |
296
|
|
|
|
|
|
|
$out{suffix_encoded} = ''; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
# fixup revision year |
300
|
|
|
|
|
|
|
if ( !defined $out{revision_year_encoded} ) { |
301
|
|
|
|
|
|
|
$out{revision_year_encoded} = ''; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
$next->( $self, \%out ); |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
}; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub _canonical { |
309
|
62
|
|
|
62
|
|
107
|
my $self = shift; |
310
|
62
|
100
|
|
|
|
2280
|
if ( $self->lccn_structure eq 'B' ) { |
|
|
50
|
|
|
|
|
|
311
|
|
|
|
|
|
|
return |
312
|
19
|
|
|
|
|
622
|
sprintf( "%- 2s%4i%06i", |
313
|
|
|
|
|
|
|
$self->prefix, $self->year_encoded, $self->serial ); |
314
|
|
|
|
|
|
|
} elsif ( $self->lccn_structure eq 'A' ) { |
315
|
43
|
|
|
|
|
1568
|
my $string = |
316
|
|
|
|
|
|
|
sprintf( "%- 3s%02i%06i %s", |
317
|
|
|
|
|
|
|
$self->prefix, $self->year_encoded, |
318
|
|
|
|
|
|
|
$self->serial, $self->suffix_encoded |
319
|
|
|
|
|
|
|
); |
320
|
|
|
|
|
|
|
|
321
|
43
|
100
|
|
|
|
1722
|
if ( length $self->revision_year_encoded ) { |
322
|
5
|
100
|
|
|
|
170
|
if ( !length $self->suffix_encoded ) { |
323
|
2
|
|
|
|
|
5
|
$string .= '/'; |
324
|
|
|
|
|
|
|
} |
325
|
5
|
|
|
|
|
177
|
$string .= '/r' . $self->revision_year_encoded; |
326
|
5
|
100
|
|
|
|
173
|
if ( $self->revision_number ) { |
327
|
2
|
|
|
|
|
66
|
$string .= $self->revision_number; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
43
|
|
|
|
|
1678
|
return $string; |
332
|
|
|
|
|
|
|
} else { # should never get here |
333
|
0
|
|
|
|
|
0
|
return ''; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
5
|
|
|
5
|
|
21101
|
no Moose; # remove Moose keywords |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
89
|
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# normalize documented at http://www.loc.gov/marc/lccn-namespace.html |
340
|
|
|
|
|
|
|
# and http://lccn.loc.gov/lccnperm-faq.html |
341
|
|
|
|
|
|
|
sub _normalized { |
342
|
526
|
|
|
526
|
|
830
|
my $self = shift; |
343
|
526
|
|
|
|
|
18993
|
my $string = join '', $self->prefix, $self->year_encoded, $self->serial; |
344
|
526
|
|
|
|
|
1933
|
$string =~ s/[\s-]//g; |
345
|
526
|
|
|
|
|
23147
|
return $string; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# permalink syntax documented at http://lccn.loc.gov/lccnperm-faq.html |
349
|
|
|
|
|
|
|
sub _permalink { |
350
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
351
|
1
|
|
|
|
|
31
|
return URI->new( 'http://lccn.loc.gov/' . $self->normalized ); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# info: uri syntax documented at http://www.loc.gov/standards/uri/info.html |
355
|
|
|
|
|
|
|
sub _info_uri { |
356
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
357
|
1
|
|
|
|
|
76
|
return URI->new( 'info:lccn/' . $self->normalized ); |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _overload_string { |
361
|
62
|
|
|
62
|
|
106224
|
my $self = shift; |
362
|
62
|
|
|
|
|
2399
|
return $self->canonical; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub _overload_equality { |
366
|
464
|
|
|
464
|
|
359867
|
my ( $self, $other ) = @_; |
367
|
|
|
|
|
|
|
|
368
|
464
|
|
|
|
|
685
|
my $other_lccn; |
369
|
464
|
100
|
66
|
|
|
4827
|
if ( ref($other) and blessed($other) and $other->isa('Business::LCCN') ) { |
|
|
|
66
|
|
|
|
|
370
|
232
|
|
|
|
|
432
|
$other_lccn = $other; |
371
|
|
|
|
|
|
|
} else { |
372
|
232
|
|
|
|
|
1444
|
$other_lccn = new Business::LCCN($other); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
464
|
50
|
|
|
|
469645
|
if ( !defined $other_lccn ) { |
376
|
0
|
|
|
|
|
0
|
return 0; |
377
|
|
|
|
|
|
|
} else { |
378
|
464
|
|
|
|
|
19346
|
return ( $self->normalized eq $other_lccn->normalized ); |
379
|
|
|
|
|
|
|
} |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# returns a list of all the suffix alphabetic identifiers |
383
|
|
|
|
|
|
|
sub _suffix_alphabetic_identifiers { |
384
|
62
|
|
|
62
|
|
121
|
my $self = shift; |
385
|
62
|
100
|
|
|
|
216
|
if ( length $self->{suffix_encoded} ) { |
386
|
3
|
|
|
|
|
123
|
my @identifiers = $self->suffix_encoded =~ m{\b([A-Z]+)\b}; |
387
|
3
|
|
|
|
|
125
|
return \@identifiers; |
388
|
|
|
|
|
|
|
} else { |
389
|
59
|
|
|
|
|
2245
|
return []; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub _verify_7_checksum { |
394
|
407
|
|
|
407
|
|
829
|
my ( $year_encoded, $serial ) = @_; |
395
|
407
|
100
|
66
|
|
|
3370
|
unless ( $year_encoded =~ m/^\d{2}$/ |
396
|
|
|
|
|
|
|
and $serial =~ m/^\d{6}$/ ) { |
397
|
41
|
|
|
|
|
141
|
return 0; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
366
|
|
|
|
|
3111
|
my @year_digits = split //, $year_encoded; |
401
|
366
|
|
|
|
|
1619
|
my @serial_digits = split //, $serial; |
402
|
|
|
|
|
|
|
|
403
|
366
|
|
|
|
|
2464
|
my $product |
404
|
|
|
|
|
|
|
= $year_digits[0] * 7 |
405
|
|
|
|
|
|
|
+ $year_digits[1] * 8 |
406
|
|
|
|
|
|
|
+ $serial_digits[0] * 4 |
407
|
|
|
|
|
|
|
+ $serial_digits[1] * 6 |
408
|
|
|
|
|
|
|
+ $serial_digits[2] * 3 |
409
|
|
|
|
|
|
|
+ $serial_digits[3] * 5 |
410
|
|
|
|
|
|
|
+ $serial_digits[4] * 2 |
411
|
|
|
|
|
|
|
+ $serial_digits[5] * 1; |
412
|
|
|
|
|
|
|
|
413
|
366
|
100
|
|
|
|
1049
|
if ( $product % 11 == 0 ) { |
414
|
2
|
|
|
|
|
12
|
return 1; |
415
|
|
|
|
|
|
|
} else { |
416
|
364
|
|
|
|
|
1584
|
return 0; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 INTERFACE |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 Methods |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=head3 C<new> |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
The new method takes a single encoded LCCN string, in a variety of |
427
|
|
|
|
|
|
|
formats -- with or without hyphens, with proper spacing or without. |
428
|
|
|
|
|
|
|
Examples: |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
"89-1234", "89-001234", "89001234", "2002-1234", "2002-001234", |
431
|
|
|
|
|
|
|
"2002001234", " 89001234 ", " 2002001234", "a89-1234", |
432
|
|
|
|
|
|
|
"a89-001234", "a89001234", "a2002-1234", "a2002-001234", |
433
|
|
|
|
|
|
|
"a2002001234", "a 89001234 ", "a 2002001234", "ab98-1234", |
434
|
|
|
|
|
|
|
"ab98-001234", "ab98001234", "ab2002-1234", "ab2002-001234", |
435
|
|
|
|
|
|
|
"ab2002001234", "ab 98001234 ", "ab 2002001234", "abc89-1234", |
436
|
|
|
|
|
|
|
"abc89-001234", "abc89001234", "abc89001234 ", permalinks URLs |
437
|
|
|
|
|
|
|
like "http://lccn.loc.gov/2002001234" and info URIs like |
438
|
|
|
|
|
|
|
"info:lccn/2002001234" |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Returns a Business::LCCN object, or undef if the string can't be |
441
|
|
|
|
|
|
|
parsed as a valid LCCN. If the string can't be parsed, C<new> will |
442
|
|
|
|
|
|
|
warn with a diagnostic message explaining why the string was invalid. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
C<new> can also take an optional hashref of options as a second |
445
|
|
|
|
|
|
|
parameter. The only option supported is C<no_warnings>, which will |
446
|
|
|
|
|
|
|
disable any diagnostic warnings explaining why a candidate LCCN string |
447
|
|
|
|
|
|
|
was invalid: |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# returns undef, issues warning about input not containing any digits |
450
|
|
|
|
|
|
|
$foo = LCCN->new('x'); |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# returns undef, but does not issue any additional warning |
453
|
|
|
|
|
|
|
$bar = LCCN->new( 'x', { no_warnings => 1 } ); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=head3 LCCN attributes |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=head3 C<lccn_structure> |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
LCCN structure type, either "A" (issued 1898-2000) or "B" (issued |
460
|
|
|
|
|
|
|
2001-). |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
=head3 C<prefix> |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
LCCN's alphabetic prefix, 1-3 characters long. Returns an empty string |
465
|
|
|
|
|
|
|
if LCCN has no prefix. |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=head3 C<prefix_encoded> |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
The prefix as encoded, either two (structure A) or three (structure B) |
470
|
|
|
|
|
|
|
characters long, space-padded. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
=head3 C<year_cataloged> |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
The year a book was cataloged. Returns an undef in cases where the |
475
|
|
|
|
|
|
|
cataloging year in unclear. For example, LCCN S<" 75425165 //r75"> |
476
|
|
|
|
|
|
|
has a cataloged year of 1975. |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head3 C<year_encoded> |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
A two (structure A) or four (structure B) digit string typically |
481
|
|
|
|
|
|
|
representing the year the book was cataloged, but sometimes serving as |
482
|
|
|
|
|
|
|
a checksum, or a source code. For example, LCCN S<" 75425165 //r75"> |
483
|
|
|
|
|
|
|
has an encoded year field of S<"75">. |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=head3 C<serial> |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
A six-digit number zero-padded serial number. For example, LCCN |
488
|
|
|
|
|
|
|
S<" 75425165 //r75"> has a serial number of S<"425165">. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head3 C<suffix_alphabetic_identifiers> |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Structure A LCCNs can include one or more 1-3 character |
493
|
|
|
|
|
|
|
suffix/alphabetic identifiers. Returns a list of all identifiers |
494
|
|
|
|
|
|
|
present. For example, for LCCN S<" 79139101 /AC/MN">, |
495
|
|
|
|
|
|
|
suffix_alphabetic_identifiers returns ('AC', 'MN'). |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=head3 C<suffix_encoded> |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
The LCCN's suffix/alphabetic identifier field, as encoded in the LCCN. |
500
|
|
|
|
|
|
|
Returns an empty string if no suffix present. |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=head3 C<revision_year> |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
Structure A LCCNs can include a revision date in their |
505
|
|
|
|
|
|
|
bibliographic records. Returns the four-digit year the record was |
506
|
|
|
|
|
|
|
revised, or undef if not present. For example, LCCN |
507
|
|
|
|
|
|
|
S<" 75425165 //r75"> has a revision year of 1975. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=head3 C<revision_year_encoded> |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
The two-letter revision date, as encoded in structure A LCCNs. Returns |
512
|
|
|
|
|
|
|
an empty string if no revision year present. For example, LCCN |
513
|
|
|
|
|
|
|
S<" 75425165 //r75"> has a revision year of C<"75">. |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
=head3 C<revision_number> |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Some structure A LCCNs have a revision year and number, |
518
|
|
|
|
|
|
|
representing the number of times the record has been revised. For |
519
|
|
|
|
|
|
|
example, LCCN S<" 75425165 //r752"> has revision_number 2. Returns |
520
|
|
|
|
|
|
|
undef if not present. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head3 LCCN representations |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head3 C<canonical> |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Returns the canonical 12+ character default representation of an |
527
|
|
|
|
|
|
|
LCCN. For example, S<" 85000002 "> is the canonical representation of |
528
|
|
|
|
|
|
|
S<"85000002">, S<"85-000002">, S<"85-2">, S<" 85000002">. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head3 C<normalized> |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Returns the normalized 9-12 character representation of an LCCN. |
533
|
|
|
|
|
|
|
Normalized LCCNs are often used in URIs and Internet-era |
534
|
|
|
|
|
|
|
representations. For example, S<"n2001050268"> is the normalized |
535
|
|
|
|
|
|
|
representation of S<"n 85-000002 ">, S<"n85-2">, S<"n 85-0000002">. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head3 C<info_uri> |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Returns the info: URI for an LCCN. For example, the URI for LCCN |
540
|
|
|
|
|
|
|
S<"n 85-000002 "> is S<"info:lccn/n85000002">. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head3 C<original> |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Returns the original representation of the LCCN, as passed to C<new>. |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=head3 C<permalink> |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Returns the Library of Congress permalink URL for an LCCN. For |
549
|
|
|
|
|
|
|
example, the permalink URL for LCCN S<"n 85-000002 "> is |
550
|
|
|
|
|
|
|
S<"http://lccn.loc.gov/n85000002">. |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=head2 Operator overloading |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head3 C<""> |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
In string context, Business::LCCN objects stringify as the |
557
|
|
|
|
|
|
|
canonical representation of the LCCN. |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=head3 C<eq>, C<==> |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
Business::LCCN objects can be compared to other Business::LCCN |
562
|
|
|
|
|
|
|
objects or LCCN strings. |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
=head1 SEE ALSO |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
L<Business::ISBN>, L<http://www.loc.gov/marc/lccn_structure.html>, |
567
|
|
|
|
|
|
|
L<http://lccn.loc.gov/>, |
568
|
|
|
|
|
|
|
L<http://www.loc.gov/standards/uri/info.html>, |
569
|
|
|
|
|
|
|
L<http://en.wikipedia.org/wiki/Library_of_Congress_Control_Number> |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
Running C<new> on invalid input may generate warnings, unless the |
574
|
|
|
|
|
|
|
C<no_warnings> option is set. |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
=head1 AUTHOR |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
Anirvan Chatterjee, C<< <anirvan at cpan.org> >> |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=head1 BUGS |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
583
|
|
|
|
|
|
|
C<bug-business-lccn at rt.cpan.org>, or through the web interface at |
584
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Business-LCCN>. I |
585
|
|
|
|
|
|
|
will be notified, and then you'll automatically be notified of |
586
|
|
|
|
|
|
|
progress on your bug as I make changes. |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=head1 SUPPORT |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
perldoc Business::LCCN |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
You can also look for information at: |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=over 4 |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Business-LCCN> |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
L<http://annocpan.org/dist/Business-LCCN> |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=item * CPAN Ratings |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
L<http://cpanratings.perl.org/d/Business-LCCN> |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item * Search CPAN |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
L<http://search.cpan.org/dist/Business-LCCN> |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=back |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Copyright 2008 Anirvan Chatterjee, all rights reserved. |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
621
|
|
|
|
|
|
|
under the same terms as Perl itself. |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=cut |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
1; # End of Business::LCCN |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
# Local Variables: |
628
|
|
|
|
|
|
|
# mode: perltidy |
629
|
|
|
|
|
|
|
# End: |