1 package Equinox::Migration::SubfieldMapper;
3 # Copyright 2009-2012, Equinox Software, Inc.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 Equinox::Migration::SubfieldMapper - Generate named-field to MARC tag map from file
32 our $VERSION = '1.005';
37 Using a file as input, E::M::SM generates a mapping of MARC subfields
38 to arbitrary field names, and provides several access mechanisms to
41 use Equinox::Migration::SubfieldMapper;
49 Takes one optional argument, C<file>. If this is speficied, the tag
50 list will be populated as per that file on instantiation.
52 Returns a E::M::SM object.
57 my ($class, %args) = @_;
59 my $self = bless { conf => { mods => undef },
64 die "Argument 'mods' is wrong type\n"
65 unless (ref $args{mods} eq "ARRAY");
66 for my $mod ( @{$args{mods}} )
67 { $self->{conf}{mods}{$mod} = 1 }
72 $self->{conf}{file} = $args{file};
75 die "Can't open file: $!\n";
84 Ask it whether your mapping has various things, and it'll let you know.
86 $sm->has('fieldname') # is this fieldname mapped?
87 $sm->has(901) # are there any mappings for this tag?
88 $sm->has(650,'c') # is this tag/subfield combo mapped?
89 $sm->has('name', 245, 'a') # is this name mapped to 245$a?
91 Returns 1 if true, 0 if false.
93 FIXME: use named params instead of positional
98 my ($self, @chunks) = @_;
99 return undef unless (defined $chunks[0]);
101 if ($chunks[0] =~ /^\d/) {
102 if (defined $chunks[1]) {
103 return 1 if ( defined $self->{tags}{$chunks[0]}{$chunks[1]} );
106 return 1 if ( defined $self->{tags}{$chunks[0]} );
110 if (defined $chunks[2]) {
111 return 1 if ( $self->{fields}{$chunks[0]}{tag} eq $chunks[1] and
112 $self->{fields}{$chunks[0]}{sub} eq $chunks[2] );
114 } elsif (defined $chunks[1]) {
115 return 1 if ( $self->{fields}{$chunks[0]}{tag} eq $chunks[1] );
118 return 1 if ( defined $self->{fields}{$chunks[0]} );
126 Returns an arrayref containing the tags defined in the map.
128 my $tags = $sfm->tags;
129 for my tag ( @{$tags} ) {
130 my $subs = $sfm->subfields($tag);
138 return [ keys %{$self->{tags}} ];
143 Given a tag, return an arrayref of the subfields mapped with that tag.
145 my $tags = $sfm->tags;
146 for my tag ( @{$tags} ) {
147 my $subs = $sfm->subfields($tag);
151 Returns C<undef> if C<tag> is not mapped.
156 my ($self, $tag) = @_;
157 return undef unless $self->has($tag);
158 return [ keys %{$self->{tags}{$tag}} ];
164 Given a tag and subfield code,
166 my $fname = $sm->field(945, 'p')
168 return the name mapped to them. Returns C<undef> if no mapping exists.
173 my ($self, $tag, $sub) = @_;
174 return undef unless (defined $tag and defined $sub);
175 return undef unless $self->has($tag, $sub);
176 return $self->{tags}{$tag}{$sub};
181 With no argument, returns a hashref containing all modifiers for the entire map:
185 tag => [ list_of subs ],
191 Given a fieldname, returns a hashref of the modifiers set on that mapping.
193 $self->mods('fieldname')
195 Returns undef is nothing is defined.
200 my ($self, $field) = @_;
201 return $self->{allmods} unless defined $field;
202 return undef unless $self->has($field);
203 return undef unless (%{ $self->{fields}{$field}{mods} });
204 return $self->{fields}{$field}{mods};
209 Returns the content filters set on a mapping
211 $self->filters('fieldname')
213 If there are no filters, C<undef> will be returned. Else a listref
219 my ($self, $field) = @_;
220 return undef unless $self->has($field);
221 return undef unless ($self->{fields}{$field}{filt});
222 return $self->{fields}{$field}{filt};
227 Returns the separator string set on a mapping. Used only
233 my ($self, $field) = @_;
234 return undef unless $self->has($field);
235 return $self->{fields}{$field}{sep};
238 =head1 MAP CONSTRUCTION METHODS
240 These methods are not generally accessed from user code.
244 Generate initial mapping from file.
249 my ($self, $file) = @_;
251 open TAGFILE, '<', $self->{conf}{file};
257 my @tokens = split /\s+/;
259 my $map = { mods => [], filt => [], sep => ' ' };
260 $map->{field} = shift @tokens;
261 $map->{tag} = shift @tokens;
262 while (defined (my $tok = shift @tokens)) {
263 last if ($tok =~ m/^#/);
264 if ($tok =~ m/^[a-zA-Z]:'/ and $tok !~ /^'$/) {
265 $tok .= ' ' . shift @tokens
266 until ($tokens[0] =~ m/'$/);
267 $tok .= ' ' . shift @tokens;
272 { push @{$map->{mods}}, $tok }
273 elsif ($tok =~ m/^f:/)
274 { push @{$map->{filt}}, $tok }
275 elsif ($tok =~ m/^[a-zA-Z0-9]$/)
276 { $map->{sub} = $tok }
277 elsif ($tok =~ /^c:(.*)$/)
280 { die "Unknown chunk '$tok' at line $.\n" }
288 Add new item to mapping. Not usually called directly from user code.
292 Where C<$map> is a hashref that, at a minimum, looks like
294 { field => "value", tag => NNN, sub => X }
296 and may also have the key/value pairs
304 my ($self, $map) = @_;
306 # trim the mods and filters
308 my $filt = []; my %filt = ();
309 for my $m (@{$map->{mods}}) {
310 die "Modifier collision '$m' at line $." if $mods->{$m};
313 push @{$self->{allmods}{$m}{ $map->{tag} }}, $map->{sub};
315 for my $f (@{$map->{filt}}) {
316 die "Filter collision '$f' at line $." if $filt{$f};
318 push @{$filt}, $f; $filt{$f} = 1;
320 $map->{mods} = $mods;
321 $map->{filt} = $filt;
323 # check bits for validity
324 $self->validate($map);
326 # add data to the fields hash
327 $self->{fields}{ $map->{field} } = { tag => $map->{tag},
329 mods => $map->{mods},
330 filt => $map->{filt},
333 # and to the tags hash
334 $self->{tags}{ $map->{tag} }{ $map->{sub} } = $map->{field};
339 Passed a reference to the hash given to C<add>, validate scans its
340 contents for common errors and dies if there is an issue.
342 * field, tag, and sub are required
343 * fieldnames must start with a letter
344 * fieldnames must be unique
345 * tag must be between 0 and 999
346 * subfield code must be a single alphanumeric character
347 * tag+subfield can only be mapped once
348 * if a list of allowable mod values was given in the call to
349 C<new>, any modifiers must be on that list
354 my ($self, $map) = @_;
356 $.= 1 unless defined $.;
358 die "Required field missing (line $.)\n"
359 unless (defined $map->{field} and defined $map->{tag} and defined $map->{sub});
361 die "Fieldnames must start with letter (line $.)\n"
362 unless ($map->{field} =~ /^[a-zA-z]/);
364 die "Invalid tag (line $.)\n"
365 if ($map->{tag} =~ /[^\d\-]/ or $map->{tag} < 0 or $map->{tag} > 999);
367 die "Invalid subfield code (line $.)\n"
368 if (length $map->{sub} != 1 or $map->{sub} =~ /[^a-zA-Z0-9]/);
370 # test mod names if we have a set to check against
371 if (defined $self->{conf}{mods}) {
372 for my $mod ( keys %{$map->{mods}} ) {
373 die "Modifier '$mod' not allowed\n"
374 unless $self->{conf}{mods}{$mod};
378 die "Fieldnames must be unique (line $.)\n"
379 if (defined $self->{fields}{$map->{field}});
381 die "Subfields cannot be mapped twice (line $.)\n"
382 if (defined $self->{tags}{$map->{tag}}{$map->{sub}});
389 Shawn Boyette, C<< <sboyette at esilibrary.com> >>
393 Please report any bugs or feature requests to the above email address.
397 You can find documentation for this module with the perldoc command.
399 perldoc Equinox::Migration::SubfieldMapper
402 =head1 COPYRIGHT & LICENSE
404 Copyright 2009 Equinox, all rights reserved.
406 This program is free software; you can redistribute it and/or modify it
407 under the same terms as Perl itself.
412 1; # End of Equinox::Migration::SimpleTagList