File Coverage

blib/lib/Locale/Maketext/Lexicon.pm
Criterion Covered Total %
statement 185 229 80.7
branch 58 112 51.7
condition 12 30 40.0
subroutine 31 38 81.5
pod 0 10 0.0
total 286 419 68.2


line stmt bran cond sub pod time code
1             package Locale::Maketext::Lexicon;
2             $Locale::Maketext::Lexicon::VERSION = '1.00';
3 17     17   190850 use 5.004;
  17         65  
  17         690  
4 17     17   113 use strict;
  17         39  
  17         18579  
5              
6             # ABSTRACT: Use other catalog formats in Maketext
7              
8              
9             our %Opts;
10 214 50   214 0 655 sub option { shift if ref( $_[0] ); $Opts{ lc $_[0] } }
  214         1988  
11 38 50   38 0 158 sub set_option { shift if ref( $_[0] ); $Opts{ lc $_[0] } = $_[1] }
  38         185  
12              
13             sub encoding {
14 70 50   70 0 159 my $encoding = option( @_, 'encoding' ) or return;
15 1 0       13 return $encoding unless lc($encoding) eq 'locale';
16              
17 1         7 local $^W; # no warnings 'uninitialized', really.
18 1         2 my ( $country_language, $locale_encoding );
19              
20 1         12 local $@;
21             eval {
22 0         0 require I18N::Langinfo;
23 0         0 $locale_encoding
24             = I18N::Langinfo::langinfo( I18N::Langinfo::CODESET() );
25 1 0       7 } or eval {
26 1         2 require Win32::Console;
27 1         14 $locale_encoding = 'cp' . Win32::Console::OutputCP();
28             };
29 0 0       0 if ( !$locale_encoding ) {
30 0         0 foreach my $key (qw( LANGUAGE LC_ALL LC_MESSAGES LANG )) {
31 0 0       0 $ENV{$key} =~ /^([^.]+)\.([^.:]+)/ or next;
32 0         0 ( $country_language, $locale_encoding ) = ( $1, $2 );
33 0         0 last;
34             }
35             }
36 0 0 0     0 if ( defined $locale_encoding
      0        
37             && lc($locale_encoding) eq 'euc'
38             && defined $country_language )
39             {
40 0 0       0 if ( $country_language =~ /^ja_JP|japan(?:ese)?$/i ) {
    0          
    0          
    0          
41 0         0 $locale_encoding = 'euc-jp';
42             }
43             elsif ( $country_language =~ /^ko_KR|korean?$/i ) {
44 0         0 $locale_encoding = 'euc-kr';
45             }
46             elsif ( $country_language =~ /^zh_CN|chin(?:a|ese)?$/i ) {
47 0         0 $locale_encoding = 'euc-cn';
48             }
49             elsif ( $country_language =~ /^zh_TW|taiwan(?:ese)?$/i ) {
50 0         0 $locale_encoding = 'euc-tw';
51             }
52             }
53              
54 0         0 return $locale_encoding;
55             }
56              
57             sub import {
58 28     29   49974 my $class = shift;
59 28 50       122 return unless @_;
60              
61 28         37 my %entries;
62 28 100       156 if ( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
    50          
63              
64             # a hashref with $lang as keys, [$format, $src ...] as values
65 26         44 %entries = %{ $_[0] };
  26         149  
66             }
67             elsif ( @_ % 2 == 0 ) {
68 2         23 %entries = ( '' => [ splice @_, 0, 2 ], @_ );
69             }
70              
71             # expand the wildcard entry
72 28 100       129 if ( my $wild_entry = delete $entries{'*'} ) {
73 19         110 while ( my ( $format, $src ) = splice( @$wild_entry, 0, 2 ) ) {
74 19 50       51 next if ref($src); # XXX: implement globbing for the 'Tie' backend
75              
76 19         56 my $pattern = quotemeta($src);
77 19 50       182 $pattern =~ s/\\\*(?=[^*]+$)/\([-\\w]+\)/g or next;
78 19         47 $pattern =~ s/\\\*/.*?/g;
79 19         35 $pattern =~ s/\\\?/./g;
80 19         42 $pattern =~ s/\\\[/[/g;
81 19         32 $pattern =~ s/\\\]/]/g;
82 19         43 $pattern =~ s[\\\{(.*?)\\\\}][
83 0         0 '(?:'.join('|', split(/,/, $1)).')'
84             ]eg;
85              
86 19         131 require File::Glob;
87 19         3640 foreach my $file ( File::Glob::bsd_glob($src) ) {
88 57 50       599 $file =~ /$pattern/ or next;
89 57 50       169 push @{ $entries{$1} }, ( $format => $file ) if $1;
  57         244  
90             }
91 0         0 delete $entries{$1}
92             unless !defined($1)
93 19 0 0     139 or exists $entries{$1} and @{ $entries{$1} };
      33        
94             }
95             }
96              
97 28         64 %Opts = ();
98 28         210 foreach my $key ( grep /^_/, keys %entries ) {
99 27         165 set_option( lc( substr( $key, 1 ) ) => delete( $entries{$key} ) );
100             }
101 28         109 my $OptsRef = {%Opts};
102              
103 28         119 while ( my ( $lang, $entry ) = each %entries ) {
104 78         210 my $export = caller;
105              
106 78 100       177 if ( length $lang ) {
107              
108             # normalize language tag to Maketext's subclass convention
109 76         142 $lang = lc($lang);
110 76         138 $lang =~ s/-/_/g;
111 76         146 $export .= "::$lang";
112             }
113              
114 78 50       91 my @pairs = @{ $entry || [] } or die "no format specified";
  78 50       383  
115              
116 78         272 while ( my ( $format, $src ) = splice( @pairs, 0, 2 ) ) {
117 80 50 100     856 if ( defined($src) and !ref($src) and $src =~ /\*/ ) {
      66        
118             unshift( @pairs, $format => $_ )
119 0         0 for File::Glob::bsd_glob($src);
120 0         0 next;
121             }
122              
123             my @content
124 80         216 = eval { $class->lexicon_get( $src, scalar caller(1), $lang ); };
  80         567  
125 80 100 66     398 next if $@ and $@ =~ /^next\b/;
126 78 50       184 die $@ if $@;
127              
128 17     17   143 no strict 'refs';
  17         36  
  17         10243  
129 11 50   11   8908 eval "use $class\::$format; 1" or die $@;
  11     7   31  
  11     6   192  
  7     6   5347  
  7     6   16  
  7     6   131  
  6     6   43  
  6     6   12  
  6     5   106  
  6         38  
  6         13  
  6         92  
  6         42  
  6         12  
  6         160  
  6         3660  
  6         67  
  6         97  
  6         3147  
  6         15  
  6         241  
  6         39  
  6         14  
  6         91  
  5         32  
  5         8  
  5         75  
  78         6972  
130              
131 78 100       140 if ( %{"$export\::Lexicon"} ) {
  78         774  
132 51         73 my $lexicon = \%{"$export\::Lexicon"};
  51         157  
133 51 50       162 if ( my $obj = tied %$lexicon ) {
134              
135             # if it's our tied hash then force loading
136             # otherwise late load will rewrite
137 0 0       0 $obj->_force if $obj->isa(__PACKAGE__);
138             }
139              
140             # clear the memoized cache for old entries:
141 51         281 Locale::Maketext->clear_isa_scan;
142              
143 51         464 my $new = "$class\::$format"->parse(@content);
144              
145             # avoid hash rebuild, on big sets
146 50         325 @{$lexicon}{ keys %$new } = values %$new;
  50         684  
147             }
148             else {
149 27 50       161 local $^W if $] >= 5.009; # no warnings 'once', really.
150 27         40 tie %{"$export\::Lexicon"}, __PACKAGE__,
  27         332  
151             {
152             Opts => $OptsRef,
153             Export => "$export\::Lexicon",
154             Class => "$class\::$format",
155             Content => \@content,
156             };
157 27 100       1536 tied( %{"$export\::Lexicon"} )->_force
  1         7  
158             if $OptsRef->{'preload'};
159             }
160              
161 77 100       2085 length $lang or next;
162              
163             # Avoid re-entry
164 75         1662 my $caller = caller();
165 75 100       1306 next if $export->isa($caller);
166              
167 16         25 push( @{"$export\::ISA"}, scalar caller );
  16         236  
168              
169 16 100       67 if ( my $style = option('style') ) {
170 7 50       112 my $cref
171             = $class->can( lc("_style_$style") )
172             ->( $class, $export->can('maketext') )
173             or die "Unknown style: $style";
174              
175             # Avoid redefinition warnings
176 7     0   47 local $SIG{__WARN__} = sub {1};
  0         0  
177 7         9 *{"$export\::maketext"} = $cref;
  7         121  
178             }
179             }
180             }
181             }
182              
183             sub _style_gettext {
184 7     7   11 my ( $self, $orig ) = @_;
185              
186 7         29 require Locale::Maketext::Lexicon::Gettext;
187              
188             sub {
189 24     24   13356 my $lh = shift;
190 24         33 my $str = shift;
191 24         95 return $orig->(
192             $lh,
193             Locale::Maketext::Lexicon::Gettext::_gettext_to_maketext($str), @_
194             );
195             }
196 7         47 }
197              
198             sub TIEHASH {
199 27     27   56 my ( $class, $args ) = @_;
200 27         107 return bless( $args, $class );
201              
202             }
203              
204             {
205 17     17   107 no strict 'refs';
  17         45  
  17         10451  
206              
207             sub _force {
208 35     35   89 my $args = shift;
209 35 100       9864 unless ( $args->{'Done'} ) {
210 25         82 $args->{'Done'} = 1;
211 25         100 local *Opts = $args->{Opts};
212 25         263 *{ $args->{Export} }
  25         271  
213 25         55 = $args->{Class}->parse( @{ $args->{Content} } );
214 25 50       103 $args->{'Export'}{'_AUTO'} = 1
215             if option('auto');
216             }
217 35         463 return $args->{'Export'};
218             }
219 6     6   32 sub FETCH { _force( $_[0] )->{ $_[1] } }
220 3     3   367 sub EXISTS { _force( $_[0] )->{ $_[1] } }
221 0     0   0 sub DELETE { delete _force( $_[0] )->{ $_[1] } }
222 22     22   54795 sub SCALAR { scalar %{ _force( $_[0] ) } }
  22         108  
223 3     3   64 sub STORE { _force( $_[0] )->{ $_[1] } = $_[2] }
224 0     0   0 sub CLEAR { %{ _force( $_[0] )->{ $_[1] } } = () }
  0         0  
225 0     0   0 sub NEXTKEY { each %{ _force( $_[0] ) } }
  0         0  
226              
227             sub FIRSTKEY {
228 0     0   0 my $hash = _force( $_[0] );
229 0         0 my $a = scalar keys %$hash;
230 0         0 each %$hash;
231             }
232             }
233              
234             sub lexicon_get {
235 80     80 0 163 my ( $class, $src, $caller, $lang ) = @_;
236 80 100       193 return unless defined $src;
237              
238 78         157 foreach my $type ( qw(ARRAY HASH SCALAR GLOB), ref($src) ) {
239 377 100       1866 next unless UNIVERSAL::isa( $src, $type );
240              
241 7         22 my $method = 'lexicon_get_' . lc($type);
242 7 50       60 die "cannot handle source $type for $src: no $method defined"
243             unless $class->can($method);
244              
245 7         30 return $class->$method( $src, $caller, $lang );
246             }
247              
248             # default handler
249 71         252 return $class->lexicon_get_( $src, $caller, $lang );
250             }
251              
252             # for scalarrefs and arrayrefs we just dereference the $src
253 0     0 0 0 sub lexicon_get_scalar { ${ $_[1] } }
  0         0  
