Group
Extension

PICA-Data/lib/PICA/Writer/Base.pm

package PICA::Writer::Base;
use v5.14.1;

our $VERSION = '2.12';

use Scalar::Util qw(blessed openhandle reftype);
use PICA::Schema qw(clean_pica);
use Term::ANSIColor;
use Encode qw(decode);
use Carp   qw(croak);

sub new {
    my $class = shift;
    my (%options) = @_ % 2 ? (fh => @_) : @_;

    my $self = bless \%options, $class;

    my $fh = $self->{fh} // \*STDOUT;
    if (!ref $fh) {
        if (open(my $handle, '>:encoding(UTF-8)', $fh)) {
            $fh = $handle;
        }
        else {
            croak "cannot open file for writing: $fh\n";
        }
    }
    elsif (reftype $fh eq 'SCALAR' and !blessed $fh) {
        if (length $$fh) {
            $$fh = decode("UTF-8", $$fh);
        }
        open(my $handle, '>>:encoding(UTF-8)', $fh);
        $fh = $handle;
    }
    elsif (!openhandle($fh) and !(blessed $fh && $fh->can('print'))) {
        croak 'expect filehandle or object with method print!';
    }
    $self->{fh} = $fh;

    $self;
}

sub write {
    my $self = shift;
    $self->write_record($_) for @_;
    $self;
}

sub write_identifier {
    my ($self, $field) = @_;

    my $fh  = $self->{fh};
    my %col = %{$self->{color} // {}};

    $fh->print($col{tag} ? colored($field->[0], $col{tag}) : $field->[0]);

    if ($field->[1] > 0) {
        my $occ = sprintf("%02d", $field->[1]);
        $fh->print(($col{syntax} ? colored('/', $col{syntax}) : '/')
            . ($col{occurrence} ? colored($occ, $col{occurrence}) : $occ));
    }
}

sub write_record {
    my ($self, $record) = @_;
    $record = clean_pica(
        $record,
        allow_empty_subfields => 1,
        ignore_empty_records  => 1
    ) or return;
    return unless @$record;

    my $fh = $self->{fh};
    $self->write_field($_) for @$record;
    $fh->print($self->END_OF_RECORD);
}

sub write_field {
    my ($self, $field) = @_;

    $self->write_start_field($field);

    for (my $i = 3; $i < scalar @$field; $i += 2) {
        $self->write_subfield($field->[$i - 1], $field->[$i]);
    }

    $self->{fh}->print($self->END_OF_FIELD);
}

sub annotation {
    my ($self, $field) = @_;

    return unless $self->{annotate} // @$field % 2;
    return @$field % 2 ? $field->[$#$field] : " ";
}

sub write_start_field {
    my ($self, $field) = @_;

    # ignore annotation by default
    $self->write_identifier($field);
    $self->{fh}->print(' ');
}

sub write_subfield {
    my ($self, $code, $value) = @_;
    $self->{fh}->print($self->SUBFIELD_INDICATOR . $code . $value);
}

sub end {
    my $self = shift;
    close $self->{fh} if $self->{fh} ne \*STDOUT;
}

1;
__END__

=head1 NAME

PICA::Writer::Base - Base class of PICA+ writers

=head1 SYNOPSIS

    use PICA::Writer::Plain;
    my $writer = PICA::Writer::Plain->new( $fh );

    foreach my $record (@pica_records) {
        $writer->write($record);
    }

    use PICA::Writer::Plus;
    $writer = PICA::Writer::Plus->new( $fh );
    ...

    use PICA::Writer::XML;
    $writer = PICA::Writer::XML->new( $fh );
    ...

=head1 DESCRIPTION

This abstract base class of PICA+ writers should not be instantiated directly.
Use one of the following subclasses instead:

=over 

=item L<PICA::Writer::Plain>

=item L<PICA::Writer::Plus>

=item L<PICA::Writer::XML>

=item L<PICA::Writer::PPXML>

=item L<PICA::Writer::JSON>

=item L<PICA::Writer::Generic>

=item L<PICA::Writer::Patch>

=back

=head1 METHODS

=head2 new( [ $fh | fh => $fh ] [ %options ] )

Create a new PICA writer, writing to STDOUT by default. The optional C<fh>
argument can be a filename, a handle or any other blessed object with a
C<print> method, e.g. L<IO::Handle>.

L<PICA::Data> also provides a functional constructor C<pica_writer>.

=head2 write ( @records )

Writes one or more records, given as hash with key 'C<record>' or as array
reference with a list of fields, as described in L<PICA::Data>. Records
are syntactically validated with L<PICA::Schema>'s C<clean_pica>.

=head2 write_record ( $record ) 

Writes one record.

=head2 end

Finishes writing by closing the file handle (unless writing to STDOUT).

=head1 OPTIONS

=head2 color

Syntax highlighting can be enabled for L<PICA::Writer::Plain> and
L<PICA::Writer::Plus> using color names from L<Term::ANSIColor>, e.g.

    pica_writer('plain', color => {
      tag => 'blue',
      occurrence => 'magenta',
      code => 'green',
      value => 'white',
      syntax => 'yellow',
    })

=head2 annotate

Writer L<PICA::Writer::Plain> and L<PICA::Writer::Plus> includes optional field
annotations. Set this option to true to enforce field annotations (set to an
empty space if missing) or to false to ignore them.

=head1 SEE ALSO

See L<PICA::Parser::Base> for corresponding parser modules.

See L<Catmandu::Exporter::PICA> for usage of this module within the L<Catmandu>
framework (recommended). 

Alternative (outdated) PICA+ writers had been implemented as L<PICA::Writer>
and L<PICA::XMLWriter> included in the release of L<PICA::Record>.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.