File Coverage

blib/lib/DBD/PO/Text/PO.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package DBD::PO::Text::PO;
2            
3 18     18   150875 use strict;
  18         39  
  18         593  
4 18     18   146 use warnings;
  18         45  
  18         837  
5            
6             our $VERSION = '2.08';
7            
8 18     18   97 use Carp qw(croak);
  18         35  
  18         955  
9 18     18   1899 use English qw(-no_match_vars $OS_ERROR);
  18         5417  
  18         299  
10 18     18   21066 use Params::Validate qw(:all);
  18         215754  
  18         5042  
11 18     18   14275 use DBD::PO::Locale::PO qw(@FORMAT_FLAGS $ALLOW_LOST_BLANK_LINES);
  0            
  0            
12             use Socket qw($CRLF);
13             use Set::Scalar;
14            
15             use parent qw(Exporter);
16             our @EXPORT_OK = qw(
17             $EOL_DEFAULT
18             $SEPARATOR_DEFAULT
19             $CHARSET_DEFAULT
20             @COL_NAMES
21             );
22            
23             our $EOL_DEFAULT = $CRLF;
24             our $SEPARATOR_DEFAULT = "\n";
25             our $CHARSET_DEFAULT = 'iso-8859-1';
26            
27             our @COL_NAMES;
28             my @COL_PARAMETERS;
29             my @COL_METHODS;
30             our $LOST_BLANK_LINES;
31            
32             sub init {
33             my (undef, @config) = @_;
34            
35             my $config = Set::Scalar->new(@config);
36             my $allowed = Set::Scalar->new(
37             qw( :all :plural :previous :format allow_lost_blank_lines ),
38             @FORMAT_FLAGS,
39             );
40             my $not_allowed = $config - $allowed;
41             if ( ! $not_allowed->is_empty() ) {
42             croak 'Unkonwn config parameter: ', join ', ', $not_allowed->elements();
43             }
44             if ( $config->has(':all') ) {
45             $config->delete(':all');
46             $config->insert(qw(:plural :previous :format allow_lost_blank_lines));
47             }
48             my $has_plural = $config->has(':plural');
49             $config->delete(':plural');
50             my $has_previous = $config->has(':previous');
51             $config->delete(':previous');
52             if ( $config->has(':format') ) {
53             $config->delete(':format');
54             $config->insert(@FORMAT_FLAGS);
55             }
56             $ALLOW_LOST_BLANK_LINES = $config->has('allow_lost_blank_lines');
57             $config->delete('allow_lost_blank_lines');
58            
59             my @cols = (
60             # typical
61             [ qw( msgid -msgid msgid ) ], # original text
62             [ qw( msgstr -msgstr msgstr ) ], # translation
63             [ qw( comment -comment comment ) ], # translater comment
64             [ qw( automatic -automatic automatic ) ], # automatic comment
65             [ qw( reference -reference reference ) ],
66             [ qw( msgctxt -msgctxt msgctxt ) ], # context
67             # flags
68             [ qw( fuzzy -fuzzy fuzzy ) ],
69             # switch to ignore
70             [ qw( obsolete -obsolete obsolete ) ],
71             # plural only
72             (
73             $has_plural
74             ? (
75             [ qw( msgid_plural -msgid_plural msgid_plural ) ],
76             # dummy # dummy
77             [ qw( msgstr_0 -msgstr_0 msgstr_0 ) ], # singular or zero
78             [ qw( msgstr_1 -msgstr_1 msgstr_1 ) ], # plural or singular
79             [ qw( msgstr_2 -msgstr_2 msgstr_2 ) ], # plural
80             [ qw( msgstr_3 -msgstr_3 msgstr_3 ) ], # plural
81             [ qw( msgstr_4 -msgstr_4 msgstr_4 ) ], # plural
82             [ qw( msgstr_5 -msgstr_5 msgstr_5 ) ], # plural
83             )
84             : ()
85             ),
86             # prevoius
87             (
88             $has_previous
89             ? (
90             [ qw( previous_msgctxt -previous_msgctxt previous_msgctxt ) ],
91             [ qw( previous_msgid -previous_msgid previous_msgid ) ],
92             [ qw( previous_msgid_plural -previous_msgid_plural previous_msgid_plural ) ],
93             )
94             : ()
95             ),
96             # format-flags
97             (
98             map { ## no critic (ComplexMappings)
99             (my $col_name = $_) =~ tr{-}{_};
100             # dummy
101             ([ $col_name, "-$_", $_ ]);
102             } $config->elements()
103             ),
104             );
105            
106             @COL_NAMES = map {$_->[0]} @cols; # for SQL
107             @COL_PARAMETERS = map {$_->[1]} @cols; # for DBD::PO::Locale::PO->new(...)
108             @COL_METHODS = map {$_->[2]} @cols; # it is the method for the $po object
109            
110             return;
111             }
112             init();
113            
114             my $dequote = sub {
115             my $string = shift;
116            
117             return if $string eq 'NULL';
118             if ($string =~ s{\A _Q_U_O_T_E_D_:}{}xms) {
119             $string =~ s{\\\\}{\\}xmsg;
120             }
121            
122             return $string;
123             };
124            
125             my $array_from_anything = sub {
126             my ($self, $anything) = @_;
127            
128             my @array = map { ## no critic (ComplexMappings)
129             my $dequoted = $dequote->($_);
130             split m{\Q$self->{separator}\E}xms, $dequoted;
131             } ref $anything eq 'ARRAY'
132             ? @{$anything}
133             : defined $anything
134             ? $anything
135             : ();
136            
137             return \@array;
138             };
139            
140             sub new { ## no critic (RequireArgUnpacking)
141             my ($class, $options) = validate_pos(
142             @_,
143             {type => SCALAR},
144             {type => HASHREF},
145             );
146             $options = validate_with(
147             params => $options,
148             spec => {
149             eol => {type => SCALAR, default => $EOL_DEFAULT},
150             separator => {type => SCALAR, default => $SEPARATOR_DEFAULT},
151             charset => {type => SCALAR | UNDEF, optional => 1},
152             },
153             called => "2nd parameter of new('$class', \$hash_ref)",
154             );
155            
156             if ($options->{charset}) {
157             $options->{encoding} = ":encoding($options->{charset})";
158             }
159            
160             return bless $options, $class;
161             }
162            
163             sub write_entry { ## no critic (ExcessComplexity)
164             my ($self, $file_name, $file_handle, $col_ref) = @_;
165            
166             my %line;
167             for my $index (0 .. $#COL_NAMES) {
168             my $parameter = $COL_PARAMETERS[$index];
169             my $values = $array_from_anything->($self, $col_ref->[$index]);
170             if ( ## no critic (CascadingIfElse)
171             $parameter eq '-comment'
172             || $parameter eq '-automatic'
173             || $parameter eq '-reference'
174             ) {
175             if (@{$values}) {
176             $line{$parameter} = join $self->{eol}, @{$values};
177             }
178             }
179             elsif (
180             $parameter eq '-obsolete'
181             || $parameter eq '-fuzzy'
182             ) {
183             $line{$parameter} = $values->[0] ? 1 : 0;
184             }
185             elsif (
186             my ($prefix) = $parameter =~ m{\A - ( [a-z-]+ ) -format \z}xms
187             ) {
188             my $flag = $values->[0];
189             # translate:
190             # perl_false => nothing set
191             # -something => -no-flag = 1
192             # something => -flag = 1
193             if ($flag) {
194             $line{
195             (
196             $flag =~ m{\A -}xms
197             ? '-no'
198             : q{}
199             )
200             . "-$prefix-format"
201             } = 1;
202             }
203             }
204             elsif ( $parameter =~ m{\A -msgstr_ ( \d ) \z}xms ) {
205             if ( @{$values} ) {
206             $line{'-msgstr_n'}->{$1} = join "\n", @{$values};
207             }
208             }
209             else {
210             if ( @{$values} ) {
211             $line{$parameter} = join "\n", @{$values};
212             if (! tell $file_handle) {
213             if ($parameter eq '-msgid') {
214             croak 'A header has no msgid';
215             }
216             else { # -msgstr
217             if ($line{$parameter} !~ m{\b charset =}xms) { ## no critic (DeepNests)
218             croak 'This can not be a header';
219             }
220             }
221             }
222             }
223             else {
224             if ($parameter eq '-msgid' && tell $file_handle) {
225             croak 'A line has to have a msgid';
226             }
227             elsif ($parameter eq '-msgstr' && ! tell $file_handle) {
228             croak 'A header has to have a msgstr';
229             }
230             }
231             }
232             ++$index;
233             }
234             my $line = DBD::PO::Locale::PO->new(
235             eol => $self->{eol},
236             '-msgid' => q{},
237             (
238             exists $line{'-msgid_plural'}
239             ? ('-msgstr_n' => { 0 => q{} })
240             : ('-msgstr' => q{})
241             ),
242             %line,
243             )->dump();
244             print {$file_handle} $line
245             or croak "Print $file_name: $OS_ERROR";
246            
247             return $self;
248             }
249            
250             sub read_entry {
251             my ($self, $file_name, $file_handle) = @_;
252            
253             if (! defined $self->{line_number}) {
254             $self->{line_number} = 0;
255             }
256             my $po = DBD::PO::Locale::PO->load_entry(
257             $file_name,
258             $file_handle,
259             \$self->{line_number},
260             $self->{eol},
261             );
262             # EOF
263             if (! $po) {
264             delete $self->{line_number};
265             return [];
266             }
267             # run a line, it is a po object
268             my @cols;
269             my $index = 0;
270             METHOD:
271             for my $method (@COL_METHODS) {
272             if ( ## no critic (CascadingIfElse)
273             $method eq 'comment'
274             || $method eq 'automatic'
275             || $method eq 'reference'
276             ) {
277             my $comment = $po->$method();
278             $cols[$index]
279             = defined $comment
280             ? (
281             join $self->{separator},
282             split m{\Q$self->{eol}\E}xms,
283             $comment
284             )
285             : q{};
286             }
287             elsif (
288             $method eq 'obsolete'
289             || $method eq 'fuzzy'
290             ) {
291             $cols[$index] = $po->$method() ? 1 : 0;
292             }
293             elsif ( $method =~ m{\A [a-z-]+ -format \z}xms) {
294             my $flag = $po->format_flag($method);
295             # translate:
296             # undef => 0
297             # 0 => -1
298             # 1 => 1
299             $cols[$index] = defined $flag
300             ? (
301             $flag ? 1 : -1 ## no critic (MagicNumbers)
302             )
303             : 0;
304             }
305             elsif (
306             $method =~ m{
307             \A (?:
308             msgstr
309             | (?: msg | previous_msg ) (?: ctxt | id | id_plural )
310             ) \z
311             }xms
312             ) {
313             my $data = $po->$method();
314             if (! defined $data) {
315             $data = q{};
316             }
317             $cols[$index]
318             = join $self->{separator},
319             split m{\\n}xms,
320             $data;
321             }
322             elsif ( my ($n) = $method =~ m{\A msgstr_ ( \d ) \z}xms ) {
323             my $data = $po->msgstr_n();
324             if ($data) {
325             $data = $data->{$n};
326             }
327             if (! defined $data) {
328             $data = q{};
329             }
330             $cols[$index]
331             = join $self->{separator},
332             split m{\\n}xms,
333             $data;
334             }
335             else {
336             croak "Strange extract method $method";
337             }
338             ++$index;
339             }
340            
341             return \@cols;
342             }
343            
344             1;
345            
346             __END__