254 2     2 0 4 sub lexicon_get_array { @{ $_[1] } }
  2         10  
255              
256             sub lexicon_get_hash {
257 0     0 0 0 my ( $class, $src, $caller, $lang ) = @_;
258 0         0 return map { $_ => $src->{$_} } sort keys %$src;
  0         0  
259             }
260              
261             sub lexicon_get_glob {
262 5     5 0 12 my ( $class, $src, $caller, $lang ) = @_;
263              
264 17     17   118 no strict 'refs';
  17         38  
  17         11113  
265 5 50       33 local $^W if $] >= 5.009; # no warnings 'once', really.
266              
267             # be extra magical and check for DATA section
268 5 50 66     43 if ( eof($src) and $src eq \*{"$caller\::DATA"}
  1   33     11  
  5         40  
269             or $src eq \*{"main\::DATA"} )
270             {
271              
272             # okay, the *DATA isn't initiated yet. let's read.
273             #
274 5         893 require FileHandle;
275 5         11734 my $fh = FileHandle->new;
276 5 50       186 my $package = ( ( $src eq \*{"main\::DATA"} ) ? 'main' : $caller );
  5         78  
277              
278 5 50 33     163 if ( $package eq 'main' and -e $0 ) {
279 5 50       24 $fh->open($0) or die "Can't open $0: $!";
280             }
281             else {
282 0         0 my $level = 1;
283 0         0 while ( my ( $pkg, $filename ) = caller( $level++ ) ) {
284 0 0       0 next unless $pkg eq $package;
285 0 0       0 next unless -e $filename;
286 0         0 next;
287              
288 0 0       0 $fh->open($filename) or die "Can't open $filename: $!";
289 0         0 last;
290             }
291             }
292              
293 5         402 while (<$fh>) {
294              
295             # okay, this isn't foolproof, but good enough
296 657 100       2330 last if /^__DATA__$/;
297             }
298              
299 5         1137 return <$fh>;
300             }
301              
302             # fh containing the lines
303 0         0 my $pos = tell($src);
304 0         0 my @lines = <$src>;
305 0         0 seek( $src, $pos, 0 );
306 0         0 return @lines;
307             }
308              
309             # assume filename - search path, open and return its contents
310             sub lexicon_get_ {
311 71     71 0 131 my ( $class, $src, $caller, $lang ) = @_;
312 71         185 $src = $class->lexicon_find( $src, $caller, $lang );
313 71 100       192 defined $src or die 'next';
314              
315 69         7032 require FileHandle;
316 69         96282 my $fh = FileHandle->new;
317 69 50       2808 $fh->open($src) or die "Cannot read $src (called by $caller): $!";
318 69         4295 binmode($fh);
319 69         914460 return <$fh>;
320             }
321              
322             sub lexicon_find {
323 71     71 0 109 my ( $class, $src, $caller, $lang ) = @_;
324 71 100       2415 return $src if -e $src;
325              
326 3         24 require File::Spec;
327              
328 3         16 my @path = split '::', $caller;
329 3 50       13 push @path, $lang if length $lang;
330              
331 3         8 while (@path) {
332 8         18 foreach (@INC) {
333 77         585 my $file = File::Spec->catfile( $_, @path, $src );
334 77 100       1682 return $file if -e $file;
335             }
336 7         19 pop @path;
337             }
338              
339 2         5 return undef;
340             }
341              
342             1;
343              
344             __END__