File Coverage

blib/lib/Locale/CLDR/Collator.pm
Criterion Covered Total %
statement 67 153 43.7
branch 9 44 20.4
condition 5 27 18.5
subroutine 16 26 61.5
pod 0 13 0.0
total 97 263 36.8


line stmt bran cond sub pod time code
1              
2             use version;
3 21     21   127 our $VERSION = version->declare('v0.34.1');
  21         37  
  21         138  
4              
5             use v5.10.1;
6 21     21   1894 use mro 'c3';
  21         59  
7 21     21   87 use utf8;
  21         32  
  21         140  
8 21     21   454 use if $^V ge v5.12.0, feature => 'unicode_strings';
  21         54  
  21         222  
9 21     21   863  
  21         39  
  21         747  
10             #line 6538
11             use Unicode::Normalize('NFD');
12             use Unicode::UCD qw( charinfo );
13             use List::MoreUtils qw(pairwise);
14             use Moo;
15             use Types::Standard qw(Str Int Maybe ArrayRef InstanceOf RegexpRef Bool);
16             with 'Locale::CLDR::CollatorBase';
17              
18             my $NUMBER_SORT_TOP = "\x{FD00}\x{0034}";
19             my $LEVEL_SEPARATOR = "\x{0001}";
20              
21             has 'type' => (
22             is => 'ro',
23             isa => Str,
24             default => 'standard',
25             );
26              
27             has 'locale' => (
28             is => 'ro',
29             isa => Maybe[InstanceOf['Locale::CLDR']],
30             default => undef,
31             predicate => 'has_locale',
32             );
33              
34             has 'alternate' => (
35             is => 'ro',
36             isa => Str,
37             default => 'noignore'
38             );
39              
40             # Note that backwards is only at level 2
41             has 'backwards' => (
42             is => 'ro',
43             isa => Str,
44             default => 'false',
45             );
46              
47             has 'case_level' => (
48             is => 'ro',
49             isa => Str,
50             default => 'false',
51             );
52              
53             has 'case_ordering' => (
54             is => 'ro',
55             isa => Str,
56             default => 'false',
57             );
58              
59             has 'normalization' => (
60             is => 'ro',
61             isa => Str,
62             default => 'true',
63             );
64              
65             has 'numeric' => (
66             is => 'ro',
67             isa => Str,
68             default => 'false',
69             );
70              
71             has 'reorder' => (
72             is => 'ro',
73             isa => ArrayRef,
74             default => sub { [] },
75             );
76              
77             has 'strength' => (
78             is => 'ro',
79             isa => Int,
80             default => 3,
81             );
82              
83             has 'max_variable' => (
84             is => 'ro',
85             isa => Str,
86             default => chr(0x0397),
87             );
88              
89             has _character_rx => (
90             is => 'ro',
91             isa => RegexpRef,
92             lazy => 1,
93             init_arg => undef,
94             default => sub {
95             my $self = shift;
96             my $list = join '|', @{$self->multi_rx()}, '.';
97             return qr/\G($list)/s;
98             },
99             );
100              
101             has _in_variable_weigting => (
102             is => 'rw',
103             isa => Bool,
104             init_arg => undef,
105             default => 0,
106             );
107              
108             # Set up the locale overrides
109             my $self = shift;
110            
111             my $overrides = [];
112             if ($self->has_locale) {
113             $overrides = $self->locale->_collation_overrides($self->type);
114             }
115            
116             foreach my $override (@$overrides) {
117             $self->_set_ce(@$override);
118             }
119             }
120              
121             # Get the collation element at the current strength
122             my ($self, $string) = @_;
123             my @ce;
124             if ($self->numeric eq 'true' && $string =~/^\p{Nd}^/) {
125             my $numeric_top = $self->collation_elements()->{$NUMBER_SORT_TOP};
126             my @numbers = $self->_convert_digits_to_numbers($string);
127             @ce = map { "$numeric_top${LEVEL_SEPARATOR}№$_" } @numbers;
128             }
129             else {
130             my $rx = $self->_character_rx;
131             my @characters = $string =~ /$rx/g;
132            
133             foreach my $character (@characters) {
134             my @current_ce;
135             if (length $character > 1) {
136             # We have a collation element that dependeds on two or more codepoints
137             # Remove the code points that the collation element depends on and if
138             # there are still codepoints get the collation elements for them
139             my @multi_rx = @{$self->multi_rx};
140             my $multi;
141             for (my $count = 0; $count < @multi_rx; $count++) {
142             if ($character =~ /$multi_rx[$count]/) {
143             $multi = $self->multi_class()->[$count];
144             last;
145             }
146             }
147            
148             my $match = $character;
149             eval "\$match =~ tr/$multi//cd;";
150             push @current_ce, $self->collation_elements()->{$match};
151             $character =~ s/$multi//g;
152             if (length $character) {
153             foreach my $codepoint (split //, $character) {
154             push @current_ce,
155             $self->collation_elements()->{$codepoint}
156             // $self->generate_ce($codepoint);
157             }
158             }
159             }
160             else {
161             my $ce = $self->collation_elements()->{$character};
162             $ce //= $self->generate_ce($character);
163             push @current_ce, $ce;
164             }
165             push @ce, $self->_process_variable_weightings(@current_ce);
166             }
167             }
168             return @ce;
169             }
170              
171             my ($self, @ce) = @_;
172             return @ce if $self->alternate() eq 'noignore';
173            
174             foreach my $ce (@ce) {
175             if ($ce->[0] le $self->max_variable) {
176             # Variable waighted codepoint
177             if ($self->alternate eq 'blanked') {
178             @$ce = map { chr() } qw(0 0 0);
179            
180             }
181             if ($self->alternate eq 'shifted') {
182             my $l4;
183             if ($ce->[0] eq "\0" && $ce->[1] eq "\0" && $ce->[2] eq "\0") {
184             $ce->[3] = "\0";
185             }
186             else {
187             $ce->[3] = $ce->[1];
188             }
189             @$ce[0 .. 2] = map { chr() } qw (0 0 0);
190             }
191             $self->_in_variable_weigting(1);
192             }
193             else {
194             if ($self->_in_variable_weigting()) {
195             if( $ce->[0] eq "\0" && $self->alternate eq 'shifted' ) {
196             $ce->[3] = "\0";
197             }
198             elsif($ce->[0] ne "\0") {
199             $self->_in_variable_weigting(0);
200             if ( $self->alternate eq 'shifted' ) {
201             $ce->[3] = chr(0xFFFF)
202             }
203             }
204             }
205             }
206             }
207             }
208              
209             # Converts $string into a sort key. Two sort keys can be correctly sorted by cmp
210             my ($self, $string) = @_;
211              
212             $string = NFD($string) if $self->normalization eq 'true';
213              
214             my @sort_key;
215            
216             my @ce = $self->get_collation_elements($string);
217              
218             for (my $count = 0; $count < $self->strength(); $count++ ) {
219             foreach my $ce (@ce) {
220             $ce = [ split //, $ce] unless ref $ce;
221             if (defined $ce->[$count] && $ce->[$count] ne "\0") {
222             push @sort_key, $ce->[$count];
223             }
224             }
225             }
226            
227             return join "\0", @sort_key;
228             }
229              
230             my ($self, $character) = @_;
231            
232             my $aaaa;
233             my $bbbb;
234            
235             if ($^V ge v5.26 && eval q($character =~ /(?!\p{Cn})(?:\p{Block=Tangut}|\p{Block=Tangut_Components})/)) {
236             $aaaa = 0xFB00;
237             $bbbb = (ord($character) - 0x17000) | 0x8000;
238             }
239             # Block Nushu was added in Perl 5.28
240             elsif ($^V ge v5.28 && eval q($character =~ /(?!\p{Cn})\p{Block=Nushu}/)) {
241             $aaaa = 0xFB01;
242             $bbbb = (ord($character) - 0x1B170) | 0x8000;
243             }
244             elsif ($character =~ /(?=\p{Unified_Ideograph=True})(?:\p{Block=CJK_Unified_Ideographs}|\p{Block=CJK_Compatibility_Ideographs})/) {
245             $aaaa = 0xFB40 + (ord($character) >> 15);
246             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
247             }
248             elsif ($character =~ /(?=\p{Unified_Ideograph=True})(?!\p{Block=CJK_Unified_Ideographs})(?!\p{Block=CJK_Compatibility_Ideographs})/) {
249             $aaaa = 0xFB80 + (ord($character) >> 15);
250             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
251             }
252             else {
253             $aaaa = 0xFBC0 + (ord($character) >> 15);
254             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
255             }
256             return join '', map {chr($_)} $aaaa, 0x0020, 0x0002, ord($LEVEL_SEPARATOR), $bbbb, 0, 0;
257             }
258              
259             # sorts a list according to the locales collation rules
260             my $self = shift;
261            
262             return map { $_->[0]}
263             sort { $a->[1] cmp $b->[1] }
264             map { [$_, $self->getSortKey($_)] }
265             @_;
266             }
267              
268             my ($self, $a, $b) = @_;
269            
270             return $self->getSortKey($a) cmp $self->getSortKey($b);
271             }
272              
273             my ($self, $a, $b) = @_;
274            
275             return $self->getSortKey($a) eq $self->getSortKey($b);
276             }
277              
278             my ($self, $a, $b) = @_;
279            
280             return $self->getSortKey($a) ne $self->getSortKey($b);
281             }
282              
283             my ($self, $a, $b) = @_;
284            
285             return $self->getSortKey($a) lt $self->getSortKey($b);
286             }
287              
288             my ($self, $a, $b) = @_;
289            
290             return $self->getSortKey($a) le $self->getSortKey($b);
291             }
292             my ($self, $a, $b) = @_;
293            
294             return $self->getSortKey($a) gt $self->getSortKey($b);
295             }
296              
297             my ($self, $a, $b) = @_;
298            
299             return $self->getSortKey($a) ge $self->getSortKey($b);
300             }
301              
302             # Get Human readable sort key
303             my ($self, $sort_key) = @_;
304            
305             my @levels = split/\x0/, $sort_key;
306            
307             foreach my $level (@levels) {
308             $level = join ' ', map { sprintf '%0.4X', ord } split //, $level;
309             }
310            
311             return '[ ' . join (' | ', @levels) . ' ]';
312             }
313              
314             my ($self, $digits) = @_;
315             my @numbers = ();
316             my $script = '';
317             foreach my $number (split //, $digits) {
318             my $char_info = charinfo(ord($number));
319             my ($decimal, $chr_script) = @{$char_info}{qw( decimal script )};
320             if ($chr_script eq $script) {
321             $numbers[-1] *= 10;
322             $numbers[-1] += $decimal;
323             }
324             else {
325             push @numbers, $decimal;
326             $script = $chr_script;
327             }
328             }
329             return @numbers;
330             }
331              
332             no Moo;
333              
334             1;
335              
336             # vim: tabstop=4