Jonathan Druart
9d6d641d1f
On bug 17591 we discovered that there was something weird going on with the way we export and use subroutines/modules. This patch tries to standardize our EXPORT to use EXPORT_OK only. That way we will need to explicitely define the subroutine we want to use from a module. This patch is a squashed version of: Bug 17600: After export.pl Bug 17600: After perlimport Bug 17600: Manual changes Bug 17600: Other manual changes after second perlimports run Bug 17600: Fix tests And a lot of other manual changes. export.pl is a dirty script that can be found on bug 17600. "perlimport" is: git clone https://github.com/oalders/App-perlimports.git cd App-perlimports/ cpanm --installdeps . export PERL5LIB="$PERL5LIB:/kohadevbox/koha/App-perlimports/lib" find . \( -name "*.pl" -o -name "*.pm" \) -exec perl App-perlimports/script/perlimports --inplace-edit --no-preserve-unused --filename {} \; The ideas of this patch are to: * use EXPORT_OK instead of EXPORT * perltidy the EXPORT_OK list * remove '&' before the subroutine names * remove some uneeded use statements * explicitely import the subroutines we need within the controllers or modules Note that the private subroutines (starting with _) should not be exported (and not used from outside of the module except from tests). EXPORT vs EXPORT_OK (from https://www.thegeekstuff.com/2010/06/perl-exporter-examples/) """ Export allows to export the functions and variables of modules to user’s namespace using the standard import method. This way, we don’t need to create the objects for the modules to access it’s members. @EXPORT and @EXPORT_OK are the two main variables used during export operation. @EXPORT contains list of symbols (subroutines and variables) of the module to be exported into the caller namespace. @EXPORT_OK does export of symbols on demand basis. """ If this patch caused a conflict with a patch you wrote prior to its push: * Make sure you are not reintroducing a "use" statement that has been removed * "$subroutine" is not exported by the C4::$MODULE module means that you need to add the subroutine to the @EXPORT_OK list * Bareword "$subroutine" not allowed while "strict subs" means that you didn't imported the subroutine from the module: - use $MODULE qw( $subroutine list ); You can also use the fully qualified namespace: C4::$MODULE::$subroutine Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
336 lines
8.1 KiB
Perl
336 lines
8.1 KiB
Perl
package Koha::Edifact;
|
|
|
|
# Copyright 2014,2015 PTFS-Europe Ltd
|
|
#
|
|
# This file is part of Koha.
|
|
#
|
|
# Koha is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Koha is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
|
|
|
use strict;
|
|
use warnings;
|
|
use File::Slurp qw( read_file );
|
|
use Carp qw( carp croak );
|
|
use Koha::Edifact::Segment;
|
|
use Koha::Edifact::Message;
|
|
|
|
my $separator = {
|
|
component => q{\:},
|
|
data => q{\+},
|
|
decimal => q{.},
|
|
release => q{\?},
|
|
reserved => q{ },
|
|
segment => q{\'},
|
|
};
|
|
|
|
sub new {
|
|
my ( $class, $param_hashref ) = @_;
|
|
my $transmission;
|
|
my $self = ();
|
|
|
|
if ( $param_hashref->{filename} ) {
|
|
if ( $param_hashref->{transmission} ) {
|
|
carp
|
|
"Cannot instantiate $class : both filename and transmission passed";
|
|
return;
|
|
}
|
|
$transmission = read_file( $param_hashref->{filename} );
|
|
}
|
|
else {
|
|
$transmission = $param_hashref->{transmission};
|
|
}
|
|
$self->{transmission} = _init($transmission);
|
|
|
|
bless $self, $class;
|
|
return $self;
|
|
}
|
|
|
|
sub interchange_header {
|
|
my ( $self, $field ) = @_;
|
|
|
|
my %element = (
|
|
sender => 1,
|
|
recipient => 2,
|
|
datetime => 3,
|
|
interchange_control_reference => 4,
|
|
application_reference => 6,
|
|
);
|
|
if ( !exists $element{$field} ) {
|
|
carp "No interchange header field $field available";
|
|
return;
|
|
}
|
|
my $data = $self->{transmission}->[0]->elem( $element{$field} );
|
|
return $data;
|
|
}
|
|
|
|
sub interchange_trailer {
|
|
my ( $self, $field ) = @_;
|
|
my $trailer = $self->{transmission}->[-1];
|
|
if ( $field eq 'interchange_control_count' ) {
|
|
return $trailer->elem(0);
|
|
}
|
|
elsif ( $field eq 'interchange_control_reference' ) {
|
|
return $trailer->elem(1);
|
|
}
|
|
carp "Trailer field $field not recognized";
|
|
return;
|
|
}
|
|
|
|
sub new_data_iterator {
|
|
my $self = shift;
|
|
my $offset = 0;
|
|
while ( $self->{transmission}->[$offset]->tag() ne 'UNH' ) {
|
|
++$offset;
|
|
if ( $offset == @{ $self->{transmission} } ) {
|
|
carp 'Cannot find message start';
|
|
return;
|
|
}
|
|
}
|
|
$self->{data_iterator} = $offset;
|
|
return 1;
|
|
}
|
|
|
|
sub next_segment {
|
|
my $self = shift;
|
|
if ( defined $self->{data_iterator} ) {
|
|
my $seg = $self->{transmission}->[ $self->{data_iterator} ];
|
|
if ( $seg->tag eq 'UNH' ) {
|
|
|
|
$self->{msg_type} = $seg->elem( 1, 0 );
|
|
}
|
|
elsif ( $seg->tag eq 'LIN' ) {
|
|
$self->{msg_type} = 'detail';
|
|
}
|
|
|
|
if ( $seg->tag ne 'UNZ' ) {
|
|
$self->{data_iterator}++;
|
|
}
|
|
else {
|
|
$self->{data_iterator} = undef;
|
|
}
|
|
return $seg;
|
|
}
|
|
return;
|
|
}
|
|
|
|
# for debugging return whole transmission
|
|
sub get_transmission {
|
|
my $self = shift;
|
|
|
|
return $self->{transmission};
|
|
}
|
|
|
|
sub message_type {
|
|
my $self = shift;
|
|
return $self->{msg_type};
|
|
}
|
|
|
|
sub _init {
|
|
my $msg = shift;
|
|
if ( !$msg ) {
|
|
return;
|
|
}
|
|
if ( $msg =~ s/^UNA(.{6})// ) {
|
|
if ( service_string_advice($1) ) {
|
|
return segmentize($msg);
|
|
|
|
}
|
|
return;
|
|
}
|
|
else {
|
|
my $s = substr $msg, 10;
|
|
croak "File does not start with a Service string advice :$s";
|
|
}
|
|
}
|
|
|
|
# return an array of Message objects
|
|
sub message_array {
|
|
my $self = shift;
|
|
|
|
# return an array of array_refs 1 ref to a message
|
|
my $msg_arr = [];
|
|
my $msg = [];
|
|
my $in_msg = 0;
|
|
foreach my $seg ( @{ $self->{transmission} } ) {
|
|
if ( $seg->tag eq 'UNH' ) {
|
|
$in_msg = 1;
|
|
push @{$msg}, $seg;
|
|
}
|
|
elsif ( $seg->tag eq 'UNT' ) {
|
|
$in_msg = 0;
|
|
if ( @{$msg} ) {
|
|
push @{$msg_arr}, Koha::Edifact::Message->new($msg);
|
|
$msg = [];
|
|
}
|
|
}
|
|
elsif ($in_msg) {
|
|
push @{$msg}, $seg;
|
|
}
|
|
}
|
|
return $msg_arr;
|
|
}
|
|
|
|
#
|
|
# internal parsing routines used in _init
|
|
#
|
|
sub service_string_advice {
|
|
my $ssa = shift;
|
|
|
|
# At present this just validates that the ssa
|
|
# is standard Edifact
|
|
# TBD reset the seps if non standard
|
|
if ( $ssa ne q{:+.? '} ) {
|
|
carp " Non standard Service String Advice [$ssa]";
|
|
return;
|
|
}
|
|
|
|
# else use default separators
|
|
return 1;
|
|
}
|
|
|
|
sub segmentize {
|
|
my $raw = shift;
|
|
|
|
# In practice edifact uses latin-1 but check
|
|
# Transport now converts to utf-8 on ingest
|
|
# Do not convert here
|
|
#my $char_set = 'iso-8859-1';
|
|
#if ( $raw =~ m/^UNB[+]UNO(.)/ ) {
|
|
# $char_set = msgcharset($1);
|
|
#}
|
|
#from_to( $raw, $char_set, 'utf8' );
|
|
|
|
my $re = qr{
|
|
(?> # dont backtrack into this group
|
|
[?]. # either the escape character
|
|
# followed by any other character
|
|
| # or
|
|
[^'?] # a character that is neither escape
|
|
# nor split
|
|
)+
|
|
}x;
|
|
my @segmented;
|
|
while ( $raw =~ /($re)/g ) {
|
|
push @segmented, Koha::Edifact::Segment->new( { seg_string => $1 } );
|
|
}
|
|
return \@segmented;
|
|
}
|
|
|
|
sub msgcharset {
|
|
my $code = shift;
|
|
if ( $code =~ m/^[^ABCDEF]$/ ) {
|
|
$code = 'default';
|
|
}
|
|
my %encoding_map = (
|
|
A => 'ascii',
|
|
B => 'ascii',
|
|
C => 'iso-8859-1',
|
|
D => 'iso-8859-1',
|
|
E => 'iso-8859-1',
|
|
F => 'iso-8859-1',
|
|
default => 'iso-8859-1',
|
|
);
|
|
return $encoding_map{$code};
|
|
}
|
|
|
|
1;
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Edifact - Edifact message handler
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Koha module for parsing Edifact messages
|
|
|
|
=head1 SUBROUTINES
|
|
|
|
=head2 new
|
|
|
|
my $e = Koha::Edifact->new( { filename => 'myfilename' } );
|
|
or
|
|
my $e = Koha::Edifact->new( { transmission => $msg_variable } );
|
|
|
|
instantiate the Edifact parser, requires either to be passed an in-memory
|
|
edifact message as transmission or a filename which it will read on creation
|
|
|
|
=head2 interchange_header
|
|
|
|
will return the data in the header field designated by the parameter
|
|
specified. Valid parameters are: 'sender', 'recipient', 'datetime',
|
|
'interchange_control_reference', and 'application_reference'
|
|
|
|
=head2 interchange_trailer
|
|
|
|
called either with the string 'interchange_control_count' or
|
|
'interchange_control_reference' will return the corresponding field from
|
|
the interchange trailer
|
|
|
|
=head2 new_data_iterator
|
|
|
|
Sets the object's data_iterator to point to the UNH segment
|
|
|
|
=head2 next_segment
|
|
|
|
Returns the next segment pointed to by the data_iterator. Increments the
|
|
data_iterator member or destroys it if segment UNZ has been reached
|
|
|
|
=head2 get_transmission
|
|
|
|
This method is useful in debugg:ing. Call on an Edifact object
|
|
it returns the object's transmission member
|
|
|
|
=head2 message_type
|
|
|
|
return the object's message type
|
|
|
|
=head2 message_array
|
|
|
|
return an array of Message objects contained in the Edifact transmission
|
|
|
|
=head1 Internal Methods
|
|
|
|
=head2 _init
|
|
|
|
Called by the constructor to do the parsing of the transmission
|
|
|
|
=head2 service_string_advice
|
|
|
|
Examines the Service String Advice returns 1 if the default separartors are in use
|
|
undef otherwise
|
|
|
|
=head2 segmentize
|
|
|
|
takes a raw Edifact message and returns a reference to an array of
|
|
its segments
|
|
|
|
=head2 msgcharset
|
|
|
|
Return the character set the message was encoded in. The default is iso-8859-1
|
|
|
|
We preserve this info but will have converted to utf-8 on ingest
|
|
|
|
=head1 AUTHOR
|
|
|
|
Colin Campbell <colin.campbell@ptfs-europe.com>
|
|
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright 2014,2015, PTFS-Europe Ltd
|
|
This program is free software, You may redistribute it under
|
|
under the terms of the GNU General Public License
|
|
|
|
|
|
=cut
|