{
$PICA::Field::VERSION
=
'0.585'
;
}
our
@EXPORT
=
qw(parse_pp_tag)
;
our
$SUBFIELD_INDICATOR
=
"\x1F"
;
our
$START_OF_FIELD
=
"\x1E"
;
our
$END_OF_FIELD
=
"\x0A"
;
our
$FIELD_TAG_REGEXP
=
qr/[012][0-9][0-9][A-Z@]$/
;
our
$FIELD_OCCURRENCE_REGEXP
=
qr/[0-9][0-9]$/
;
our
$SUBFIELD_CODE_REGEXP
=
qr/^[0-9a-zA-Z]$/
;
'bool'
=>
sub
{ !
$_
[0]->empty; },
'""'
=>
sub
{
$_
[0]->string; };
use
sort
'stable'
;
sub
new($) {
my
$class
=
shift
;
$class
=
ref
(
$class
) ||
$class
;
my
$tag
=
shift
;
$tag
or croak(
"No tag provided."
);
if
(not
@_
) {
return
PICA::Field->parse(
$tag
);
}
my
(
$occurrence
,
$tagno
) = parse_pp_tag(
$tag
);
defined
$tagno
or croak(
"\"$tag\" is not a valid tag."
);
my
$self
=
bless
{
_tag
=>
$tagno
,
_occurrence
=>
$occurrence
,
_subfields
=> [],
},
$class
;
$self
->add(
@_
);
return
$self
;
}
sub
copy {
my
$self
=
shift
;
my
$tagno
=
$self
->{_tag};
my
$occurrence
=
$self
->{_occurrence};
my
$copy
=
bless
{
_tag
=>
$tagno
,
_occurrence
=>
$occurrence
,
},
ref
(
$self
);
$copy
->add( @{
$self
->{_subfields}} );
return
$copy
;
}
sub
parse {
my
$class
=
shift
;
$class
=
ref
(
$class
) ||
$class
;
my
$data
=
shift
;
my
$tag_filter_func
=
shift
;
my
$END_OF_FIELD
=
qr/[\x0A\x0D]+/
;
$data
=~ s/^
$START_OF_FIELD
//;
$data
=~ s/
$END_OF_FIELD
$//;
my
$self
=
bless
{},
$class
;
my
(
$tagno
,
$subfields
) = (
$data
=~ /([^\$\x1F\x83\s]+)\s?(.*)/);
return
if
$tag_filter_func
and !
$tag_filter_func
->(
$tagno
);
my
$sfreg
;
my
$sf
=
defined
$subfields
?
substr
(
$subfields
, 0, 1) :
''
;
if
(
$sf
eq
"\x1F"
) {
$sfreg
=
'\x1F'
; }
elsif
(
$sf
eq
'$'
) {
$sfreg
=
'\$'
; }
elsif
(
$sf
eq
"\x83"
) {
$sfreg
=
'\x83'
; }
elsif
(
$sf
eq
"\x9f"
) {
$sfreg
=
'\x9f'
; }
elsif
(
$sf
eq
''
) {
return
$self
->new(
$tagno
,
''
);
}
else
{
croak(
"not allowed subfield indicator (ord: "
.
ord
(
$sf
) .
") specified"
);
}
$sfreg
=
'('
.
$sfreg
.
'[0-9a-zA-Z])'
;
my
@sfields
=
split
(
$sfreg
,
$subfields
);
shift
@sfields
;
my
@subfields
= ();
my
(
$value
,
$code
);
while
(
@sfields
) {
$code
=
shift
@sfields
;
$code
=
substr
(
$code
, 1);
$value
=
shift
@sfields
;
next
unless
defined
$value
;
$value
=~ s/\$\$/\$/g
if
$sf
eq
'$'
;
$value
=~ s/\s+/ /gm;
push
(
@subfields
, (
$code
,
$value
));
}
return
$self
->new(
$tagno
,
@subfields
);
}
sub
tag {
my
$self
=
shift
;
my
$tag
=
shift
;
if
(
defined
$tag
) {
my
(
$occurrence
,
$tagno
) = parse_pp_tag(
$tag
);
defined
$tagno
or croak(
"\"$tag\" is not a valid tag."
);
$self
->{_tag} =
$tagno
;
$self
->{_occurrence} =
$occurrence
;
}
return
$self
->{_tag} . (
$self
->{_occurrence} ? (
"/"
.
$self
->{_occurrence}) :
""
);
}
sub
occurrence {
my
$self
=
shift
;
my
$occurrence
=
shift
;
if
(
defined
$occurrence
) {
croak
unless
$occurrence
>= 0 and
$occurrence
<= 99;
$self
->{_occurrence} =
sprintf
(
"%02d"
,
$occurrence
);
}
return
$self
->{_occurrence};
}
*occ
= \
&occurrence
;
sub
level {
my
$self
=
shift
;
return
substr
(
$self
->{_tag},0,1);
}
sub
subfield {
my
$self
=
shift
;
my
$codes
=
$_
[0];
if
(
ref
(
$codes
) ne
'Regexp'
) {
$codes
=
join
(
''
,
@_
);
if
(
$codes
eq
''
) {
$codes
=
qr/./
;
}
else
{
$codes
=
qr/[$codes]/
;
}
}
my
@list
;
my
@data
= @{
$self
->{_subfields}};
for
(
my
$i
=0;
$i
<
@data
;
$i
+=2 ) {
next
unless
$data
[
$i
] =~
$codes
;
my
$value
=
$data
[
$i
+1];
$value
=~ s/\s+/ /gm;
if
(
wantarray
) {
push
(
@list
,
$value
);
}
else
{
return
$value
;
}
}
return
$list
[0]
unless
wantarray
;
return
@list
;
}
*sf
= \
&subfield
;
sub
content {
my
$self
=
shift
;
my
$codes
=
join
(
''
,
@_
);
$codes
=
$codes
eq
''
?
'.'
:
"[$codes]"
;
$codes
=
qr/$codes/
;
my
@list
;
my
@data
= @{
$self
->{_subfields}};
for
(
my
$i
=0;
$i
<
@data
;
$i
+=2 ) {
next
unless
$data
[
$i
] =~
$codes
;
push
(
@list
, [
$data
[
$i
],
$data
[
$i
+1] ] );
}
return
@list
;
}
sub
add {
my
$self
=
shift
;
my
$nfields
=
@_
/ 2;
(
$nfields
>= 1) or
return
0;
for
my
$i
( 1..
$nfields
) {
my
$offset
= (
$i
-1)*2;
my
$code
=
$_
[
$offset
];
my
$value
=
$_
[
$offset
+1];
$value
=
defined
$value
?
"$value"
:
""
;
$value
=~ s/\s+/ /gm;
croak(
"Subfield code \"$code\" is not a valid subfield code"
)
if
!(
$code
=~
$SUBFIELD_CODE_REGEXP
);
push
( @{
$self
->{_subfields}},
$code
,
$value
);
}
return
$nfields
;
}
sub
update {
my
$self
=
shift
;
my
%values
;
my
@order
;
while
(
@_
) {
my
$c
=
shift
;
croak(
"Subfield code \"$c\" is not a valid subfield code"
)
unless
$c
=~
$SUBFIELD_CODE_REGEXP
;
my
$v
=
shift
;
if
(
exists
$values
{
$c
} ) {
push
@{
$values
{
$c
}}, (UNIVERSAL::isa(
$v
,
'ARRAY'
) ? @{
$v
} :
$v
);
}
else
{
push
@order
,
$c
;
$values
{
$c
} = UNIVERSAL::isa(
$v
,
'ARRAY'
) ?
$v
: [
$v
];
}
}
my
@data
;
my
$changes
= 0;
while
( @{
$self
->{_subfields}} ) {
my
$code
=
shift
@{
$self
->{_subfields}};
my
$value
=
shift
@{
$self
->{_subfields}};
if
(
exists
$values
{
$code
} ) {
if
(
defined
$values
{
$code
} ) {
my
@vals
=
grep
{
defined
$_
} @{
$values
{
$code
}};
push
@data
,
map
{
$code
=>
"$_"
}
@vals
;
$changes
+=
scalar
@vals
;
$values
{
$code
} =
undef
;
}
}
else
{
push
@data
,
$code
=>
$value
;
}
}
foreach
my
$code
(
@order
) {
next
unless
defined
$values
{
$code
};
my
@vals
=
grep
{
defined
$_
} @{
$values
{
$code
}};
$changes
+=
scalar
@vals
;
push
@data
,
map
{
$code
=>
"$_"
}
@vals
;
}
$self
->{_subfields} = \
@data
;
return
$changes
;
}
sub
replace {
my
$self
=
shift
;
my
$new
;
if
(
@_
and UNIVERSAL::isa(
$self
,
'PICA::Field'
)) {
$new
=
shift
;
}
else
{
$new
= PICA::Field->new(
@_
);
}
%$self
=
%$new
;
}
sub
empty_subfields {
my
$self
=
shift
;
my
@list
;
my
@data
= @{
$self
->{_subfields}};
while
(
defined
(
my
$code
=
shift
@data
) ) {
push
(
@list
,
$code
)
if
shift
@data
eq
""
;
}
return
@list
;
}
sub
empty {
my
$self
=
shift
;
return
1
unless
@{
$self
->{_subfields}};
my
@data
= @{
$self
->{_subfields}};
while
(
defined
(
my
$code
=
shift
@data
) ) {
return
0
if
shift
@data
ne
""
;
}
return
1;
}
sub
purged {
my
$self
=
shift
;
my
@subfields
;
my
$code
;
foreach
(@{
$self
->{_subfields}}) {
if
(
defined
$code
) {
push
@subfields
, (
$code
,
$_
)
if
defined
$_
and
$_
ne
""
;
undef
$code
;
}
else
{
$code
=
$_
;
}
}
return
unless
@subfields
;
my
$copy
=
bless
{
_tag
=>
$self
->{_tag},
_occurrence
=>
$self
->{_occurrence},
_subfields
=> \
@subfields
},
ref
(
$self
);
return
$copy
;
}
sub
normalized {
my
$self
=
shift
;
my
$subfields
=
shift
;
return
$self
->string(
subfields
=>
$subfields
,
startfield
=>
$START_OF_FIELD
,
endfield
=>
$END_OF_FIELD
,
startsubfield
=>
$SUBFIELD_INDICATOR
);
}
sub
sort
{
my
(
$self
,
$order
) =
@_
;
return
unless
@{
$self
->{_subfields}};
$order
=
""
unless
defined
$order
;
my
(
%pos
,
$i
);
for
(
split
(
''
,
$order
.
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
)) {
$pos
{
$_
} =
$i
++
unless
defined
$pos
{
$_
};
}
my
@sf
= @{
$self
->{_subfields}};
my
$n
=
@sf
/ 2 - 1;
my
@sorted
= ();
@sorted
=
sort
{
$pos
{
$sf
[2
*$a
]} <=>
$pos
{
$sf
[2
*$b
]}
} (0..
$n
);
$self
->{_subfields} = [
map
{
$sf
[2
*$_
] =>
$sf
[2
*$_
+1] }
@sorted
];
}
sub
size {
my
$self
=
shift
;
return
@{
$self
->{_subfields}} / 2;
}
sub
string {
my
$self
=
shift
;
my
(
%args
) =
@_
?
@_
: ();
my
$subfields
=
defined
(
$args
{subfields}) ?
$args
{subfields} :
''
;
my
$startfield
=
defined
(
$args
{startfield}) ?
$args
{startfield} :
''
;
my
$endfield
=
defined
(
$args
{endfield}) ?
$args
{endfield} :
"\n"
;
my
$startsubfield
=
defined
(
$args
{startsubfield}) ?
$args
{startsubfield} :
'$'
;
my
@subs
;
my
$subs
=
$self
->{_subfields};
my
$nfields
=
@$subs
/ 2;
for
my
$i
( 1..
$nfields
) {
my
$offset
= (
$i
-1)*2;
my
$code
=
$subs
->[
$offset
];
my
$value
=
$subs
->[
$offset
+1];
if
(!
$subfields
||
$code
=~ /^[
$subfields
]$/) {
$value
=~ s/\$/\$\$/g
if
$startsubfield
eq
'$'
;
push
(
@subs
,
$code
.
$value
)
}
}
return
""
unless
@subs
;
my
$occ
=
''
;
$occ
=
"/"
.
$self
->{_occurrence}
if
defined
$self
->{_occurrence};
return
$startfield
.
$self
->{_tag} .
$occ
.
' '
.
$startsubfield
.
join
(
$startsubfield
,
@subs
) .
$endfield
;
}
my
$write_xml
=
sub
{
my
(
$self
,
$writer
) =
@_
;
my
(
$datafield
,
$subfield
);
if
(UNIVERSAL::isa(
$writer
,
'XML::Writer::Namespaces'
)) {
$datafield
= [
$PICA::Record::XMLNAMESPACE
,
'datafield'
];
$subfield
= [
$PICA::Record::XMLNAMESPACE
,
'subfield'
];
}
else
{
$datafield
=
'datafield'
;
$subfield
=
'subfield'
;
}
my
%attr
= (
'tag'
=>
$self
->{_tag});
$attr
{occurrence} =
$self
->{_occurrence}
if
defined
$self
->{_occurrence};
$writer
->startTag(
$datafield
,
%attr
);
my
$subs
=
$self
->{_subfields};
my
$nfields
=
@$subs
/ 2;
if
(
$nfields
) {
for
my
$i
( 1..
$nfields
) {
my
$offset
= (
$i
-1)*2;
$writer
->startTag(
$subfield
,
code
=>
$subs
->[
$offset
] );
$writer
->characters(
$subs
->[
$offset
+1] );
$writer
->endTag();
}
}
$writer
->endTag();
$writer
;
};
sub
xml {
my
$self
=
shift
;
my
%param
;
if
( UNIVERSAL::isa(
$_
[0],
'XML::Writer'
) ) {
(
%param
) = (
writer
=>
@_
);
}
elsif
(
ref
(
$_
[0]) ) {
(
%param
) = (
OUTPUT
=>
@_
);
}
else
{
(
%param
) =
@_
;
}
if
(
defined
$param
{writer} ) {
$write_xml
->(
$self
,
$param
{writer} );
return
$param
{writer};
}
else
{
my
(
$string
,
$sref
);
if
(not
defined
$param
{OUTPUT}) {
$sref
= \
$string
;
$param
{OUTPUT} =
$sref
;
}
my
$writer
= PICA::Writer::xmlwriter(
%param
);
$write_xml
->(
$self
,
$writer
);
return
defined
$sref
?
"$string"
:
$writer
;
}
}
sub
html {
my
$self
=
shift
;
my
%options
=
@_
;
my
$field
=
'field'
;
my
$tag
=
'tag'
;
my
$tagcode
=
'tagcode'
;
my
$occurrence
=
'occurrence'
;
my
$sfcode
=
'sfcode'
;
my
$sfindicator
=
'sfindicator'
;
my
$html
=
"<div class='$field'><span class='$tag'>"
.
"<span class='$tagcode'>"
.
$self
->{_tag} .
"</span>"
;
if
(
defined
$self
->{_occurrence}) {
$html
.=
"/<span class='$occurrence'>"
.
$self
->{_occurrence} .
"</span>"
;
}
else
{
}
$html
.=
"</span> "
;
my
$subs
=
$self
->{_subfields};
my
$nfields
=
@$subs
/ 2;
if
(
$nfields
) {
for
my
$i
( 1..
$nfields
) {
my
$offset
= (
$i
-1)*2;
my
$code
=
$subs
->[
$offset
];
my
$text
=
$subs
->[
$offset
+1];
$html
.=
"<span class='$sfindicator'>\$</span>"
.
"<span class='$sfcode'>$code</span>"
;
$text
=~ s/&/
&
;/g;
$text
=~ s/</
<
;/g;
$html
.=
$text
;
}
}
return
$html
.
"</div>\n"
;
}
sub
parse_pp_tag {
my
$tag
=
shift
;
my
(
$tagno
,
$occurrence
) =
split
(
'/'
,
$tag
);
undef
$tagno
unless
defined
$tagno
and
$tagno
=~
$FIELD_TAG_REGEXP
;
undef
$occurrence
unless
defined
$occurrence
and
$occurrence
=~
$FIELD_OCCURRENCE_REGEXP
;
return
(
$occurrence
,
$tagno
);
}
1;