PICA-Data/lib/PICA/Data/Field.pm
package PICA::Data::Field;
use v5.14.1;
our $VERSION = '2.12';
use Carp qw(croak);
use Hash::MultiValue;
use List::Util qw(first);
sub new {
my $class = shift;
my $tag = shift;
# simplify migration from PICA::Record
return pica_field($tag->{_tag}, $tag->{_occurrence},
@{$tag->{_subfields}})
if ref $tag eq 'PICA::Field';
my $field = bless [], $class;
my $occ = '';
if (@_ % 2) {
$occ = shift // '';
}
elsif ($tag =~ m/^([0-2]\d{2}[A-Z@])(\/(\d+))?$/) {
$tag = $1;
$occ = $3;
}
$field->tag($tag);
$field->occurrence($occ);
croak "missing subfields" unless @_;
my $ann = pop @_ if @_ % 2;
$field->subfields(@_);
$field->annotation($ann) if defined $ann && $ann ne '';
return $field;
}
sub level {
substr $_[0]->[0], 0, 1;
}
sub tag {
if (@_ > 1) {
my $tag = $_[1];
croak "invalid tag: $tag" if $tag !~ qr/^[0-2]\d{2}[A-Z@]$/;
$_[0]->[0] = $tag;
}
$_[0]->[0];
}
sub occurrence {
if (@_ > 1) {
my $occ = $_[1];
if ($occ == 0) {
$occ = undef;
}
else {
croak "invalid occurrence: $occ" if $occ !~ qr/^\d+$/;
if ($occ < 99) {
$occ = sprintf('%02d', $occ);
}
elsif ($_[0]->level eq '2') {
$occ = sprintf('%03d', $occ);
}
}
$_[0]->[1] = $occ;
}
$_[0]->[1];
}
sub id {
my ($tag, $occ) = @{$_[0]};
$occ > 0 ? "$tag/$occ" : $tag;
}
sub annotation {
my ($field, $ann) = @_;
if (@_ > 1) {
my $has_ann = !($#$field % 2);
if (($ann // '') eq '') {
pop @$field if $has_ann;
}
else {
croak "invalid annotation: $ann" if $ann !~ /^[^A-Za-z0-9]$/;
if ($has_ann) {
$field->[-1] = $ann;
}
else {
push @$field, $ann;
}
}
}
return $#$field % 2 ? undef : $field->[-1];
}
sub subfields {
my $field = shift;
if (@_) {
while (@_) {
my $code = shift;
my $value = shift;
croak "invalid subfield code: $code" if $code !~ /^[A-Za-z0-9]$/;
if (defined $value and $value ne '') {
push @$field, $code, $value;
}
}
}
else {
my $l = @$field % 2 ? $#$field - 1 : $#$field;
return Hash::MultiValue->new(@$field[2 .. $l]);
}
}
sub set {
my ($field, $code, $value) = @_;
croak "invalid subfield code: $code" if $code !~ /^[A-Za-z0-9]$/;
return unless defined $value and $value ne '';
for (my $i = 2; $i <= @$field / 2; $i++) {
if ($field->[$i] eq $code) {
$field->[$i + 1] = $value;
return;
}
}
push @$field, $code, $value;
}
sub equal {
my ($a, $b) = @_;
return (@$a == @$b && !defined first {$a->[$_] ne $b->[$_]} 0 .. $#{$b});
}
sub clone {
bless TO_JSON($_[0]), 'PICA::Data::Field';
}
sub TO_JSON {
[@{$_[0]}];
}
1;
=head1 NAME
PICA::Data::Field - PICA+ Field
=head1 DESCRIPTION
A PICA::Data::Field is a blessed array reference with tag, occurrence,
subfields, and optional annotation.
=head1 METHODS
=head2 new( $tag, [$occ,] @subfields [,$annotation] )
Create a new PICA+ field. Will die on invalid tag, occurrence, subfield
code or annotation.
=head2 level
Get the record level (0, 1, or 2).
=head2 tag( [$value] )
Get or set the tag.
=head2 occurrence( [$value] )
Get or set the occurrence.
=head2 id
Get the field identifier (tag and optional occurrence).
=head2 subfields( [ $code => $value, ...] )
Set all subfields if arguments are given. Otherwise return a
L<Hash::MultiValue> of all subfields. Use it's getter methods to access
subfield values. Changing subfields this way won't work!
=head2 set( $code => $value )
Set or append a subfield.
=head2 equal( $field )
Check whether the field is equal to another field.
=head2 clone
Return a copy of this field.
=head2 SEE ALSO
Full records are processed as L<PICA::Data> objects.
=cut