File Coverage

blib/lib/Locale/CLDR/Collator.pm
Criterion Covered Total %
statement 86 144 59.7
branch 17 66 25.7
condition 9 24 37.5
subroutine 16 22 72.7
pod 0 9 0.0
total 128 265 48.3


line stmt bran cond sub pod time code
1             package Locale::CLDR::Collator;
2              
3 23     23   251 use version;
  23         86  
  23         297  
4             our $VERSION = version->declare('v0.46.0');
5              
6 23     23   3271 use v5.10.1;
  23         116  
7 23     23   183 use mro 'c3';
  23         58  
  23         243  
8 23     23   918 use utf8;
  23         88  
  23         184  
9 23     23   1136 use feature 'unicode_strings';
  23         44  
  23         3906  
10              
11             #line 7012
12             use Unicode::Normalize('NFD');
13             use Moo;
14             use Types::Standard qw(Str Int Maybe ArrayRef InstanceOf RegexpRef Bool);
15             with 'Locale::CLDR::CollatorBase';
16              
17             sub IsCLDREmpty {
18             return '';
19             }
20              
21             # Test for missing Unicode properties
22             BEGIN {
23             our %missing_unicode_properties = ();
24             my @properties = (qw(
25             Block=Tangut
26             Block=Tangut_Components
27             Block=Tangut_Supplement
28             Block=Nushu
29             Block=Khitan_Small_Script
30             Unified_Ideograph=True
31             Block=CJK_Unified_Ideograph
32             Block=CJK_Compatibility_Ideographs
33             ccc=0
34             ccc
35             ));
36              
37             foreach my $missing (@properties) {
38             $missing_unicode_properties{$missing} = 1
39             unless eval "'a' =~ qr/\\p{$missing}|a/";
40             }
41             }
42              
43             sub _fix_missing_unicode_properties {
44             my $self = shift;
45             my $regex = shift;
46             our %missing_unicode_properties;
47            
48            
49             return '' unless defined $regex;
50            
51             foreach my $missing (keys %missing_unicode_properties) {
52             $regex =~ s/\\(p)\{$missing\}/\\${1}{IsCLDREmpty}/ig
53             if $missing_unicode_properties{$missing};
54             }
55            
56             return qr/$regex/;
57             }
58              
59              
60             has 'type' => (
61             is => 'ro',
62             isa => Str,
63             default => 'standard',
64             );
65              
66             has 'locale' => (
67             is => 'ro',
68             isa => Maybe[InstanceOf['Locale::CLDR']],
69             default => undef,
70             predicate => 'has_locale',
71             );
72              
73             has 'alternate' => (
74             is => 'ro',
75             isa => Str,
76             default => 'noignore'
77             );
78              
79             # Note that backwards is only at level 2
80             has 'backwards' => (
81             is => 'ro',
82             isa => Str,
83             default => 'false',
84             );
85              
86             has 'case_level' => (
87             is => 'ro',
88             isa => Str,
89             default => 'false',
90             );
91              
92             has 'case_ordering' => (
93             is => 'ro',
94             isa => Str,
95             default => 'false',
96             );
97              
98             has 'normalization' => (
99             is => 'ro',
100             isa => Str,
101             default => 'true',
102             );
103              
104             has 'numeric' => (
105             is => 'ro',
106             isa => Str,
107             default => 'false',
108             );
109              
110             has 'reorder' => (
111             is => 'ro',
112             isa => ArrayRef,
113             default => sub { [] },
114             );
115              
116             has 'strength' => (
117             is => 'ro',
118             isa => Int,
119             default => 3,
120             );
121              
122             sub _generate_derived_ce {
123             my ($self, $character) = @_;
124              
125             my $aaaa;
126             my $bbbb;
127              
128             if ( $character =~ $self->_fix_missing_unicode_properties( '(?!\p{Cn})(?:\p{Block=Tangut}|\p{Block=Tangut_Components}|\p{Block=Tangut_Supplement})' )) {
129             $aaaa = 0xFB00;
130             $bbbb = (ord($character) - 0x17000) | 0x8000;
131             }
132             elsif ($character =~ $self->_fix_missing_unicode_properties( '(?!\p{Cn})\p{Block=Nushu}' )) {
133             $aaaa = 0xFB01;
134             $bbbb = (ord($character) - 0x1B170) | 0x8000;
135             }
136             elsif ($character =~ $self->_fix_missing_unicode_properties( '(?=\p{Unified_Ideograph=True})(?:\p{Block=CJK_Unified_Ideographs}|\p{Block=CJK_Compatibility_Ideographs})' )) {
137             $aaaa = 0xFB40 + (ord($character) >> 15);
138             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
139             }
140             elsif ($character =~ $self->_fix_missing_unicode_properties( '(?=\p{Unified_Ideograph=True})(?!\p{Block=CJK_Unified_Ideographs})(?!\p{Block=CJK_Compatibility_Ideographs})' )) {
141             $aaaa = 0xFB80 + (ord($character) >> 15);
142             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
143             }
144             else {
145             $aaaa = 0xFBC0 + (ord($character) >> 15);
146             $bbbb = (ord($character) & 0x7FFFF) | 0x8000;
147             }
148             return [[$aaaa, 0x0020, 0x0002], [$bbbb, 0, 0]];
149             }
150              
151             sub _process_variable_weightings {
152             my ($self, $ces) = @_;
153             return $ces if $self->alternate() eq 'noignore';
154              
155             foreach my $ce (@$ces) {
156             if ($ce->[0] <= $self->max_variable_weight && $ce->[0] >= $self->min_variable_weight) {
157             # Variable waighted codepoint
158             if ($self->alternate eq 'blanked') {
159             @$ce = qw(0 0 0);
160              
161             }
162             if ($self->alternate eq 'shifted') {
163             my $l4;
164             if ($ce->[0] == 0 && $ce->[1] == 0 && $ce->[2] == 0) {
165             $ce->[3] = 0;
166             }
167             else {
168             $ce->[3] = $ce->[1];
169             }
170             @$ce[0 .. 2] = qw(0 0 0);
171             }
172             $self->_in_variable_weigting(1);
173             }
174             else {
175             if ($self->_in_variable_weigting()) {
176             if( $ce->[0] == 0 && $self->alternate eq 'shifted' ) {
177             $ce->[3] = 0;
178             }
179             elsif($ce->[0] != 0) {
180             $self->_in_variable_weigting(0);
181             if ( $self->alternate eq 'shifted' ) {
182             $ce->[3] = 0xFFFF;
183             }
184             }
185             }
186             }
187             }
188            
189             return $ces;
190             }
191              
192             sub get_collation_elements {
193             my $self = shift;
194             my $string = shift;
195             my $ces = [];
196            
197            
198             while ($string) {
199             my ($match3) = $string =~ /^(...)/;
200             my ($match2) = $string =~ /^(..)/;
201             my ($match1) = $string =~ /^(.)/;
202             my $ce;
203            
204             my $matched = '';
205             $match1 //= '';
206             $match2 //= '';
207             $match3 //= '';
208            
209             if ($self->collation_elements->{$match3}) {
210             $matched = $match3;
211             $string =~ s/^...//;
212             $ce = $self->collation_elements->{$match3};
213             }
214             elsif ($self->collation_elements->{$match2}) {
215             $matched = $match2;
216             $string =~ s/^..//;
217             $ce = $self->collation_elements->{$match2};
218             }
219             elsif ($self->collation_elements->{$match1}) {
220             $matched = $match1;
221             $string =~ s/^.//;
222             $ce = $self->collation_elements->{$match1};
223             }
224            
225             if ($matched) {
226             my $regex = '';
227             if (_fix_missing_unicode_properties('ccc=0') !~ /IsCLDREmpty/) {
228             $regex = eval 'qr/^(\\P{ccc=0}+)/';
229             }
230             elsif (_fix_missing_unicode_properties('ccc') !~ /IsCLDREmpty/) {
231             $regex = eval 'qr/^(\\p{ccc}+)/';
232             }
233             if ($regex && (my ($ccc) = $string =~ $regex)) {
234             foreach my $cp (split //, $ccc) {
235             my $new_match = "$matched$cp";
236             if ($self->collation_elements->{$new_match}) {
237             $matched = $new_match;
238             $string =~ s/^.*?\K$cp//;
239             $ce = $self->collation_elements->{$new_match};
240             }
241             }
242             }
243             }
244            
245             if (! @$ce) {
246             $ce = $self->_generate_derived_ce($match1);
247             }
248            
249             push @$ces, @{$self->_process_variable_weightings($ce)};
250             }
251            
252             return $ces;
253             }
254              
255             # Converts $string into a sort key. Two sort keys can be correctly sorted by cmp
256             sub get_sort_key {
257             my ($self, $string) = @_;
258              
259             $string = NFD($string) if $self->normalization eq 'true';
260              
261             my @sort_key;
262              
263             my $ces = $self->get_collation_elements($string);
264              
265             for (my $count = 0; $count < $self->strength(); $count++ ) {
266             if ($count == 1 && $self->backwards ne 'noignore') {
267             foreach my $ce (reverse @$ces) {
268             if ($ce->[$count]) {
269             push @sort_key, $ce->[$count];
270             }
271             }
272             }
273             else {
274             foreach my $ce (@$ces) {
275             if ($ce->[$count]) {
276             push @sort_key, $ce->[$count];
277             }
278             }
279             }
280             push @sort_key, 0;
281             }
282              
283             return join '', map { chr $_ } @sort_key;
284             }
285              
286             sub sort {
287             my $self = shift;
288             my @elements = @_;
289            
290             return sort { $self->cmp($a,$b) } @elements;
291             }
292              
293             sub cmp {
294             my $self = shift;
295             my $s1 = shift;
296             my $s2 = shift;
297            
298             my $sk1 = $self->get_sort_key($s1);
299             my $sk2 = $self->get_sort_key($s2);
300            
301             return $sk1 cmp $sk2;
302             }
303              
304             sub eq {
305             my $self = shift;
306            
307             return $self->cmp(@_) == 0 ? 1 : 0;
308             }
309              
310             sub ne {
311             my $self = shift;
312            
313             return $self->cmp(@_) == 0 ? 0 : 1;
314             }
315              
316             sub lt {
317             my $self = shift;
318            
319             return $self->cmp(@_) == -1 ? 1 : 0;
320             }
321              
322             sub gt {
323             my $self = shift;
324            
325             return $self->cmp(@_) == 1 ? 1 : 0;
326             }
327              
328             no Moo;
329              
330             1;
331              
332             # vim: tabstop=4