sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
- my $self = shift;
+ my $self = shift;
$self->{_mfhd_CAPTIONS} = {};
$self->{_mfhd_COMPRESSIBLE} = (substr($self->leader, 17, 1) =~ /[45]/);
foreach my $field ('853', '854', '855') {
- my $captions = {};
- foreach my $caption ($self->field($field)) {
- my $cap_id;
-
- $cap_id = $caption->subfield('8') || '0';
-
- if (exists $captions->{$cap_id}) {
- carp "Multiple MFHD captions with label '$cap_id'";
- }
-
- $captions->{$cap_id} = new MFHD::Caption($caption);
- if ($self->{_mfhd_COMPRESSIBLE}) {
- $self->{_mfhd_COMPRESSIBLE} &&= $captions->{$cap_id}->compressible;
- }
- }
- $self->{_mfhd_CAPTIONS}->{$field} = $captions;
+ my $captions = {};
+ foreach my $caption ($self->field($field)) {
+ my $cap_id;
+
+ $cap_id = $caption->subfield('8') || '0';
+
+ if (exists $captions->{$cap_id}) {
+ carp "Multiple MFHD captions with label '$cap_id'";
+ }
+
+ $captions->{$cap_id} = new MFHD::Caption($caption);
+ if ($self->{_mfhd_COMPRESSIBLE}) {
+ $self->{_mfhd_COMPRESSIBLE} &&=
+ $captions->{$cap_id}->compressible;
+ }
+ }
+ $self->{_mfhd_CAPTIONS}->{$field} = $captions;
}
foreach my $field ('863', '864', '865') {
- my $holdings = {};
- my $cap_field;
-
- ($cap_field = $field) =~ s/6/5/;
-
- foreach my $hfield ($self->field($field)) {
- my ($linkage, $link_id, $seqno);
- my $holding;
-
- $linkage = $hfield->subfield('8');
- ($link_id, $seqno) = split(/\./, $linkage);
-
- if (!exists $holdings->{$link_id}) {
- $holdings->{$link_id} = {};
- }
- $holding = new MFHD::Holding($seqno, $hfield,
- $self->{_mfhd_CAPTIONS}->{$cap_field}->{$link_id});
- $holdings->{$link_id}->{$seqno} = $holding;
-
- if ($self->{_mfhd_COMPRESSIBLE}) {
- $self->{_mfhd_COMPRESSIBLE} &&= $holding->validate;
- }
- }
- $self->{_mfhd_HOLDINGS}->{$field} = $holdings;
+ my $holdings = {};
+ my $cap_field;
+
+ ($cap_field = $field) =~ s/6/5/;
+
+ foreach my $hfield ($self->field($field)) {
+ my ($linkage, $link_id, $seqno);
+ my $holding;
+
+ $linkage = $hfield->subfield('8');
+ ($link_id, $seqno) = split(/\./, $linkage);
+
+ if (!exists $holdings->{$link_id}) {
+ $holdings->{$link_id} = {};
+ }
+ $holding =
+ new MFHD::Holding($seqno, $hfield,
+ $self->{_mfhd_CAPTIONS}->{$cap_field}->{$link_id});
+ $holdings->{$link_id}->{$seqno} = $holding;
+
+ if ($self->{_mfhd_COMPRESSIBLE}) {
+ $self->{_mfhd_COMPRESSIBLE} &&= $holding->validate;
+ }
+ }
+ $self->{_mfhd_HOLDINGS}->{$field} = $holdings;
}
- bless ($self, $class);
+ bless($self, $class);
return $self;
}
}
sub captions {
- my $self = shift;
+ my $self = shift;
my $field = shift;
- return sort keys %{$self->{_mfhd_CAPTIONS}->{$field}}
+ return sort keys %{$self->{_mfhd_CAPTIONS}->{$field}};
}
sub holdings {
- my $self = shift;
+ my $self = shift;
my $field = shift;
my $capid = shift;
- return sort {$a->seqno <=> $b->seqno} values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}};
+ return
+ sort { $a->seqno <=> $b->seqno }
+ values %{$self->{_mfhd_HOLDINGS}->{$field}->{$capid}};
}
1;
use base 'MARC::Field';
-sub new
-{
- my $proto = shift;
- my $class = ref($proto) || $proto;
- my $self = shift;
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = shift;
my $last_enum = undef;
- $self->{_mfhdc_ENUMS} = {};
- $self->{_mfhdc_CHRONS} = {};
- $self->{_mfhdc_PATTERN} = {};
- $self->{_mfhdc_COPY} = undef;
- $self->{_mfhdc_UNIT} = undef;
- $self->{_mfhdc_COMPRESSIBLE} = 1; # until proven otherwise
+ $self->{_mfhdc_ENUMS} = {};
+ $self->{_mfhdc_CHRONS} = {};
+ $self->{_mfhdc_PATTERN} = {};
+ $self->{_mfhdc_COPY} = undef;
+ $self->{_mfhdc_UNIT} = undef;
+ $self->{_mfhdc_COMPRESSIBLE} = 1; # until proven otherwise
foreach my $subfield ($self->subfields) {
- my ($key, $val) = @$subfield;
- if ($key eq '8') {
- $self->{LINK} = $val;
- } elsif ($key =~ /[a-h]/) {
- # Enumeration Captions
- $self->{_mfhdc_ENUMS}->{$key} = {CAPTION => $val,
- COUNT => undef,
- RESTART => undef};
- if ($key =~ /[ag]/) {
- $last_enum = undef;
- } else {
- $last_enum = $key;
- }
- } elsif ($key =~ /[i-m]/) {
- # Chronology captions
- $self->{_mfhdc_CHRONS}->{$key} = $val;
- } elsif ($key eq 'u') {
- # Bib units per next higher enumeration level
- carp('$u specified for top-level enumeration')
- unless defined($last_enum);
- $self->{_mfhdc_ENUMS}->{$last_enum}->{COUNT} = $val;
- } elsif ($key eq 'v') {
- carp '$v specified for top-level enumeration'
- unless defined($last_enum);
- $self->{_mfhdc_ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r');
- } elsif ($key =~ /[npwz]/) {
- # Publication Pattern info ('o' == type of unit, 'q'..'t' undefined)
- $self->{_mfhdc_PATTERN}->{$key} = $val;
- } elsif ($key =~ /x/) {
- # Calendar change can have multiple comma-separated values
- $self->{_mfhdc_PATTERN}->{x} = [split /,/, $val];
- } elsif ($key eq 'y') {
- $self->{_mfhdc_PATTERN}->{y} = {}
- unless exists $self->{_mfhdc_PATTERN}->{y};
- update_pattern($self, $val);
- } elsif ($key eq 'o') {
- # Type of unit
- $self->{_mfhdc_UNIT} = $val;
- } elsif ($key eq 't') {
- $self->{_mfhdc_COPY} = $val;
- } else {
- carp "Unknown caption subfield '$key'";
- }
+ my ($key, $val) = @$subfield;
+ if ($key eq '8') {
+ $self->{LINK} = $val;
+ } elsif ($key =~ /[a-h]/) {
+ # Enumeration Captions
+ $self->{_mfhdc_ENUMS}->{$key} = {
+ CAPTION => $val,
+ COUNT => undef,
+ RESTART => undef
+ };
+ if ($key =~ /[ag]/) {
+ $last_enum = undef;
+ } else {
+ $last_enum = $key;
+ }
+ } elsif ($key =~ /[i-m]/) {
+ # Chronology captions
+ $self->{_mfhdc_CHRONS}->{$key} = $val;
+ } elsif ($key eq 'u') {
+ # Bib units per next higher enumeration level
+ carp('$u specified for top-level enumeration')
+ unless defined($last_enum);
+ $self->{_mfhdc_ENUMS}->{$last_enum}->{COUNT} = $val;
+ } elsif ($key eq 'v') {
+ carp '$v specified for top-level enumeration'
+ unless defined($last_enum);
+ $self->{_mfhdc_ENUMS}->{$last_enum}->{RESTART} = ($val eq 'r');
+ } elsif ($key =~ /[npwz]/) {
+ # Publication Pattern info ('o' == type of unit, 'q'..'t' undefined)
+ $self->{_mfhdc_PATTERN}->{$key} = $val;
+ } elsif ($key =~ /x/) {
+ # Calendar change can have multiple comma-separated values
+ $self->{_mfhdc_PATTERN}->{x} = [split /,/, $val];
+ } elsif ($key eq 'y') {
+ $self->{_mfhdc_PATTERN}->{y} = {}
+ unless exists $self->{_mfhdc_PATTERN}->{y};
+ update_pattern($self, $val);
+ } elsif ($key eq 'o') {
+ # Type of unit
+ $self->{_mfhdc_UNIT} = $val;
+ } elsif ($key eq 't') {
+ $self->{_mfhdc_COPY} = $val;
+ } else {
+ carp "Unknown caption subfield '$key'";
+ }
}
# subsequent levels of enumeration (primary and alternate)
# of "issues" per "volume", or whether numbering of issues
# restarts, then we can't compress.
foreach my $key ('b', 'c', 'd', 'e', 'f', 'h') {
- if (exists $self->{_mfhdc_ENUMS}->{$key}) {
- my $pattern = $self->{_mfhdc_ENUMS}->{$key};
- if (!$pattern->{RESTART} || !$pattern->{COUNT}
- || ($pattern->{COUNT} eq 'var')
- || ($pattern->{COUNT} eq 'und')) {
- $self->{_mfhdc_COMPRESSIBLE} = 0;
- last;
- }
- }
+ if (exists $self->{_mfhdc_ENUMS}->{$key}) {
+ my $pattern = $self->{_mfhdc_ENUMS}->{$key};
+ if ( !$pattern->{RESTART}
+ || !$pattern->{COUNT}
+ || ($pattern->{COUNT} eq 'var')
+ || ($pattern->{COUNT} eq 'und')) {
+ $self->{_mfhdc_COMPRESSIBLE} = 0;
+ last;
+ }
+ }
}
my $pat = $self->{_mfhdc_PATTERN};
# Sanity check publication frequency vs publication pattern:
# if the frequency is a number, then the pattern better
# have that number of values associated with it.
- if (exists($pat->{w}) && ($pat->{w} =~ /^\d+$/)
- && ($pat->{w} != scalar(@{$pat->{y}->{p}}))) {
- carp("Caption::new: publication frequency '$pat->{w}' != publication pattern @{$pat->{y}->{p}}");
+ if ( exists($pat->{w})
+ && ($pat->{w} =~ /^\d+$/)
+ && ($pat->{w} != scalar(@{$pat->{y}->{p}}))) {
+ carp(
+"Caption::new: publication frequency '$pat->{w}' != publication pattern @{$pat->{y}->{p}}"
+ );
}
-
# If there's a $x subfield and a $j, then it's compressible
if (exists $pat->{x} && exists $self->{_mfhdc_CHRONS}->{'j'}) {
- $self->{_mfhdc_COMPRESSIBLE} = 1;
+ $self->{_mfhdc_COMPRESSIBLE} = 1;
}
- bless ($self, $class);
+ bless($self, $class);
return $self;
}
sub update_pattern {
- my $self = shift;
- my $val = shift;
+ my $self = shift;
+ my $val = shift;
my $pathash = $self->{_mfhdc_PATTERN}->{y};
my ($pubcode, $pat) = unpack("a1a*", $val);
}
sub decode_pattern {
- my $self = shift;
+ my $self = shift;
my $pattern = $self->{_mfhdc_PATTERN}->{y};
# XXX WRITE ME (?)
sub chrons {
my $self = shift;
- my $key = shift;
+ my $key = shift;
if (exists $self->{_mfhdc_CHRONS}->{$key}) {
- return $self->{_mfhdc_CHRONS}->{$key};
+ return $self->{_mfhdc_CHRONS}->{$key};
} else {
- return undef;
+ return undef;
}
}
sub capfield {
my $self = shift;
- my $key = shift;
+ my $key = shift;
if (exists $self->{_mfhdc_ENUMS}->{$key}) {
- return $self->{_mfhdc_ENUMS}->{$key};
+ return $self->{_mfhdc_ENUMS}->{$key};
} elsif (exists $self->{_mfhdc_CHRONS}->{$key}) {
- return $self->{_mfhdc_CHRONS}->{$key};
+ return $self->{_mfhdc_CHRONS}->{$key};
} else {
- return undef;
+ return undef;
}
}
sub capstr {
my $self = shift;
- my $key = shift;
- my $val = $self->capfield($key);
+ my $key = shift;
+ my $val = $self->capfield($key);
if (ref $val) {
- return $val->{CAPTION};
+ return $val->{CAPTION};
} else {
- return $val;
+ return $val;
}
}
my $self = shift;
# There is always a '$a' subfield in well-formed fields.
- return 0 if exists $self->{_mfhdc_CHRONS}->{i}
- || exists $self->{_mfhdc_PATTERN}->{x};
+ return 0
+ if exists $self->{_mfhdc_CHRONS}->{i}
+ || exists $self->{_mfhdc_PATTERN}->{x};
- foreach my $key ('a' .. 'f') {
- my $enum;
+ foreach my $key ('a'..'f') {
+ my $enum;
- last if !exists $self->{_mfhdc_ENUMS}->{$key};
+ last if !exists $self->{_mfhdc_ENUMS}->{$key};
- $enum = $self->{_mfhdc_ENUMS}->{$key};
- return 0 if defined $enum->{COUNT} || defined $enum->{RESTART};
+ $enum = $self->{_mfhdc_ENUMS}->{$key};
+ return 0 if defined $enum->{COUNT} || defined $enum->{RESTART};
}
return (exists $self->{_mfhdc_PATTERN}->{w});
}
sub regularity_match {
- my $self = shift;
+ my $self = shift;
my $pubcode = shift;
- my @date = @_;
+ my @date = @_;
# we can't match something that doesn't exist.
return 0 if !exists $self->{_mfhdc_PATTERN}->{y}->{$pubcode};
foreach my $regularity (@{$self->{_mfhdc_PATTERN}->{y}->{$pubcode}}) {
- my $chroncode= substr($regularity, 0, 1);
- my $matchfunc = MFHD::Date::dispatch($chroncode);
- my @pats = split(/,/, substr($regularity, 1));
-
- if (!defined $matchfunc) {
- carp "Unrecognized chroncode '$chroncode'";
- return 0;
- }
-
- # XXX WRITE ME
- foreach my $pat (@pats) {
- $pat =~ s|/.+||; # If it's a combined date, match the start
- if ($matchfunc->($pat, @date)) {
- return 1;
- }
- }
+ my $chroncode = substr($regularity, 0, 1);
+ my $matchfunc = MFHD::Date::dispatch($chroncode);
+ my @pats = split(/,/, substr($regularity, 1));
+
+ if (!defined $matchfunc) {
+ carp "Unrecognized chroncode '$chroncode'";
+ return 0;
+ }
+
+ # XXX WRITE ME
+ foreach my $pat (@pats) {
+ $pat =~ s|/.+||; # If it's a combined date, match the start
+ if ($matchfunc->($pat, @date)) {
+ return 1;
+ }
+ }
}
return 0;
my $self = shift;
my @date = @_;
-# printf("# is_omitted: testing date %s: %d\n", join('/', @date),
-# $self->regularity_match('o', @date));
+ # printf("# is_omitted: testing date %s: %d\n", join('/', @date),
+ # $self->regularity_match('o', @date));
return $self->regularity_match('o', @date);
}
}
sub enum_is_combined {
- my $self = shift;
+ my $self = shift;
my $subfield = shift;
- my $iss = shift;
- my $level = ord($subfield) - ord('a') + 1;
+ my $iss = shift;
+ my $level = ord($subfield) - ord('a') + 1;
return 0 if !exists $self->{_mfhdc_PATTERN}->{y}->{c};
foreach my $regularity (@{$self->{_mfhdc_PATTERN}->{y}->{c}}) {
- next unless $regularity =~ m/^e$level/o;
+ next unless $regularity =~ m/^e$level/o;
- my @pats = split(/,/, substr($regularity, 2));
+ my @pats = split(/,/, substr($regularity, 2));
- foreach my $pat (@pats) {
- $pat =~ s|/.+||; # if it's a combined issue, match the start
- return 1 if ($iss eq $pat);
- }
+ foreach my $pat (@pats) {
+ $pat =~ s|/.+||; # if it's a combined issue, match the start
+ return 1 if ($iss eq $pat);
+ }
}
return 0;
}
-
# Test to see if $dt1 is on or after $dt2
# if length(@{$dt2} == 2, then just month/day are compared
# if length(@{$dt2} == 1, then just the months are compared
# printf("# on_or_after(%s, %s): ", join('/', @{$dt1}), join('/', @{$dt2}));
- foreach my $i (0..(scalar(@{$dt2})-1)) {
- if ($dt1->[$i] > $dt2->[$i]) {
-# printf("after - pass\n");
- # $dt1 occurs AFTER $dt2
- return 1;
- } elsif ($dt1->[$i] < $dt2->[$i]) {
-# printf("before - fail\n");
- # $dt1 occurs BEFORE $dt2
- return 0;
- }
- # both are still equal, keep going
+ foreach my $i (0..(scalar(@{$dt2}) - 1)) {
+ if ($dt1->[$i] > $dt2->[$i]) {
+ # printf("after - pass\n");
+ # $dt1 occurs AFTER $dt2
+ return 1;
+ } elsif ($dt1->[$i] < $dt2->[$i]) {
+ # printf("before - fail\n");
+ # $dt1 occurs BEFORE $dt2
+ return 0;
+ }
+ # both are still equal, keep going
}
# We fell out of the loop with them being equal, so it's 'on'
-# printf("on - pass\n");
+ # printf("on - pass\n");
return 1;
}
sub calendar_increment {
- my $self = shift;
- my $cur = shift;
- my $new = shift;
+ my $self = shift;
+ my $cur = shift;
+ my $new = shift;
my $cal_change = $self->calendar_change;
my $month;
my $day;
# A calendar change is defined, need to check if it applies
if (scalar(@{$new}) == 1) {
- carp "Can't calculate date change for ", $self->as_string;
- return 0;
+ carp "Can't calculate date change for ", $self->as_string;
+ return 0;
}
foreach my $change (@{$cal_change}) {
- my $incr;
-
- if (length($change) == 2) {
- $month = $change;
- } elsif (length($change) == 4) {
- ($month, $day) = unpack("a2a2", $change);
- }
-
-# printf("# calendar_increment('%s', '%s'): change on '%s/%s'\n",
-# join('/', @{$cur}), join('/', @{$new}),
-# $month, defined($day) ? $day : 'UNDEF');
-
- if ($cur->[0] == $new->[0]) {
- # Same year, so a 'simple' month/day comparison will be fine
- $incr = (!on_or_after([$cur->[1], $cur->[2]], [$month, $day])
- && on_or_after([$new->[1], $new->[2]], [$month, $day]));
- } else {
- # @cur is in the year before @new. There are
- # two possible cases for the calendar change date that
- # indicate that it's time to change the volume:
- # (1) the change date is AFTER @cur in the year, or
- # (2) the change date is BEFORE @new in the year.
- #
- # -------|------|------X------|------|
- # @cur (1) Jan 1 (2) @new
-
- $incr = (on_or_after([$new->[1], $new->[2]], [$month, $day])
- || !on_or_after([$cur->[1], $cur->[2]], [$month, $day]));
- }
- return $incr if $incr;
+ my $incr;
+
+ if (length($change) == 2) {
+ $month = $change;
+ } elsif (length($change) == 4) {
+ ($month, $day) = unpack("a2a2", $change);
+ }
+
+ # printf("# calendar_increment('%s', '%s'): change on '%s/%s'\n",
+ # join('/', @{$cur}), join('/', @{$new}),
+ # $month, defined($day) ? $day : 'UNDEF');
+
+ if ($cur->[0] == $new->[0]) {
+ # Same year, so a 'simple' month/day comparison will be fine
+ $incr =
+ ( !on_or_after([$cur->[1], $cur->[2]], [$month, $day])
+ && on_or_after([$new->[1], $new->[2]], [$month, $day]));
+ } else {
+ # @cur is in the year before @new. There are
+ # two possible cases for the calendar change date that
+ # indicate that it's time to change the volume:
+ # (1) the change date is AFTER @cur in the year, or
+ # (2) the change date is BEFORE @new in the year.
+ #
+ # -------|------|------X------|------|
+ # @cur (1) Jan 1 (2) @new
+
+ $incr =
+ (on_or_after([$new->[1], $new->[2]], [$month, $day])
+ || !on_or_after([$cur->[1], $cur->[2]], [$month, $day]));
+ }
+ return $incr if $incr;
}
return 0;
}
sub next_date {
- my $self = shift;
- my $next = shift;
+ my $self = shift;
+ my $next = shift;
my $carry = shift;
- my @keys = @_;
+ my @keys = @_;
my @cur;
my @new;
- my @newend; # only used for combined issues
+ my @newend; # only used for combined issues
my $incr;
- my $reg = $self->{_mfhdc_REGULARITY};
+ my $reg = $self->{_mfhdc_REGULARITY};
my $pattern = $self->{_mfhdc_PATTERN};
- my $freq = $pattern->{w};
+ my $freq = $pattern->{w};
foreach my $i (0..$#keys) {
- $cur[$i] = $next->{$keys[$i]} if exists $next->{$keys[$i]};
+ $cur[$i] = $next->{$keys[$i]} if exists $next->{$keys[$i]};
}
# If the current issue has a combined date (eg, May/June)
$cur[-1] =~ s|^[^/]+/||;
if (defined $pattern->{y}->{p}) {
- # There is a $y publication pattern defined in the record:
- # use it to calculate the next issue date.
-
- foreach my $pubpat (@{$pattern->{y}->{p}}, @{$pattern->{y}->{c}}) {
- my $chroncode = substr($pubpat, 0, 1);
- my $genfunc = MFHD::Date::generator($chroncode);
- my @pats = split(/,/, substr($pubpat, 1));
-
- next if $chroncode eq 'e';
-
- if (!defined $genfunc) {
- carp "Unrecognized chroncode '$chroncode'";
- return undef;
- }
-
- foreach my $pat (@pats) {
- my $combined = $pat =~ m|/|;
- my ($start, $end);
- my @candidate;
-
-# printf("# next_date: generating with pattern '%s'\n", $pat);
-
- if ($combined) {
- ($start, $end) = split('/', $pat, 2);
- } else {
- ($start, $end) = (undef, undef);
- }
-
- @candidate = $genfunc->($start || $pat, @cur);
-
- while ($self->is_omitted(@candidate)) {
-# printf("# pubpat omitting date '%s'\n",
-# join('/', @candidate));
- @candidate = $genfunc->($start || $pat, @candidate);
- }
-
-# printf("# testing new candidate '%s' against '%s'\n",
-# join('/', @candidate), join('/', @new));
-
- if (!defined($new[0])
- || !on_or_after(\@candidate, \@new)) {
- # first time through the loop
- # or @candidate is before @new =>
- # @candidate is the next issue.
- @new = @candidate;
- if (defined $end) {
- @newend = $genfunc->($end, @cur);
- } else {
- $newend[0] = undef;
- }
-
-# printf("# selecting candidate date '%s'\n", join('/', @new));
- }
- }
- }
-
- if (defined($newend[0])) {
- # The best match was a combined issue
- foreach my $i (0..$#new) {
- # don't combine identical fields
- next if $new[$i] eq $newend[$i];
- $new[$i] .= '/' . $newend[$i];
- }
- }
+ # There is a $y publication pattern defined in the record:
+ # use it to calculate the next issue date.
+
+ foreach my $pubpat (@{$pattern->{y}->{p}}, @{$pattern->{y}->{c}}) {
+ my $chroncode = substr($pubpat, 0, 1);
+ my $genfunc = MFHD::Date::generator($chroncode);
+ my @pats = split(/,/, substr($pubpat, 1));
+
+ next if $chroncode eq 'e';
+
+ if (!defined $genfunc) {
+ carp "Unrecognized chroncode '$chroncode'";
+ return undef;
+ }
+
+ foreach my $pat (@pats) {
+ my $combined = $pat =~ m|/|;
+ my ($start, $end);
+ my @candidate;
+
+ # printf("# next_date: generating with pattern '%s'\n", $pat);
+
+ if ($combined) {
+ ($start, $end) = split('/', $pat, 2);
+ } else {
+ ($start, $end) = (undef, undef);
+ }
+
+ @candidate = $genfunc->($start || $pat, @cur);
+
+ while ($self->is_omitted(@candidate)) {
+ # printf("# pubpat omitting date '%s'\n",
+ # join('/', @candidate));
+ @candidate = $genfunc->($start || $pat, @candidate);
+ }
+
+ # printf("# testing new candidate '%s' against '%s'\n",
+ # join('/', @candidate), join('/', @new));
+
+ if ( !defined($new[0])
+ || !on_or_after(\@candidate, \@new)) {
+ # first time through the loop
+ # or @candidate is before @new =>
+ # @candidate is the next issue.
+ @new = @candidate;
+ if (defined $end) {
+ @newend = $genfunc->($end, @cur);
+ } else {
+ $newend[0] = undef;
+ }
+
+ # printf("# selecting candidate date '%s'\n", join('/', @new));
+ }
+ }
+ }
+
+ if (defined($newend[0])) {
+ # The best match was a combined issue
+ foreach my $i (0..$#new) {
+ # don't combine identical fields
+ next if $new[$i] eq $newend[$i];
+ $new[$i] .= '/' . $newend[$i];
+ }
+ }
}
if (scalar @new == 0) {
- # There was no suitable publication pattern defined,
- # so use the $w frequency to figure out the next date
- if (!defined($freq)) {
- carp "Undefined frequency in next_date!";
- } elsif (!MFHD::Date::can_increment($freq)) {
- carp "Don't know how to deal with frequency '$freq'!";
- } else {
- #
- # One of the standard defined issue frequencies
- #
- @new = MFHD::Date::incr_date($freq, @cur);
-
- while ($self->is_omitted(@new)) {
- @new = MFHD::Date::incr_date($freq, @new);
- }
-
- if ($self->is_combined(@new)) {
- my @second_date = MFHD::Date::incr_date($freq, @new);
-
- # I am cheating: This code assumes that only the smallest
- # time increment is combined. So, no "Apr 15/May 1" allowed.
- $new[-1] = $new[-1] . '/' . $second_date[-1];
- }
- }
+ # There was no suitable publication pattern defined,
+ # so use the $w frequency to figure out the next date
+ if (!defined($freq)) {
+ carp "Undefined frequency in next_date!";
+ } elsif (!MFHD::Date::can_increment($freq)) {
+ carp "Don't know how to deal with frequency '$freq'!";
+ } else {
+ #
+ # One of the standard defined issue frequencies
+ #
+ @new = MFHD::Date::incr_date($freq, @cur);
+
+ while ($self->is_omitted(@new)) {
+ @new = MFHD::Date::incr_date($freq, @new);
+ }
+
+ if ($self->is_combined(@new)) {
+ my @second_date = MFHD::Date::incr_date($freq, @new);
+
+ # I am cheating: This code assumes that only the smallest
+ # time increment is combined. So, no "Apr 15/May 1" allowed.
+ $new[-1] = $new[-1] . '/' . $second_date[-1];
+ }
+ }
}
for my $i (0..$#new) {
- $next->{$keys[$i]} = $new[$i];
+ $next->{$keys[$i]} = $new[$i];
}
# Figure out if we need to adust volume number
# right now just use the $carry that was passed in.
# in long run, need to base this on ($carry or date_change)
if ($carry) {
- # if $carry is set, the date doesn't matter: we're not
- # going to increment the v. number twice at year-change.
- $next->{a} += $carry;
+ # if $carry is set, the date doesn't matter: we're not
+ # going to increment the v. number twice at year-change.
+ $next->{a} += $carry;
} elsif (defined $pattern->{x}) {
- $next->{a} += $self->calendar_increment(\@cur, \@new);
+ $next->{a} += $self->calendar_increment(\@cur, \@new);
}
}
# First handle any "alternative enumeration", since they're
# a lot simpler, and don't depend on the the calendar
foreach my $key ('h', 'g') {
- next if !exists $next->{$key};
- if (!$self->capstr($key)) {
- warn "Holding data exists for $key, but no caption specified";
- $next->{$key} += 1;
- last;
- }
-
- my $cap = $self->capfield($key);
- if ($cap->{RESTART} && $cap->{COUNT}
- && ($next->{$key} == $cap->{COUNT})) {
- $next->{$key} = 1;
- } else {
- $next->{$key} += 1;
- last;
- }
+ next if !exists $next->{$key};
+ if (!$self->capstr($key)) {
+ warn "Holding data exists for $key, but no caption specified";
+ $next->{$key} += 1;
+ last;
+ }
+
+ my $cap = $self->capfield($key);
+ if ( $cap->{RESTART}
+ && $cap->{COUNT}
+ && ($next->{$key} == $cap->{COUNT})) {
+ $next->{$key} = 1;
+ } else {
+ $next->{$key} += 1;
+ last;
+ }
}
}
# particular publication pattern for the given level of enumeration
# returns the pattern string or undef
sub enum_pubpat {
- my $self = shift;
+ my $self = shift;
my $level = shift;
return undef if !exists $self->{_mfhdc_PATTERN}->{y}->{p};
foreach my $reg (@{$self->{_mfhdc_PATTERN}->{y}->{p}}) {
- if ($reg =~ m/^e$level/o) {
- return substr($reg, 2);
- }
+ if ($reg =~ m/^e$level/o) {
+ return substr($reg, 2);
+ }
}
return undef;
}
# least once. If there's no subfield b, then there's only a single
# level of enumeration, so we just add one to it and we're done.
if (exists $next->{b}) {
- $carry = 0;
+ $carry = 0;
} else {
- $carry = 1;
+ $carry = 1;
}
foreach my $key (reverse('b'..'f')) {
- my $level;
- my $pubpat;
-
- next if !exists $next->{$key};
-
- # If the current issue has a combined issue number (eg, 2/3)
- # get rid of the first issue number and base the calculation
- # on the final issue number in the combined issue.
- if ($next->{$key} =~ m|/|) {
- $next->{$key} =~ s|^[^/]+/||;
- }
-
- $level = ord($key) - ord('a') + 1; # enumeration level
-
- $pubpat = $self->enum_pubpat($level);
-
- if ($pubpat) {
-# printf("# next_enum: found pubpat '%s' for subfield '%s'\n",
-# $pubpat, $key);
- my @pats = split(/,/, $pubpat);
-
- # If we fall out the bottom of the loop, then $carry
- # will still be 1, and we will reset the current
- # level to the first value in @pats and increment
- # then next higher level.
- $carry = 1;
-
- foreach my $pat (@pats) {
- my $combined = $pat =~ m|/|;
- my $end;
-
-# printf("# next_enum: checking current '%s' against pat '%s'\n",
-# $next->{$key}, $pat);
-
- if ($combined) {
- ($pat, $end) = split('/', $pat, 2);
- } else {
- $end = undef;
- }
-
- if ($pat > $next->{$key}) {
- $carry = 0;
- $next->{$key} = $pat;
- $next->{$key} .= '/' . $end if $end;
-# printf("# next_enum: selecting new issue no. %s\n", $next->{$key});
- last; # We've found the correct next issue number
- }
- }
- if ($carry) {
- $next->{$key} = $pats[0];
- } else {
- last; # exit the top level loop because we're done
- }
-
- } else {
- # No enumeration publication pattern specified for this level,
- # just keed adding one.
-
- if (!$self->capstr($key)) {
- # Just assume that it increments continuously and give up
- warn "Holding data exists for $key, but no caption specified";
- $next->{$key} += 1;
- $carry = 0;
- last;
- }
-
-# printf("# next_enum: no publication pattern, using frequency\n");
-
- my $cap = $self->capfield($key);
- if ($cap->{RESTART} && $cap->{COUNT}
- && ($next->{$key} eq $cap->{COUNT})) {
- $next->{$key} = 1;
- $carry = 1;
- } else {
- # If I don't need to "carry" beyond here, then I just increment
- # this level of the enumeration and stop looping, since the
- # "next" hash has been initialized with the current values
-
- $next->{$key} += 1;
- $carry = 0;
- }
-
- # You can't have a combined issue that spans two volumes: no.12/1
- # is forbidden
- if ($self->enum_is_combined($key, $next->{$key})) {
- $next->{$key} .= '/' . ($next->{$key} + 1);
- }
-
- last if !$carry;
- }
+ my $level;
+ my $pubpat;
+
+ next if !exists $next->{$key};
+
+ # If the current issue has a combined issue number (eg, 2/3)
+ # get rid of the first issue number and base the calculation
+ # on the final issue number in the combined issue.
+ if ($next->{$key} =~ m|/|) {
+ $next->{$key} =~ s|^[^/]+/||;
+ }
+
+ $level = ord($key) - ord('a') + 1; # enumeration level
+
+ $pubpat = $self->enum_pubpat($level);
+
+ if ($pubpat) {
+ # printf("# next_enum: found pubpat '%s' for subfield '%s'\n",
+ # $pubpat, $key);
+ my @pats = split(/,/, $pubpat);
+
+ # If we fall out the bottom of the loop, then $carry
+ # will still be 1, and we will reset the current
+ # level to the first value in @pats and increment
+ # then next higher level.
+ $carry = 1;
+
+ foreach my $pat (@pats) {
+ my $combined = $pat =~ m|/|;
+ my $end;
+
+ # printf("# next_enum: checking current '%s' against pat '%s'\n",
+ # $next->{$key}, $pat);
+
+ if ($combined) {
+ ($pat, $end) = split('/', $pat, 2);
+ } else {
+ $end = undef;
+ }
+
+ if ($pat > $next->{$key}) {
+ $carry = 0;
+ $next->{$key} = $pat;
+ $next->{$key} .= '/' . $end if $end;
+ # printf("# next_enum: selecting new issue no. %s\n", $next->{$key});
+ last; # We've found the correct next issue number
+ }
+ }
+ if ($carry) {
+ $next->{$key} = $pats[0];
+ } else {
+ last; # exit the top level loop because we're done
+ }
+
+ } else {
+ # No enumeration publication pattern specified for this level,
+ # just keed adding one.
+
+ if (!$self->capstr($key)) {
+ # Just assume that it increments continuously and give up
+ warn "Holding data exists for $key, but no caption specified";
+ $next->{$key} += 1;
+ $carry = 0;
+ last;
+ }
+
+ # printf("# next_enum: no publication pattern, using frequency\n");
+
+ my $cap = $self->capfield($key);
+ if ( $cap->{RESTART}
+ && $cap->{COUNT}
+ && ($next->{$key} eq $cap->{COUNT})) {
+ $next->{$key} = 1;
+ $carry = 1;
+ } else {
+ # If I don't need to "carry" beyond here, then I just increment
+ # this level of the enumeration and stop looping, since the
+ # "next" hash has been initialized with the current values
+
+ $next->{$key} += 1;
+ $carry = 0;
+ }
+
+ # You can't have a combined issue that spans two volumes: no.12/1
+ # is forbidden
+ if ($self->enum_is_combined($key, $next->{$key})) {
+ $next->{$key} .= '/' . ($next->{$key} + 1);
+ }
+
+ last if !$carry;
+ }
}
# The easy part is done. There are two things left to do:
# or because $carry is set because of the above loop
if (!$self->subfield('i')) {
- # The simple case: if there is no chronology specified
- # then just check $carry and return
- $next->{'a'} += $carry;
+ # The simple case: if there is no chronology specified
+ # then just check $carry and return
+ $next->{'a'} += $carry;
} else {
- # Figure out date of next issue, then decide if we need
- # to adjust top level enumeration based on that
- $self->next_date($next, $carry, ('i'..'m'));
+ # Figure out date of next issue, then decide if we need
+ # to adjust top level enumeration based on that
+ $self->next_date($next, $carry, ('i'..'m'));
}
}
sub next {
- my $self = shift;
+ my $self = shift;
my $holding = shift;
- my $next = {};
+ my $next = {};
# Initialize $next with current enumeration & chronology, then
# we can just operate on $next, based on the contents of the caption
if ($self->enumeration_is_chronology) {
- foreach my $key ('a' .. 'h') {
- $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}
- if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
- }
- $self->next_date($next, 0, ('a' .. 'h'));
+ foreach my $key ('a'..'h') {
+ $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}
+ if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
+ }
+ $self->next_date($next, 0, ('a'..'h'));
- return $next;
+ return $next;
}
- foreach my $key ('a' .. 'h') {
- $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}
- if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
+ foreach my $key ('a'..'h') {
+ $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS}
+ if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
}
foreach my $key ('i'..'m') {
- $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}
- if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
+ $next->{$key} = $holding->{_mfhdh_SUBFIELDS}->{$key}
+ if defined $holding->{_mfhdh_SUBFIELDS}->{$key};
}
if (exists $next->{'h'}) {
- $self->next_alt_enum($next);
+ $self->next_alt_enum($next);
}
$self->next_enum($next);
- return($next);
+ return ($next);
}
1;
use base 'Exporter';
-our @EXPORT_OK =qw(dispatch generator incr_date can_increment);
+our @EXPORT_OK = qw(dispatch generator incr_date can_increment);
my %daynames = (
- 'mo' => 1,
- 'tu' => 2,
- 'we' => 3,
- 'th' => 4,
- 'fr' => 5,
- 'sa' => 6,
- 'su' => 7,
- );
-
-my $daypat = '(mo|tu|we|th|fr|sa|su)';
+ 'mo' => 1,
+ 'tu' => 2,
+ 'we' => 3,
+ 'th' => 4,
+ 'fr' => 5,
+ 'sa' => 6,
+ 'su' => 7,
+);
+
+my $daypat = '(mo|tu|we|th|fr|sa|su)';
my $weekpat = '(99|98|97|00|01|02|03|04|05)';
my $weeknopat;
-my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)';
+my $monthpat = '(01|02|03|04|05|06|07|08|09|10|11|12)';
my $seasonpat = '(21|22|23|24)';
# Initialize $weeknopat to be '(01|02|03|...|51|52|53)'
$weeknopat .= '53)';
sub match_day {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
# Translate daynames into day of week for DateTime
# also used to check if dayname is valid.
if (exists $daynames{$pat}) {
- # dd
- # figure out day of week for date and compare
- my $dt = DateTime->new(year => $date[0],
- month => $date[1],
- day => $date[2]);
- return ($dt->day_of_week == $daynames{$pat});
+ # dd
+ # figure out day of week for date and compare
+ my $dt = DateTime->new(
+ year => $date[0],
+ month => $date[1],
+ day => $date[2]
+ );
+ return ($dt->day_of_week == $daynames{$pat});
} elsif (length($pat) == 2) {
- # DD
- return $pat == $date[2];
+ # DD
+ return $pat == $date[2];
} elsif (length($pat) == 4) {
- # MMDD
- my ($mon, $day) = unpack("a2a2", $pat);
+ # MMDD
+ my ($mon, $day) = unpack("a2a2", $pat);
- return (($mon == $date[1]) && ($day == $date[2]));
+ return (($mon == $date[1]) && ($day == $date[2]));
} else {
- carp "Invalid day pattern '$pat'";
- return 0;
+ carp "Invalid day pattern '$pat'";
+ return 0;
}
}
sub subsequent_day {
my $pat = shift;
my @cur = @_;
- my $dt = DateTime->new(year => $cur[0],
- month => $cur[1],
- day => $cur[2]);
+ my $dt = DateTime->new(
+ year => $cur[0],
+ month => $cur[1],
+ day => $cur[2]
+ );
-# printf("# subsequent_day: pat='%s' cur='%s'\n", $pat, join('/', @cur));
+ # printf("# subsequent_day: pat='%s' cur='%s'\n", $pat, join('/', @cur));
if (exists $daynames{$pat}) {
- # dd: published on the given weekday
- my $dow = $dt->day_of_week;
- my $corr = ($daynames{$pat} - $dow + 7) % 7;
-
- if ($dow == $daynames{$pat}) {
- # the next one is one week hence
- $dt->add(days => 7);
- } else {
- # the next one is later this week,
- # or it is next week (ie, on or after next Monday)
- # $corr will take care of it.
- $dt->add(days => $corr);
- }
- @cur = ($dt->year, $dt->month, $dt->day);
+ # dd: published on the given weekday
+ my $dow = $dt->day_of_week;
+ my $corr = ($daynames{$pat} - $dow + 7) % 7;
+
+ if ($dow == $daynames{$pat}) {
+ # the next one is one week hence
+ $dt->add(days => 7);
+ } else {
+ # the next one is later this week,
+ # or it is next week (ie, on or after next Monday)
+ # $corr will take care of it.
+ $dt->add(days => $corr);
+ }
+ @cur = ($dt->year, $dt->month, $dt->day);
} elsif (length($pat) == 2) {
- # DD: published on the give day of every month
- if ($dt->day >= $pat) {
- # current date is on or after $pat: next one is next month
- $dt->set(day => $pat);
- $dt->add(months => 1);
- @cur = ($dt->year, $dt->month, $dt->day);
- } else {
- # current date is before $pat: set day to pattern
- $cur[2] = $pat;
- }
+ # DD: published on the give day of every month
+ if ($dt->day >= $pat) {
+ # current date is on or after $pat: next one is next month
+ $dt->set(day => $pat);
+ $dt->add(months => 1);
+ @cur = ($dt->year, $dt->month, $dt->day);
+ } else {
+ # current date is before $pat: set day to pattern
+ $cur[2] = $pat;
+ }
} elsif (length($pat) == 4) {
- # MMDD: published on the given day of the given month
- my ($mon, $day) = unpack("a2a2", $pat);
-
- if (on_or_after($mon, $day, $cur[1], $cur[2])) {
- # Current date is on or after pattern; next one is next year
- $cur[0] += 1;
- }
- # Year is now right. Either it's next year (because of on_or_after)
- # or it's this year, because the current date is NOT on or after
- # the pattern. Just fix the month and day
- $cur[1] = $mon;
- $cur[2] = $day;
+ # MMDD: published on the given day of the given month
+ my ($mon, $day) = unpack("a2a2", $pat);
+
+ if (on_or_after($mon, $day, $cur[1], $cur[2])) {
+ # Current date is on or after pattern; next one is next year
+ $cur[0] += 1;
+ }
+ # Year is now right. Either it's next year (because of on_or_after)
+ # or it's this year, because the current date is NOT on or after
+ # the pattern. Just fix the month and day
+ $cur[1] = $mon;
+ $cur[2] = $day;
} else {
- carp "Invalid day pattern '$pat'";
- return undef;
+ carp "Invalid day pattern '$pat'";
+ return undef;
}
foreach my $i (0..$#cur) {
- $cur[$i] = '0' . (0+$cur[$i]) if $cur[$i] < 10;
+ $cur[$i] = '0' . (0 + $cur[$i]) if $cur[$i] < 10;
}
-# printf("subsequent_day: returning '%s'\n", join('/', @cur));
+ # printf("subsequent_day: returning '%s'\n", join('/', @cur));
return @cur;
}
-
# Calculate date of 3rd Friday of the month (for example)
# 1-5: count from beginning of month
# 99-97: count back from end of month
sub nth_week_of_month {
- my $dt = shift;
+ my $dt = shift;
my $week = shift;
- my $day = shift;
+ my $day = shift;
my ($nth_day, $dow);
-# printf("# nth_week_of_month(dt, '%s', '%s')\n", $week, $day);
+ # printf("# nth_week_of_month(dt, '%s', '%s')\n", $week, $day);
if (0 < $week && $week <= 5) {
- $nth_day = $dt->clone->set(day => 1);
+ $nth_day = $dt->clone->set(day => 1);
} elsif ($week >= 97) {
- $nth_day = DateTime->last_day_of_month(year => $dt->year,
- month => $dt->month);
+ $nth_day = DateTime->last_day_of_month(
+ year => $dt->year,
+ month => $dt->month
+ );
} else {
- return undef;
+ return undef;
}
$dow = $nth_day->day_of_week();
# then use that day for the calculations, otherwise, just use
# the day of the week of the original date (the date $dt).
if (defined($day)) {
- $day = $daynames{$day};
+ $day = $daynames{$day};
} else {
- $day = $dt->day_of_week;
+ $day = $dt->day_of_week;
}
if ($week <= 5) {
- # count forwards
- $nth_day->add(days => ($day - $dow + 7) % 7,
- weeks=> $week - 1);
+ # count forwards
+ $nth_day->add(
+ days => ($day - $dow + 7) % 7,
+ weeks => $week - 1
+ );
} else {
- # count backwards
- $nth_day->subtract(days => ($day - $dow + 7) % 7);
+ # count backwards
+ $nth_day->subtract(days => ($day - $dow + 7) % 7);
- # 99: last week of month, 98: second last, etc.
- for (my $i = 99 - $week; $i > 0; $i--) {
- $nth_day->subtract(weeks => 1);
- }
+ # 99: last week of month, 98: second last, etc.
+ for (my $i = 99 - $week; $i > 0; $i--) {
+ $nth_day->subtract(weeks => 1);
+ }
}
# There is no nth "day" in the month!
# of month, week, and day
#
sub check_date {
- my $dt = shift;
- my $month = shift;
+ my $dt = shift;
+ my $month = shift;
my $weekno = shift;
- my $day = shift;
+ my $day = shift;
-# printf("check_date('%s', '%s', '%s')\n", $month, $weekno, $day || '');
+ # printf("check_date('%s', '%s', '%s')\n", $month, $weekno, $day || '');
if (!defined $day) {
- # MMWW
- return (($dt->month == $month)
- && (($dt->week_of_month == $weekno)
- || ($weekno >= 97
- && ($dt->week_of_month == nth_week_of_month($dt, $weekno, $day)->week_of_month))));
+ # MMWW
+ return (
+ ($dt->month == $month)
+ && (
+ ($dt->week_of_month == $weekno)
+ || (
+ $weekno >= 97
+ && ($dt->week_of_month ==
+ nth_week_of_month($dt, $weekno, $day)->week_of_month)
+ )
+ )
+ );
}
# simple cases first
if ($daynames{$day} != $dt->day_of_week) {
- # if it's the wrong day of the week, rest doesn't matter
- return 0;
+ # if it's the wrong day of the week, rest doesn't matter
+ return 0;
}
if (!defined $month) {
- # WWdd
- return (($weekno == 0) # Every week
- || ($dt->weekday_of_month == $weekno) # this week
- || (($weekno >= 97) && ($dt->weekday_of_month == nth_week_of_month($dt, $weekno, $day)->weekday_of_month)));
+ # WWdd
+ return (
+ ($weekno == 0) # Every week
+ || ($dt->weekday_of_month == $weekno) # this week
+ || (
+ ($weekno >= 97)
+ && ($dt->weekday_of_month ==
+ nth_week_of_month($dt, $weekno, $day)->weekday_of_month)
+ )
+ );
}
# MMWWdd
if ($month != $dt->month) {
- # If it's the wrong month, then we're done
- return 0;
+ # If it's the wrong month, then we're done
+ return 0;
}
# It's the right day of the week
# It's the right month
- if (($weekno == 0) ||($weekno == $dt->weekday_of_month)) {
- # If this matches, then we're counting from the beginning
- # of the month and it matches and we're done.
- return 1;
+ if (($weekno == 0) || ($weekno == $dt->weekday_of_month)) {
+ # If this matches, then we're counting from the beginning
+ # of the month and it matches and we're done.
+ return 1;
}
# only case left is that the week number is counting from
# the end of the month: eg, second last wednesday
- return (($weekno >= 97)
- && (nth_week_of_month($dt, $weekno, $day)->weekday_of_month == $dt->weekday_of_month));
+ return (
+ ($weekno >= 97)
+ && (nth_week_of_month($dt, $weekno, $day)->weekday_of_month ==
+ $dt->weekday_of_month)
+ );
}
sub match_week {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
- my $dt = DateTime->new(year => $date[0],
- month => $date[1],
- day => $date[2]);
+ my $dt = DateTime->new(
+ year => $date[0],
+ month => $date[1],
+ day => $date[2]
+ );
if ($pat =~ m/^$weekpat$daypat$/) {
- # WWdd: 03we = Third Wednesday
- return check_date($dt, undef, $1, $2);
+ # WWdd: 03we = Third Wednesday
+ return check_date($dt, undef, $1, $2);
} elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) {
- # MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME
- return check_date($dt, $1, $2, $3);
+ # MMWWdd: 0599tu Last Tuesday in May XXX WRITE ME
+ return check_date($dt, $1, $2, $3);
} elsif ($pat =~ m/^$monthpat$weekpat$/) {
- # MMWW: 1204: Fourth week in December XXX WRITE ME
- return check_date($dt, $1, $2, undef);
+ # MMWW: 1204: Fourth week in December XXX WRITE ME
+ return check_date($dt, $1, $2, undef);
} else {
- carp "invalid week pattern '$pat'";
- return 0;
+ carp "invalid week pattern '$pat'";
+ return 0;
}
}
my $candidate;
my $dt;
-# printf("# subsequent_week('%s', '%s', '%s', '%s')\n", $pat, @cur);
+ # printf("# subsequent_week('%s', '%s', '%s', '%s')\n", $pat, @cur);
- $dt = DateTime->new(year => $cur[0],
- month=> $cur[1],
- day => $cur[2]);
+ $dt = DateTime->new(
+ year => $cur[0],
+ month => $cur[1],
+ day => $cur[2]
+ );
if ($pat =~ m/^$weekpat$daypat$/o) {
- # WWdd: published on given weekday of given week of every month
- my ($week, $day) = ($1, $2);
-
-# printf("# subsequent_week: matched /WWdd/: week='%s', day='%s'\n",
-# $week, $day);
-
- if ($week eq '00') {
- # Every week
- $candidate = $dt->clone;
-
- if ($dt->day_of_week == $daynames{$day}) {
- # Current is right day, next one is a week hence
- $candidate->add(days => 7);
- } else {
- $candidate->add(days => ($daynames{$day} - $dt->day_of_week + 7) % 7);
- }
- } else {
- # 3rd Friday of the month (eg)
- $candidate = nth_week_of_month($dt, $week, $day);
- }
-
- if ($candidate <= $dt) {
- # If the n'th week of the month happens on before the
- # current issue, then the next issue is published next
- # month, otherwise, it's published this month.
- # This will never happen for the "00: every week" pattern
+ # WWdd: published on given weekday of given week of every month
+ my ($week, $day) = ($1, $2);
+
+ # printf("# subsequent_week: matched /WWdd/: week='%s', day='%s'\n",
+ # $week, $day);
+
+ if ($week eq '00') {
+ # Every week
+ $candidate = $dt->clone;
+
+ if ($dt->day_of_week == $daynames{$day}) {
+ # Current is right day, next one is a week hence
+ $candidate->add(days => 7);
+ } else {
+ $candidate->add(
+ days => ($daynames{$day} - $dt->day_of_week + 7) % 7);
+ }
+ } else {
+ # 3rd Friday of the month (eg)
+ $candidate = nth_week_of_month($dt, $week, $day);
+ }
+
+ if ($candidate <= $dt) {
+# If the n'th week of the month happens on before the
+# current issue, then the next issue is published next
+# month, otherwise, it's published this month.
+# This will never happen for the "00: every week" pattern
# printf("# subsequent_week: candidate (%s) occurs on or before current date (%s)\n",
# join('/', $candidate->year, $candidate->month, $candidate->day),
# join('/', $dt->year, $dt->month, $dt->day));
- $candidate->set(day => 1);
- $candidate->add(months => 1);
- $candidate = nth_week_of_month($candidate, $week, $day);
- }
+ $candidate->set(day => 1);
+ $candidate->add(months => 1);
+ $candidate = nth_week_of_month($candidate, $week, $day);
+ }
} elsif ($pat =~ m/^$monthpat$weekpat$daypat$/) {
- # MMWWdd: published on given weekday of given week of given month
- my ($month, $week, $day) = ($1, $2, $3);
+ # MMWWdd: published on given weekday of given week of given month
+ my ($month, $week, $day) = ($1, $2, $3);
# printf("# subsequent_week: matched /MMWWdd/: month='%s', week='%s', day='%s'\n",
# $month, $week, $day);
- $candidate = DateTime->new(year => $dt->year,
- month=> $month,
- day => 1);
- $candidate = nth_week_of_month($candidate, $week, $day);
- if ($candidate <= $dt) {
- # We've missed it for this year, next one that matches
- # will be next year
- $candidate->add(years => 1)->set(day => 1);
- $candidate = nth_week_of_month($candidate, $week, $day);
- }
+ $candidate = DateTime->new(
+ year => $dt->year,
+ month => $month,
+ day => 1
+ );
+ $candidate = nth_week_of_month($candidate, $week, $day);
+ if ($candidate <= $dt) {
+ # We've missed it for this year, next one that matches
+ # will be next year
+ $candidate->add(years => 1)->set(day => 1);
+ $candidate = nth_week_of_month($candidate, $week, $day);
+ }
} elsif ($pat =~ m/^$monthpat$weekpat$/) {
- # MMWW: published during given week of given month
- my ($month, $week) = ($1, $2);
-
- $candidate = nth_week_of_month(DateTime->new(year => $dt->year,
- month=> $month,
- day => 1),
- $week,
- 'th');
- if ($candidate <= $dt) {
- # Already past the pattern date this year, move to next year
- $candidate->add(years => 1)->set(day => 1);
- $candidate = nth_week_of_month($candidate, $week, 'th');
- }
+ # MMWW: published during given week of given month
+ my ($month, $week) = ($1, $2);
+
+ $candidate = nth_week_of_month(
+ DateTime->new(
+ year => $dt->year,
+ month => $month,
+ day => 1
+ ),
+ $week, 'th'
+ );
+ if ($candidate <= $dt) {
+ # Already past the pattern date this year, move to next year
+ $candidate->add(years => 1)->set(day => 1);
+ $candidate = nth_week_of_month($candidate, $week, 'th');
+ }
} else {
- carp "invalid week pattern '$pat'";
- return undef;
+ carp "invalid week pattern '$pat'";
+ return undef;
}
$cur[0] = $candidate->year;
$cur[2] = $candidate->day;
foreach my $i (0..$#cur) {
- $cur[$i] = '0' . (0+$cur[$i]) if $cur[$i] < 10;
+ $cur[$i] = '0' . (0 + $cur[$i]) if $cur[$i] < 10;
}
return @cur;
}
sub match_month {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
return ($pat eq $date[1]);
my @cur = @_;
if ($cur[1] >= $pat) {
- # Current date is on or after the patter date, so the next
- # occurence is next year
- $cur[0] += 1;
+ # Current date is on or after the patter date, so the next
+ # occurence is next year
+ $cur[0] += 1;
}
# The year is right, just set the month to the pattern date.
}
sub match_season {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
return ($pat eq $date[1]);
# printf("# subsequent_season: pat='%s', cur='%s'\n", $pat, join('/',@cur));
if (($pat < 21) || ($pat > 24)) {
- carp "Unexpected season '$pat'";
- return undef;
+ carp "Unexpected season '$pat'";
+ return undef;
}
if ($cur[1] >= $pat) {
- # current season is on or past pattern season in this year,
- # advance to next year
- $cur[0] += 1;
+ # current season is on or past pattern season in this year,
+ # advance to next year
+ $cur[0] += 1;
}
# Either we've advanced to the next year or the current season
# is before the pattern season in the current year. Either way,
}
sub match_year {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
# XXX WRITE ME
}
sub match_issue {
- my $pat = shift;
+ my $pat = shift;
my @date = @_;
# We handle enumeration patterns separately. This just
}
my %dispatch = (
- d => \&match_day,
- e => \&match_issue, # not really a "chron" code
- w => \&match_week,
- m => \&match_month,
- s => \&match_season,
- y => \&match_year,
+ d => \&match_day,
+ e => \&match_issue, # not really a "chron" code
+ w => \&match_week,
+ m => \&match_month,
+ s => \&match_season,
+ y => \&match_year,
);
my %generators = (
- d => \&subsequent_day,
- e => \&subsequent_issue, # not really a "chron" code
- w => \&subsequent_week,
- m => \&subsequent_month,
- s => \&subsequent_season,
- y => \&subsequent_year,
+ d => \&subsequent_day,
+ e => \&subsequent_issue, # not really a "chron" code
+ w => \&subsequent_week,
+ m => \&subsequent_month,
+ s => \&subsequent_season,
+ y => \&subsequent_year,
);
sub dispatch {
}
my %increments = (
- a => {years => 1}, # annual
- b => {months => 2}, # bimonthly
- c => {days => 3}, # semiweekly
- d => {days => 1}, # daily
- e => {weeks => 2}, # biweekly
- f => {months => 6}, # semiannual
- g => {years => 2}, # biennial
- h => {years => 3}, # triennial
- i => {days => 2}, # three times / week
- j => {days => 10}, # three times /month
- # k => continuous
- m => {months => 1}, # monthly
- q => {months => 3}, # quarterly
- s => {days => 15}, # semimonthly
- t => {months => 4}, # three times / year
- w => {weeks => 1}, # weekly
- # x => completely irregular
+ a => {years => 1}, # annual
+ b => {months => 2}, # bimonthly
+ c => {days => 3}, # semiweekly
+ d => {days => 1}, # daily
+ e => {weeks => 2}, # biweekly
+ f => {months => 6}, # semiannual
+ g => {years => 2}, # biennial
+ h => {years => 3}, # triennial
+ i => {days => 2}, # three times / week
+ j => {days => 10}, # three times /month
+ # k => continuous
+ m => {months => 1}, # monthly
+ q => {months => 3}, # quarterly
+ s => {days => 15}, # semimonthly
+ t => {months => 4}, # three times / year
+ w => {weeks => 1}, # weekly
+ # x => completely irregular
);
sub can_increment {
sub incr_date {
my $freq = shift;
my $incr = $increments{$freq};
- my @new = @_;
+ my @new = @_;
if (scalar(@new) == 1) {
- # only a year is specified. Next date is easy
- $new[0] += $incr->{years} || 1;
+ # only a year is specified. Next date is easy
+ $new[0] += $incr->{years} || 1;
} elsif (scalar(@new) == 2) {
- # Year and month or season
- if ($new[1] > 20) {
- # season
- $new[1] += ($incr->{months}/3) || 1;
- if ($new[1] > 24) {
- # carry
- $new[0] += 1;
- $new[1] -= 4; # 25 - 4 == 21 == Spring after Winter
- }
- } else {
- # month
- $new[1] += $incr->{months} || 1;
- if ($new[1] > 12) {
- # carry
- $new[0] += 1;
- $new[1] -= 12;
- }
- }
+ # Year and month or season
+ if ($new[1] > 20) {
+ # season
+ $new[1] += ($incr->{months} / 3) || 1;
+ if ($new[1] > 24) {
+ # carry
+ $new[0] += 1;
+ $new[1] -= 4; # 25 - 4 == 21 == Spring after Winter
+ }
+ } else {
+ # month
+ $new[1] += $incr->{months} || 1;
+ if ($new[1] > 12) {
+ # carry
+ $new[0] += 1;
+ $new[1] -= 12;
+ }
+ }
} elsif (scalar(@new) == 3) {
- # Year, Month, Day: now it gets complicated.
-
- if ($new[2] =~ /^[0-9]+$/) {
- # A single number for the day of month, relatively simple
- my $dt = DateTime->new(year => $new[0],
- month=> $new[1],
- day => $new[2]);
- $dt->add(%{$incr});
- $new[0] = $dt->year;
- $new[1] = $dt->month;
- $new[2] = $dt->day;
- }
+ # Year, Month, Day: now it gets complicated.
+
+ if ($new[2] =~ /^[0-9]+$/) {
+ # A single number for the day of month, relatively simple
+ my $dt = DateTime->new(
+ year => $new[0],
+ month => $new[1],
+ day => $new[2]
+ );
+ $dt->add(%{$incr});
+ $new[0] = $dt->year;
+ $new[1] = $dt->month;
+ $new[2] = $dt->day;
+ }
} else {
- warn("Don't know how to cope with @new");
+ warn("Don't know how to cope with @new");
}
foreach my $i (0..$#new) {
- $new[$i] = '0' . (0+$new[$i]) if $new[$i] < 10;
+ $new[$i] = '0' . (0 + $new[$i]) if $new[$i] < 10;
}
return @new;
use base 'MARC::Field';
sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
- my $seqno = shift;
- my $self = shift;
- my $caption = shift;
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $seqno = shift;
+ my $self = shift;
+ my $caption = shift;
my $last_enum = undef;
- $self->{_mfhdh_SEQNO} = $seqno;
- $self->{_mfhdh_CAPTION} = $caption;
- $self->{_mfhdh_DESCR} = {};
- $self->{_mfhdh_COPY} = undef;
- $self->{_mfhdh_BREAK} = undef;
- $self->{_mfhdh_NOTES} = {};
+ $self->{_mfhdh_SEQNO} = $seqno;
+ $self->{_mfhdh_CAPTION} = $caption;
+ $self->{_mfhdh_DESCR} = {};
+ $self->{_mfhdh_COPY} = undef;
+ $self->{_mfhdh_BREAK} = undef;
+ $self->{_mfhdh_NOTES} = {};
$self->{_mfhdh_COPYRIGHT} = [];
foreach my $subfield ($self->subfields) {
- my ($key, $val) = @$subfield;
-
- if (($caption && $caption->enumeration_is_chronology && $key =~ /[a-h]/) || $key =~ /[i-m]/) {
- # Chronology
- $self->{_mfhdh_SUBFIELDS}->{$key} = $val;
- } elsif ($key =~ /[a-h]/) {
- # Enumeration details of holdings
- $self->{_mfhdh_SUBFIELDS}->{$key} = {HOLDINGS => $val,
- UNIT => undef,};
- $last_enum = $key;
- } elsif ($key eq 'o') {
- warn '$o specified prior to first enumeration'
- unless defined($last_enum);
- $self->{_mfhdh_SUBFIELDS}->{$last_enum}->{UNIT} = $val;
- $last_enum = undef;
- } elsif ($key =~ /[npq]/) {
- $self->{_mfhdh_DESCR}->{$key} = $val;
- } elsif ($key eq 's') {
- push @{$self->{_mfhdh_COPYRIGHT}}, $val;
- } elsif ($key eq 't') {
- $self->{_mfhdh_COPY} = $val;
- } elsif ($key eq 'w') {
- carp "Unrecognized break indicator '$val'"
- unless $val =~ /^[gn]$/;
- $self->{_mfhdh_BREAK} = $val;
- }
+ my ($key, $val) = @$subfield;
+
+ if (($caption && $caption->enumeration_is_chronology && $key =~ /[a-h]/)
+ || $key =~ /[i-m]/) {
+ # Chronology
+ $self->{_mfhdh_SUBFIELDS}->{$key} = $val;
+ } elsif ($key =~ /[a-h]/) {
+ # Enumeration details of holdings
+ $self->{_mfhdh_SUBFIELDS}->{$key} = {
+ HOLDINGS => $val,
+ UNIT => undef,
+ };
+ $last_enum = $key;
+ } elsif ($key eq 'o') {
+ warn '$o specified prior to first enumeration'
+ unless defined($last_enum);
+ $self->{_mfhdh_SUBFIELDS}->{$last_enum}->{UNIT} = $val;
+ $last_enum = undef;
+ } elsif ($key =~ /[npq]/) {
+ $self->{_mfhdh_DESCR}->{$key} = $val;
+ } elsif ($key eq 's') {
+ push @{$self->{_mfhdh_COPYRIGHT}}, $val;
+ } elsif ($key eq 't') {
+ $self->{_mfhdh_COPY} = $val;
+ } elsif ($key eq 'w') {
+ carp "Unrecognized break indicator '$val'"
+ unless $val =~ /^[gn]$/;
+ $self->{_mfhdh_BREAK} = $val;
+ }
}
- bless ($self, $class);
+ bless($self, $class);
return $self;
}
}
sub format_chron {
- my $self = shift;
+ my $self = shift;
my $caption = $self->{_mfhdh_CAPTION};
my @keys;
- my $str = '';
- my %month = ( '01' => 'Jan.', '02' => 'Feb.', '03' => 'Mar.',
- '04' => 'Apr.', '05' => 'May ', '06' => 'Jun.',
- '07' => 'Jul.', '08' => 'Aug.', '09' => 'Sep.',
- '10' => 'Oct.', '11' => 'Nov.', '12' => 'Dec.',
- '21' => 'Spring', '22' => 'Summer',
- '23' => 'Autumn', '24' => 'Winter' );
+ my $str = '';
+ my %month = (
+ '01' => 'Jan.',
+ '02' => 'Feb.',
+ '03' => 'Mar.',
+ '04' => 'Apr.',
+ '05' => 'May ',
+ '06' => 'Jun.',
+ '07' => 'Jul.',
+ '08' => 'Aug.',
+ '09' => 'Sep.',
+ '10' => 'Oct.',
+ '11' => 'Nov.',
+ '12' => 'Dec.',
+ '21' => 'Spring',
+ '22' => 'Summer',
+ '23' => 'Autumn',
+ '24' => 'Winter'
+ );
@keys = @_;
- foreach my $i (0 .. @keys) {
- my $key = $keys[$i];
- my $capstr;
- my $chron;
- my $sep;
-
- last if !defined $caption->capstr($key);
-
- $capstr = $caption->capstr($key);
- if (substr($capstr,0,1) eq '(') {
- # a caption enclosed in parentheses is not displayed
- $capstr = '';
- }
-
- # If this is the second level of chronology, then it's
- # likely to be a month or season, so we should use the
- # string name rather than the number given.
- if (($i == 1) && exists $month{$self->{_mfhdh_SUBFIELDS}->{$key}}) {
- $chron = $month{$self->{_mfhdh_SUBFIELDS}->{$key}};
- } else {
- $chron = $self->{_mfhdh_SUBFIELDS}->{$key};
- }
-
-
- $str .= (($i == 0 || $str =~ /[. ]$/) ? '' : ':') . $capstr . $chron;
+ foreach my $i (0..@keys) {
+ my $key = $keys[$i];
+ my $capstr;
+ my $chron;
+ my $sep;
+
+ last if !defined $caption->capstr($key);
+
+ $capstr = $caption->capstr($key);
+ if (substr($capstr, 0, 1) eq '(') {
+ # a caption enclosed in parentheses is not displayed
+ $capstr = '';
+ }
+
+ # If this is the second level of chronology, then it's
+ # likely to be a month or season, so we should use the
+ # string name rather than the number given.
+ if (($i == 1) && exists $month{$self->{_mfhdh_SUBFIELDS}->{$key}}) {
+ $chron = $month{$self->{_mfhdh_SUBFIELDS}->{$key}};
+ } else {
+ $chron = $self->{_mfhdh_SUBFIELDS}->{$key};
+ }
+
+ $str .= (($i == 0 || $str =~ /[. ]$/) ? '' : ':') . $capstr . $chron;
}
return $str;
}
sub format {
- my $self = shift;
+ my $self = shift;
my $caption = $self->{_mfhdh_CAPTION};
- my $str = '';
+ my $str = '';
if ($caption->type_of_unit) {
- $str = $caption->type_of_unit . ' ';
+ $str = $caption->type_of_unit . ' ';
}
if ($caption->enumeration_is_chronology) {
- # if issues are identified by chronology only, then the
- # chronology data is stored in the enumeration subfields,
- # so format those fields as if they were chronological.
- $str = $self->format_chron('a'..'f');
+ # if issues are identified by chronology only, then the
+ # chronology data is stored in the enumeration subfields,
+ # so format those fields as if they were chronological.
+ $str = $self->format_chron('a'..'f');
} else {
- # OK, there is enumeration data and maybe chronology
- # data as well, format both parts appropriately
-
- # Enumerations
- foreach my $key ('a'..'f') {
- my $capstr;
- my $chron;
- my $sep;
-
- last if !defined $caption->capstr($key);
-
- $capstr = $caption->capstr($key);
- if (substr($capstr, 0, 1) eq '(') {
- # a caption enclosed in parentheses is not displayed
- $capstr = '';
- }
- $str .= ($key eq 'a' ? '' : ':') . $capstr . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS};
- }
-
- # Chronology
- if (defined $caption->capstr('i')) {
- $str .= '(';
- $str .= $self->format_chron('i'..'l');
- $str .= ')';
- }
-
- if ($caption->capstr('g')) {
- # There's at least one level of alternative enumeration
- $str .= '=';
- foreach my $key ('g', 'h') {
- $str .= ($key eq 'g' ? '' : ':') . $caption->capstr($key) . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS};
- }
-
- # This assumes that alternative chronology is only ever
- # provided if there is an alternative enumeration.
- if ($caption->capstr('m')) {
- # Alternative Chronology
- $str .= '(';
- $str .= $caption->capstr('m') . $self->{_mfhdh_SUBFIELDS}->{m}->{HOLDINGS};
- $str .= ')';
- }
- }
+ # OK, there is enumeration data and maybe chronology
+ # data as well, format both parts appropriately
+
+ # Enumerations
+ foreach my $key ('a'..'f') {
+ my $capstr;
+ my $chron;
+ my $sep;
+
+ last if !defined $caption->capstr($key);
+
+ $capstr = $caption->capstr($key);
+ if (substr($capstr, 0, 1) eq '(') {
+ # a caption enclosed in parentheses is not displayed
+ $capstr = '';
+ }
+ $str .=
+ ($key eq 'a' ? '' : ':')
+ . $capstr
+ . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS};
+ }
+
+ # Chronology
+ if (defined $caption->capstr('i')) {
+ $str .= '(';
+ $str .= $self->format_chron('i'..'l');
+ $str .= ')';
+ }
+
+ if ($caption->capstr('g')) {
+ # There's at least one level of alternative enumeration
+ $str .= '=';
+ foreach my $key ('g', 'h') {
+ $str .=
+ ($key eq 'g' ? '' : ':')
+ . $caption->capstr($key)
+ . $self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS};
+ }
+
+ # This assumes that alternative chronology is only ever
+ # provided if there is an alternative enumeration.
+ if ($caption->capstr('m')) {
+ # Alternative Chronology
+ $str .= '(';
+ $str .=
+ $caption->capstr('m')
+ . $self->{_mfhdh_SUBFIELDS}->{m}->{HOLDINGS};
+ $str .= ')';
+ }
+ }
}
# Public Note
- $str .= ' '. $caption->capstr('z') if (defined $caption->capstr('z'));
+ $str .= ' ' . $caption->capstr('z') if (defined $caption->capstr('z'));
# Breaks in the sequence
if (defined($self->{_mfhdh_BREAK})) {
return $str;
}
-
# next: Given a holding statement, return a hash containing the
# enumeration values for the next issues, whether we hold it or not
# Just pass through to Caption::next
#
sub next {
- my $self = shift;
+ my $self = shift;
my $caption = $self->{_mfhdh_CAPTION};
return $caption->next($self);
#
#
sub match {
- my $self = shift;
- my $pat = shift;
+ my $self = shift;
+ my $pat = shift;
my $caption = $self->{_mfhdh_CAPTION};
foreach my $key ('a'..'f') {
- my $nextkey;
-
- ($nextkey = $key)++;
- # If the next smaller enumeration exists, and is numbered
- # continuously, then we don't need to check this one, because
- # gaps in issue numbering matter, not changes in volume numbering
- next if (exists $self->{_mfhdh_SUBFIELDS}->{$nextkey}
- && !$caption->capfield($nextkey)->{RESTART});
-
- # If a subfield exists in $self but not in $pat, or vice versa
- # or if the field has different values, then fail
- if (exists($self->{_mfhdh_SUBFIELDS}->{$key}) != exists($pat->{$key})
- || (exists $pat->{$key}
- && ($self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} ne $pat->{$key}))) {
- return 0;
- }
+ my $nextkey;
+
+ ($nextkey = $key)++;
+ # If the next smaller enumeration exists, and is numbered
+ # continuously, then we don't need to check this one, because
+ # gaps in issue numbering matter, not changes in volume numbering
+ next
+ if (exists $self->{_mfhdh_SUBFIELDS}->{$nextkey}
+ && !$caption->capfield($nextkey)->{RESTART});
+
+ # If a subfield exists in $self but not in $pat, or vice versa
+ # or if the field has different values, then fail
+ if (
+ exists($self->{_mfhdh_SUBFIELDS}->{$key}) != exists($pat->{$key})
+ || (exists $pat->{$key}
+ && ($self->{_mfhdh_SUBFIELDS}->{$key}->{HOLDINGS} ne
+ $pat->{$key}))
+ ) {
+ return 0;
+ }
}
return 1;
}
-#
+#
# Check that all the fields in a holdings statement are
# included in the corresponding caption.
-#
+#
sub validate {
my $self = shift;
foreach my $key (keys %{$self->{_mfhdh_SUBFIELDS}}) {
- if (!$self->{_mfhdh_CAPTION} || !$self->{_mfhdh_CAPTION}->capfield($key)) {
- return 0;
- }
+ if ( !$self->{_mfhdh_CAPTION}
+ || !$self->{_mfhdh_CAPTION}->capfield($key)) {
+ return 0;
+ }
}
return 1;
}
sub right_answer {
my $holding = shift;
- my $answer = {};
+ my $answer = {};
foreach my $subfield (split(/\|/, $holding->subfield('x'))) {
- next unless $subfield;
+ next unless $subfield;
- my ($key, $val) = unpack('aa*', $subfield);
- $answer->{$key} = $val;
+ my ($key, $val) = unpack('aa*', $subfield);
+ $answer->{$key} = $val;
}
return $answer;
# skim to beginning of record (a non-blank, non comment line)
while ($line = <DATA>) {
- chomp $line;
- last if (!($line =~ /^\s*$/) && !($line =~ /^#/));
+ chomp $line;
+ last if (!($line =~ /^\s*$/) && !($line =~ /^#/));
}
return undef if !$line;
carp('No record created!') unless $marc;
$marc->leader('01119nas 2200313 a 4500');
- $marc->append_fields(MARC::Field->new('008', '970701c18439999enkwr p 0 a0eng '));
- $marc->append_fields(MARC::Field->new('035', '', '',
- a => sprintf('%04d', $testno)));
+ $marc->append_fields(
+ MARC::Field->new('008', '970701c18439999enkwr p 0 a0eng '));
+ $marc->append_fields(
+ MARC::Field->new('035', '', '', a => sprintf('%04d', $testno)));
while ($line) {
- next if $line =~ /^#/; # allow embedded comments
+ next if $line =~ /^#/; # allow embedded comments
- return $marc if $line =~ /^\s*$/;
+ return $marc if $line =~ /^\s*$/;
- my ($fieldno, $indicators, $rest) = split(/ /, $line, 3);
- my @inds = unpack('cc', $indicators);
- my $field;
- my @subfields;
+ my ($fieldno, $indicators, $rest) = split(/ /, $line, 3);
+ my @inds = unpack('cc', $indicators);
+ my $field;
+ my @subfields;
- @subfields = ();
- foreach my $subfield (split(/\$/, $rest)) {
- next unless $subfield;
+ @subfields = ();
+ foreach my $subfield (split(/\$/, $rest)) {
+ next unless $subfield;
- my ($key, $val) = unpack('aa*', $subfield);
- push @subfields, $key, $val;
- }
+ my ($key, $val) = unpack('aa*', $subfield);
+ push @subfields, $key, $val;
+ }
- $field = MARC::Field->new($fieldno, $inds[0], $inds[1],
- a => 'scratch', @subfields);
+ $field = MARC::Field->new(
+ $fieldno, $inds[0], $inds[1],
+ a => 'scratch',
+ @subfields
+ );
- $marc->append_fields($field);
+ $marc->append_fields($field);
- $line = <DATA>;
- chomp $line if $line;
+ $line = <DATA>;
+ chomp $line if $line;
}
return $marc;
}
while ($rec = load_MARC_rec) {
$rec = MFHD->new($rec);
- foreach my $cap (sort {$a->tag <=> $b->tag} $rec->field('85.')) {
- my $htag;
- my @holdings;
-
- ($htag = $cap->tag) =~ s/^85/86/;
- @holdings = $rec->holdings($htag, $cap->subfield('8'));
-
- next unless scalar @holdings;
- foreach my $field (@holdings) {
- TODO: {
- local $TODO = "unimplemented"
- if ($field->subfield('z') =~ /^TODO/);
- is_deeply($field->next, right_answer($field),
- $field->subfield('8') . ': ' . $field->subfield('z'));
- }
- }
+ foreach my $cap (sort { $a->tag <=> $b->tag } $rec->field('85.')) {
+ my $htag;
+ my @holdings;
+
+ ($htag = $cap->tag) =~ s/^85/86/;
+ @holdings = $rec->holdings($htag, $cap->subfield('8'));
+
+ next unless scalar @holdings;
+ foreach my $field (@holdings) {
+ TODO: {
+ local $TODO = "unimplemented"
+ if ($field->subfield('z') =~ /^TODO/);
+ is_deeply($field->next, right_answer($field),
+ $field->subfield('8') . ': ' . $field->subfield('z'));
+ }
+ }
}
}
package OpenILS::Utils::MFHDParser;
-use strict; use warnings;
+use strict;
+use warnings;
use OpenSRF::EX qw/:try/;
use Time::HiRes qw(time);
use MARC::File::XML (BinaryEncoding => 'utf8');
use Data::Dumper;
-sub new { return bless( {}, shift() ); }
+sub new { return bless({}, shift()); }
=head1 Subroutines
=cut
sub format_textual_holdings {
- my ($self, $field) = @_;
- my $holdings;
- my $public_note;
-
- $holdings = $field->subfield('a');
- if (!$holdings) {
- return undef;
- }
-
- $public_note = $field->subfield('z');
- if ($public_note) {
- return "$holdings - $public_note";
- }
- return $holdings;
+ my ($self, $field) = @_;
+ my $holdings;
+ my $public_note;
+
+ $holdings = $field->subfield('a');
+ if (!$holdings) {
+ return undef;
+ }
+
+ $public_note = $field->subfield('z');
+ if ($public_note) {
+ return "$holdings - $public_note";
+ }
+ return $holdings;
}
=over
Returns a Perl hash containing fields of interest from the MFHD record
=cut
+
sub mfhd_to_hash {
- my ($self, $mfhd_xml) = @_;
-
- my $marc;
- my $mfhd;
-
- my $location = '';
- my $holdings = [];
- my $supplements = [];
- my $indexes = [];
- my $current_holdings = [];
- my $current_supplements = [];
- my $current_indexes = [];
- my $online = []; # Laurentian extension to MFHD standard
- my $missing = []; # Laurentian extension to MFHD standard
- my $incomplete = []; # Laurentian extension to MFHD standard
-
- try {
- $marc = MARC::Record->new_from_xml($mfhd_xml);
- } otherwise {
- $logger->error("Failed to convert MFHD XML to MARC: " . shift());
- $logger->error("Failed MFHD XML: $mfhd_xml");
- };
-
- if (!$marc) {
- return undef;
- }
-
- try {
- $mfhd = MFHD->new($marc);
- } otherwise {
- $logger->error("Failed to parse MFHD: " . shift());
- $logger->error("Failed MFHD XML: $mfhd_xml");
- };
-
- if (!$mfhd) {
- return undef;
- }
-
- try {
- foreach my $field ($marc->field('852')) {
- foreach my $subfield_ref ($field->subfields) {
- my ($subfield, $data) = @$subfield_ref;
- $location .= $data . " -- ";
- }
- }
- } otherwise {
- $logger->error("MFHD location parsing error: " . shift());
- };
-
- $location =~ s/ -- $//;
-
- try {
- foreach my $field ($marc->field('866')) {
- my $textual_holdings = $self->format_textual_holdings($field);
- if ($textual_holdings) {
- push @$holdings, $textual_holdings;
- }
- }
- foreach my $field ($marc->field('867')) {
- my $textual_holdings = $self->format_textual_holdings($field);
- if ($textual_holdings) {
- push @$supplements, $textual_holdings;
- }
- }
- foreach my $field ($marc->field('868')) {
- my $textual_holdings = $self->format_textual_holdings($field);
- if ($textual_holdings) {
- push @$indexes, $textual_holdings;
- }
- }
-
- foreach my $cap_id ($mfhd->captions('853')) {
- my @curr_holdings = $mfhd->holdings('863', $cap_id);
- next unless scalar @curr_holdings;
- foreach (@curr_holdings) {
- push @$current_holdings, $_->format();
- }
- }
-
- foreach my $cap_id ($mfhd->captions('854')) {
- my @curr_supplements = $mfhd->holdings('864', $cap_id);
- next unless scalar @curr_supplements;
- foreach (@curr_supplements) {
- push @$current_supplements, $_->format();
- }
- }
-
- foreach my $cap_id ($mfhd->captions('855')) {
- my @curr_indexes = $mfhd->holdings('865', $cap_id);
- next unless scalar @curr_indexes;
- foreach (@curr_indexes) {
- push @$current_indexes, $_->format();
- }
- }
-
- # Laurentian extensions
- foreach my $field ($marc->field('530')) {
- my $online_stmt = $self->format_textual_holdings($field);
- if ($online_stmt) {
- push @$online, $online_stmt;
- }
- }
-
- foreach my $field ($marc->field('590')) {
- my $missing_stmt = $self->format_textual_holdings($field);
- if ($missing_stmt) {
- push @$missing, $missing_stmt;
- }
- }
-
- foreach my $field ($marc->field('591')) {
- my $incomplete_stmt = $self->format_textual_holdings($field);
- if ($incomplete_stmt) {
- push @$incomplete, $incomplete_stmt;
- }
- }
- } otherwise {
- $logger->error("MFHD statement parsing error: " . shift());
- };
-
- return { location => $location, holdings => $holdings, current_holdings => $current_holdings,
- supplements => $supplements, current_supplements => $current_supplements,
- indexes => $indexes, current_indexes => $current_indexes,
- missing => $missing, incomplete => $incomplete, };
+ my ($self, $mfhd_xml) = @_;
+
+ my $marc;
+ my $mfhd;
+
+ my $location = '';
+ my $holdings = [];
+ my $supplements = [];
+ my $indexes = [];
+ my $current_holdings = [];
+ my $current_supplements = [];
+ my $current_indexes = [];
+ my $online = []; # Laurentian extension to MFHD standard
+ my $missing = []; # Laurentian extension to MFHD standard
+ my $incomplete = []; # Laurentian extension to MFHD standard
+
+ try {
+ $marc = MARC::Record->new_from_xml($mfhd_xml);
+ }
+ otherwise {
+ $logger->error("Failed to convert MFHD XML to MARC: " . shift());
+ $logger->error("Failed MFHD XML: $mfhd_xml");
+ };
+
+ if (!$marc) {
+ return undef;
+ }
+
+ try {
+ $mfhd = MFHD->new($marc);
+ }
+ otherwise {
+ $logger->error("Failed to parse MFHD: " . shift());
+ $logger->error("Failed MFHD XML: $mfhd_xml");
+ };
+
+ if (!$mfhd) {
+ return undef;
+ }
+
+ try {
+ foreach my $field ($marc->field('852')) {
+ foreach my $subfield_ref ($field->subfields) {
+ my ($subfield, $data) = @$subfield_ref;
+ $location .= $data . " -- ";
+ }
+ }
+ }
+ otherwise {
+ $logger->error("MFHD location parsing error: " . shift());
+ };
+
+ $location =~ s/ -- $//;
+
+ try {
+ foreach my $field ($marc->field('866')) {
+ my $textual_holdings = $self->format_textual_holdings($field);
+ if ($textual_holdings) {
+ push @$holdings, $textual_holdings;
+ }
+ }
+ foreach my $field ($marc->field('867')) {
+ my $textual_holdings = $self->format_textual_holdings($field);
+ if ($textual_holdings) {
+ push @$supplements, $textual_holdings;
+ }
+ }
+ foreach my $field ($marc->field('868')) {
+ my $textual_holdings = $self->format_textual_holdings($field);
+ if ($textual_holdings) {
+ push @$indexes, $textual_holdings;
+ }
+ }
+
+ foreach my $cap_id ($mfhd->captions('853')) {
+ my @curr_holdings = $mfhd->holdings('863', $cap_id);
+ next unless scalar @curr_holdings;
+ foreach (@curr_holdings) {
+ push @$current_holdings, $_->format();
+ }
+ }
+
+ foreach my $cap_id ($mfhd->captions('854')) {
+ my @curr_supplements = $mfhd->holdings('864', $cap_id);
+ next unless scalar @curr_supplements;
+ foreach (@curr_supplements) {
+ push @$current_supplements, $_->format();
+ }
+ }
+
+ foreach my $cap_id ($mfhd->captions('855')) {
+ my @curr_indexes = $mfhd->holdings('865', $cap_id);
+ next unless scalar @curr_indexes;
+ foreach (@curr_indexes) {
+ push @$current_indexes, $_->format();
+ }
+ }
+
+ # Laurentian extensions
+ foreach my $field ($marc->field('530')) {
+ my $online_stmt = $self->format_textual_holdings($field);
+ if ($online_stmt) {
+ push @$online, $online_stmt;
+ }
+ }
+
+ foreach my $field ($marc->field('590')) {
+ my $missing_stmt = $self->format_textual_holdings($field);
+ if ($missing_stmt) {
+ push @$missing, $missing_stmt;
+ }
+ }
+
+ foreach my $field ($marc->field('591')) {
+ my $incomplete_stmt = $self->format_textual_holdings($field);
+ if ($incomplete_stmt) {
+ push @$incomplete, $incomplete_stmt;
+ }
+ }
+ }
+ otherwise {
+ $logger->error("MFHD statement parsing error: " . shift());
+ };
+
+ return {
+ location => $location,
+ holdings => $holdings,
+ current_holdings => $current_holdings,
+ supplements => $supplements,
+ current_supplements => $current_supplements,
+ indexes => $indexes,
+ current_indexes => $current_indexes,
+ missing => $missing,
+ incomplete => $incomplete,
+ };
}
=over
Initialize the serial virtual record (svr) instance
=cut
+
sub init_holdings_virtual_record {
- my $record = Fieldmapper::serial::virtual_record->new;
- $record->id();
- $record->location();
- $record->owning_lib();
- $record->holdings([]);
- $record->current_holdings([]);
- $record->supplements([]);
- $record->current_supplements([]);
- $record->indexes([]);
- $record->current_indexes([]);
- $record->online([]);
- $record->missing([]);
- $record->incomplete([]);
- return $record;
+ my $record = Fieldmapper::serial::virtual_record->new;
+ $record->id();
+ $record->location();
+ $record->owning_lib();
+ $record->holdings([]);
+ $record->current_holdings([]);
+ $record->supplements([]);
+ $record->current_supplements([]);
+ $record->indexes([]);
+ $record->current_indexes([]);
+ $record->online([]);
+ $record->missing([]);
+ $record->incomplete([]);
+ return $record;
}
=over
Given an MFHD record, return a populated svr instance
=cut
+
sub generate_svr {
- my ($self, $id, $mfhd, $owning_lib) = @_;
-
- if (!$mfhd) {
- return undef;
- }
-
- my $record = init_holdings_virtual_record();
- my $holdings = $self->mfhd_to_hash($mfhd);
-
- $record->id($id);
- $record->owning_lib($owning_lib);
-
- if (!$holdings) {
- return $record;
- }
-
- $record->location($holdings->{location});
- $record->holdings($holdings->{holdings});
- $record->current_holdings($holdings->{current_holdings});
- $record->supplements($holdings->{supplements});
- $record->current_supplements($holdings->{current_supplements});
- $record->indexes($holdings->{indexes});
- $record->current_indexes($holdings->{current_indexes});
- $record->online($holdings->{online});
- $record->missing($holdings->{missing});
- $record->incomplete($holdings->{incomplete});
-
- return $record;
+ my ($self, $id, $mfhd, $owning_lib) = @_;
+
+ if (!$mfhd) {
+ return undef;
+ }
+
+ my $record = init_holdings_virtual_record();
+ my $holdings = $self->mfhd_to_hash($mfhd);
+
+ $record->id($id);
+ $record->owning_lib($owning_lib);
+
+ if (!$holdings) {
+ return $record;
+ }
+
+ $record->location($holdings->{location});
+ $record->holdings($holdings->{holdings});
+ $record->current_holdings($holdings->{current_holdings});
+ $record->supplements($holdings->{supplements});
+ $record->current_supplements($holdings->{current_supplements});
+ $record->indexes($holdings->{indexes});
+ $record->current_indexes($holdings->{current_indexes});
+ $record->online($holdings->{online});
+ $record->missing($holdings->{missing});
+ $record->incomplete($holdings->{incomplete});
+
+ return $record;
}
1;