ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/staticperl/perl/lib/Unicode/Collate.pm
Revision: 1.1
Committed: Thu Jun 30 14:27:17 2005 UTC (21 years, 4 months ago) by root
Branch: MAIN
CVS Tags: PERL-5-8-7, HEAD
Branch point for: PERL
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package Unicode::Collate;
2    
3     BEGIN {
4     unless ("A" eq pack('U', 0x41)) {
5     die "Unicode::Collate cannot stringify a Unicode code point\n";
6     }
7     }
8    
9     use 5.006;
10     use strict;
11     use warnings;
12     use Carp;
13     use File::Spec;
14    
15     no warnings 'utf8';
16    
17     our $VERSION = '0.40';
18     our $PACKAGE = __PACKAGE__;
19    
20     my @Path = qw(Unicode Collate);
21     my $KeyFile = "allkeys.txt";
22    
23     # Perl's boolean
24     use constant TRUE => 1;
25     use constant FALSE => "";
26     use constant NOMATCHPOS => -1;
27    
28     # A coderef to get combining class imported from Unicode::Normalize
29     # (i.e. \&Unicode::Normalize::getCombinClass).
30     # This is also used as a HAS_UNICODE_NORMALIZE flag.
31     my $CVgetCombinClass;
32    
33     # Supported Levels
34     use constant MinLevel => 1;
35     use constant MaxLevel => 4;
36    
37     # Minimum weights at level 2 and 3, respectively
38     use constant Min2Wt => 0x20;
39     use constant Min3Wt => 0x02;
40    
41     # Shifted weight at 4th level
42     use constant Shift4Wt => 0xFFFF;
43    
44     # A boolean for Variable and 16-bit weights at 4 levels of Collation Element
45     # PROBLEM: The Default Unicode Collation Element Table
46     # has weights over 0xFFFF at the 4th level.
47     # The tie-breaking in the variable weights
48     # other than "shift" (as well as "shift-trimmed") is unreliable.
49     use constant VCE_TEMPLATE => 'Cn4';
50    
51     # A sort key: 16-bit weights
52     # See also the PROBLEM on VCE_TEMPLATE above.
53     use constant KEY_TEMPLATE => 'n*';
54    
55     # Level separator in a sort key:
56     # i.e. pack(KEY_TEMPLATE, 0)
57     use constant LEVEL_SEP => "\0\0";
58    
59     # As Unicode code point separator for hash keys.
60     # A joined code point string (denoted by JCPS below)
61     # like "65;768" is used for internal processing
62     # instead of Perl's Unicode string like "\x41\x{300}",
63     # as the native code point is different from the Unicode code point
64     # on EBCDIC platform.
65     # This character must not be included in any stringified
66     # representation of an integer.
67     use constant CODE_SEP => ';';
68    
69     # boolean values of variable weights
70     use constant NON_VAR => 0; # Non-Variable character
71     use constant VAR => 1; # Variable character
72    
73     # specific code points
74     use constant Hangul_LBase => 0x1100;
75     use constant Hangul_LIni => 0x1100;
76     use constant Hangul_LFin => 0x1159;
77     use constant Hangul_LFill => 0x115F;
78     use constant Hangul_VBase => 0x1161;
79     use constant Hangul_VIni => 0x1160;
80     use constant Hangul_VFin => 0x11A2;
81     use constant Hangul_TBase => 0x11A7;
82     use constant Hangul_TIni => 0x11A8;
83     use constant Hangul_TFin => 0x11F9;
84     use constant Hangul_TCount => 28;
85     use constant Hangul_NCount => 588;
86     use constant Hangul_SBase => 0xAC00;
87     use constant Hangul_SIni => 0xAC00;
88     use constant Hangul_SFin => 0xD7A3;
89     use constant CJK_UidIni => 0x4E00;
90     use constant CJK_UidFin => 0x9FA5;
91     use constant CJK_ExtAIni => 0x3400;
92     use constant CJK_ExtAFin => 0x4DB5;
93     use constant CJK_ExtBIni => 0x20000;
94     use constant CJK_ExtBFin => 0x2A6D6;
95     use constant BMP_Max => 0xFFFF;
96    
97     # Logical_Order_Exception in PropList.txt
98     # TODO: synchronization with change of PropList.txt.
99     my $DefaultRearrange = [ 0x0E40..0x0E44, 0x0EC0..0x0EC4 ];
100    
101     sub UCA_Version { "11" }
102    
103     sub Base_Unicode_Version { "4.0" }
104    
105     ######
106    
107     sub pack_U {
108     return pack('U*', @_);
109     }
110    
111     sub unpack_U {
112     return unpack('U*', pack('U*').shift);
113     }
114    
115     ######
116    
117     my (%VariableOK);
118     @VariableOK{ qw/
119     blanked non-ignorable shifted shift-trimmed
120     / } = (); # keys lowercased
121    
122     our @ChangeOK = qw/
123     alternate backwards level normalization rearrange
124     katakana_before_hiragana upper_before_lower
125     overrideHangul overrideCJK preprocess UCA_Version
126     hangul_terminator variable
127     /;
128    
129     our @ChangeNG = qw/
130     entry mapping table maxlength
131     ignoreChar ignoreName undefChar undefName variableTable
132     versionTable alternateTable backwardsTable forwardsTable rearrangeTable
133     derivCode normCode rearrangeHash
134     backwardsFlag
135     /;
136     # The hash key 'ignored' is deleted at v 0.21.
137     # The hash key 'isShift' is deleted at v 0.23.
138     # The hash key 'combining' is deleted at v 0.24.
139     # The hash key 'entries' is deleted at v 0.30.
140     # The hash key 'L3_ignorable' is deleted at v 0.40.
141    
142     sub version {
143     my $self = shift;
144     return $self->{versionTable} || 'unknown';
145     }
146    
147     my (%ChangeOK, %ChangeNG);
148     @ChangeOK{ @ChangeOK } = ();
149     @ChangeNG{ @ChangeNG } = ();
150    
151     sub change {
152     my $self = shift;
153     my %hash = @_;
154     my %old;
155     if (exists $hash{variable} && exists $hash{alternate}) {
156     delete $hash{alternate};
157     }
158     elsif (!exists $hash{variable} && exists $hash{alternate}) {
159     $hash{variable} = $hash{alternate};
160     }
161     foreach my $k (keys %hash) {
162     if (exists $ChangeOK{$k}) {
163     $old{$k} = $self->{$k};
164     $self->{$k} = $hash{$k};
165     }
166     elsif (exists $ChangeNG{$k}) {
167     croak "change of $k via change() is not allowed!";
168     }
169     # else => ignored
170     }
171     $self->checkCollator;
172     return wantarray ? %old : $self;
173     }
174    
175     sub _checkLevel {
176     my $level = shift;
177     my $key = shift; # 'level' or 'backwards'
178     MinLevel <= $level or croak sprintf
179     "Illegal level %d (in value for key '%s') lower than %d.",
180     $level, $key, MinLevel;
181     $level <= MaxLevel or croak sprintf
182     "Unsupported level %d (in value for key '%s') higher than %d.",
183     $level, $key, MaxLevel;
184     }
185    
186     my %DerivCode = (
187     8 => \&_derivCE_8,
188     9 => \&_derivCE_9,
189     11 => \&_derivCE_9, # 11 == 9
190     );
191    
192     sub checkCollator {
193     my $self = shift;
194     _checkLevel($self->{level}, "level");
195    
196     $self->{derivCode} = $DerivCode{ $self->{UCA_Version} }
197     or croak "Illegal UCA version (passed $self->{UCA_Version}).";
198    
199     $self->{variable} ||= $self->{alternate} || $self->{variableTable} ||
200     $self->{alternateTable} || 'shifted';
201     $self->{variable} = $self->{alternate} = lc($self->{variable});
202     exists $VariableOK{ $self->{variable} }
203     or croak "$PACKAGE unknown variable tag name: $self->{variable}";
204    
205     if (! defined $self->{backwards}) {
206     $self->{backwardsFlag} = 0;
207     }
208     elsif (! ref $self->{backwards}) {
209     _checkLevel($self->{backwards}, "backwards");
210     $self->{backwardsFlag} = 1 << $self->{backwards};
211     }
212     else {
213     my %level;
214     $self->{backwardsFlag} = 0;
215     for my $b (@{ $self->{backwards} }) {
216     _checkLevel($b, "backwards");
217     $level{$b} = 1;
218     }
219     for my $v (sort keys %level) {
220     $self->{backwardsFlag} += 1 << $v;
221     }
222     }
223    
224     defined $self->{rearrange} or $self->{rearrange} = [];
225     ref $self->{rearrange}
226     or croak "$PACKAGE: list for rearrangement must be store in ARRAYREF";
227    
228     # keys of $self->{rearrangeHash} are $self->{rearrange}.
229     $self->{rearrangeHash} = undef;
230    
231     if (@{ $self->{rearrange} }) {
232     @{ $self->{rearrangeHash} }{ @{ $self->{rearrange} } } = ();
233     }
234    
235     $self->{normCode} = undef;
236    
237     if (defined $self->{normalization}) {
238     eval { require Unicode::Normalize };
239     $@ and croak "Unicode::Normalize is required to normalize strings";
240    
241     $CVgetCombinClass ||= \&Unicode::Normalize::getCombinClass;
242    
243     if ($self->{normalization} =~ /^(?:NF)D\z/) { # tweak for default
244     $self->{normCode} = \&Unicode::Normalize::NFD;
245     }
246     elsif ($self->{normalization} ne 'prenormalized') {
247     my $norm = $self->{normalization};
248     $self->{normCode} = sub {
249     Unicode::Normalize::normalize($norm, shift);
250     };
251     eval { $self->{normCode}->("") }; # try
252     $@ and croak "$PACKAGE unknown normalization form name: $norm";
253     }
254     }
255     return;
256     }
257    
258     sub new
259     {
260     my $class = shift;
261     my $self = bless { @_ }, $class;
262    
263     # If undef is passed explicitly, no file is read.
264     $self->{table} = $KeyFile if ! exists $self->{table};
265     $self->read_table if defined $self->{table};
266    
267     if ($self->{entry}) {
268     while ($self->{entry} =~ /([^\n]+)/g) {
269     $self->parseEntry($1);
270     }
271     }
272    
273     $self->{level} ||= MaxLevel;
274     $self->{UCA_Version} ||= UCA_Version();
275    
276     $self->{overrideHangul} = FALSE
277     if ! exists $self->{overrideHangul};
278     $self->{overrideCJK} = FALSE
279     if ! exists $self->{overrideCJK};
280     $self->{normalization} = 'NFD'
281     if ! exists $self->{normalization};
282     $self->{rearrange} = $self->{rearrangeTable} || $DefaultRearrange
283     if ! exists $self->{rearrange};
284     $self->{backwards} = $self->{backwardsTable}
285     if ! exists $self->{backwards};
286    
287     $self->checkCollator;
288    
289     return $self;
290     }
291    
292     sub read_table {
293     my $self = shift;
294    
295     my($f, $fh);
296     foreach my $d (@INC) {
297     $f = File::Spec->catfile($d, @Path, $self->{table});
298     last if open($fh, $f);
299     $f = undef;
300     }
301     defined $f
302     or croak "$PACKAGE: $self->{table} is not found in @INC";
303    
304     while (<$fh>) {
305     next if /^\s*#/;
306     unless (s/^\s*\@//) {
307     $self->parseEntry($_);
308     next;
309     }
310    
311     if (/^version\s*(\S*)/) {
312     $self->{versionTable} ||= $1;
313     }
314     elsif (/^variable\s+(\S*)/) { # since UTS #10-9
315     $self->{variableTable} ||= $1;
316     }
317     elsif (/^alternate\s+(\S*)/) { # till UTS #10-8
318     $self->{alternateTable} ||= $1;
319     }
320     elsif (/^backwards\s+(\S*)/) {
321     push @{ $self->{backwardsTable} }, $1;
322     }
323     elsif (/^forwards\s+(\S*)/) { # parhaps no use
324     push @{ $self->{forwardsTable} }, $1;
325     }
326     elsif (/^rearrange\s+(.*)/) { # (\S*) is NG
327     push @{ $self->{rearrangeTable} }, _getHexArray($1);
328     }
329     }
330     close $fh;
331     }
332    
333    
334     ##
335     ## get $line, parse it, and write an entry in $self
336     ##
337     sub parseEntry
338     {
339     my $self = shift;
340     my $line = shift;
341     my($name, $entry, @uv, @key);
342    
343     return if $line !~ /^\s*[0-9A-Fa-f]/;
344    
345     # removes comment and gets name
346     $name = $1
347     if $line =~ s/[#%]\s*(.*)//;
348     return if defined $self->{undefName} && $name =~ /$self->{undefName}/;
349    
350     # gets element
351     my($e, $k) = split /;/, $line;
352     croak "Wrong Entry: <charList> must be separated by ';' from <collElement>"
353     if ! $k;
354    
355     @uv = _getHexArray($e);
356     return if !@uv;
357    
358     $entry = join(CODE_SEP, @uv); # in JCPS
359    
360     if (defined $self->{undefChar} || defined $self->{ignoreChar}) {
361     my $ele = pack_U(@uv);
362    
363     # regarded as if it were not entried in the table
364     return
365     if defined $self->{undefChar} && $ele =~ /$self->{undefChar}/;
366    
367     # replaced as completely ignorable
368     $k = '[.0000.0000.0000.0000]'
369     if defined $self->{ignoreChar} && $ele =~ /$self->{ignoreChar}/;
370     }
371    
372     # replaced as completely ignorable
373     $k = '[.0000.0000.0000.0000]'
374     if defined $self->{ignoreName} && $name =~ /$self->{ignoreName}/;
375    
376     my $is_L3_ignorable = TRUE;
377    
378     foreach my $arr ($k =~ /\[([^\[\]]+)\]/g) { # SPACEs allowed
379     my $var = $arr =~ /\*/; # exactly /^\*/ but be lenient.
380     my @wt = _getHexArray($arr);
381     push @key, pack(VCE_TEMPLATE, $var, @wt);
382     $is_L3_ignorable = FALSE
383     if $wt[0] + $wt[1] + $wt[2] != 0;
384     # if $arr !~ /[1-9A-Fa-f]/; NG
385     # Conformance Test shows L3-ignorable is completely ignorable.
386     # For expansion, an entry $is_L3_ignorable
387     # if and only if "all" CEs are [.0000.0000.0000].
388     }
389    
390     $self->{mapping}{$entry} = $is_L3_ignorable ? [] : \@key;
391    
392     if (@uv > 1) {
393     (!$self->{maxlength}{$uv[0]} || $self->{maxlength}{$uv[0]} < @uv)
394     and $self->{maxlength}{$uv[0]} = @uv;
395     }
396     }
397    
398    
399     ##
400     ## VCE = _varCE(variable term, VCE)
401     ##
402     sub _varCE
403     {
404     my $vbl = shift;
405     my $vce = shift;
406     if ($vbl eq 'non-ignorable') {
407     return $vce;
408     }
409     my ($var, @wt) = unpack VCE_TEMPLATE, $vce;
410    
411     if ($var) {
412     return pack(VCE_TEMPLATE, $var, 0, 0, 0,
413     $vbl eq 'blanked' ? $wt[3] : $wt[0]);
414     }
415     elsif ($vbl eq 'blanked') {
416     return $vce;
417     }
418     else {
419     return pack(VCE_TEMPLATE, $var, @wt[0..2],
420     $vbl eq 'shifted' && $wt[0]+$wt[1]+$wt[2] ? Shift4Wt : 0);
421     }
422     }
423    
424     sub viewSortKey
425     {
426     my $self = shift;
427     $self->visualizeSortKey($self->getSortKey(@_));
428     }
429    
430     sub visualizeSortKey
431     {
432     my $self = shift;
433     my $view = join " ", map sprintf("%04X", $_), unpack(KEY_TEMPLATE, shift);
434    
435     if ($self->{UCA_Version} <= 8) {
436     $view =~ s/ ?0000 ?/|/g;
437     } else {
438     $view =~ s/\b0000\b/|/g;
439     }
440     return "[$view]";
441     }
442    
443    
444     ##
445     ## arrayref of JCPS = splitEnt(string to be collated)
446     ## arrayref of arrayref[JCPS, ini_pos, fin_pos] = splitEnt(string, true)
447     ##
448     sub splitEnt
449     {
450     my $self = shift;
451     my $wLen = $_[1];
452    
453     my $code = $self->{preprocess};
454     my $norm = $self->{normCode};
455     my $map = $self->{mapping};
456     my $max = $self->{maxlength};
457     my $reH = $self->{rearrangeHash};
458     my $ver9 = $self->{UCA_Version} >= 9;
459    
460     my ($str, @buf);
461    
462     if ($wLen) {
463     $code and croak "Preprocess breaks character positions. "
464     . "Don't use with index(), match(), etc.";
465     $norm and croak "Normalization breaks character positions. "
466     . "Don't use with index(), match(), etc.";
467     $str = $_[0];
468     }
469     else {
470     $str = $_[0];
471     $str = &$code($str) if ref $code;
472     $str = &$norm($str) if ref $norm;
473     }
474    
475     # get array of Unicode code point of string.
476     my @src = unpack_U($str);
477    
478     # rearrangement:
479     # Character positions are not kept if rearranged,
480     # then neglected if $wLen is true.
481     if ($reH && ! $wLen) {
482     for (my $i = 0; $i < @src; $i++) {
483     if (exists $reH->{ $src[$i] } && $i + 1 < @src) {
484     ($src[$i], $src[$i+1]) = ($src[$i+1], $src[$i]);
485     $i++;
486     }
487     }
488     }
489    
490     # To remove a character marked as a completely ignorable.
491     for (my $i = 0; $i < @src; $i++) {
492     $src[$i] = undef
493     if _isIllegal($src[$i]) || ($ver9 &&
494     $map->{ $src[$i] } && @{ $map->{ $src[$i] } } == 0);
495     }
496    
497     for (my $i = 0; $i < @src; $i++) {
498     my $jcps = $src[$i];
499     next if ! defined $jcps;
500     my $i_orig = $i;
501    
502     if ($max->{$jcps}) { # contract
503     my $temp_jcps = $jcps;
504     my $jcpsLen = 1;
505     my $maxLen = $max->{$jcps};
506    
507     for (my $p = $i + 1; $jcpsLen < $maxLen && $p < @src; $p++) {
508     next if ! defined $src[$p];
509     $temp_jcps .= CODE_SEP . $src[$p];
510     $jcpsLen++;
511     if ($map->{$temp_jcps}) {
512     $jcps = $temp_jcps;
513     $i = $p;
514     }
515     }
516    
517     # not-contiguous contraction with Combining Char (cf. UTS#10, S2.1).
518     # This process requires Unicode::Normalize.
519     # If "normalization" is undef, here should be skipped *always*
520     # (in spite of bool value of $CVgetCombinClass),
521     # since canonical ordering cannot be expected.
522     # Blocked combining character should not be contracted.
523    
524     if ($self->{normalization})
525     # $self->{normCode} is false in the case of "prenormalized".
526     {
527     my $preCC = 0;
528     my $curCC = 0;
529    
530     for (my $p = $i + 1; $p < @src; $p++) {
531     next if ! defined $src[$p];
532     $curCC = $CVgetCombinClass->($src[$p]);
533     last unless $curCC;
534     my $tail = CODE_SEP . $src[$p];
535     if ($preCC != $curCC && $map->{$jcps.$tail}) {
536     $jcps .= $tail;
537     $src[$p] = undef;
538     } else {
539     $preCC = $curCC;
540     }
541     }
542     }
543     }
544    
545     if ($wLen) {
546     for (; $i + 1 < @src; $i++) {
547     last if defined $src[$i + 1];
548     }
549     }
550    
551     push @buf, $wLen ? [$jcps, $i_orig, $i + 1] : $jcps;
552     }
553     return \@buf;
554     }
555    
556    
557     ##
558     ## list of VCE = getWt(JCPS)
559     ##
560     sub getWt
561     {
562     my $self = shift;
563     my $u = shift;
564     my $vbl = $self->{variable};
565     my $map = $self->{mapping};
566     my $der = $self->{derivCode};
567    
568     return if !defined $u;
569     return map(_varCE($vbl, $_), @{ $map->{$u} })
570     if $map->{$u};
571    
572     # JCPS must not be a contraction, then it's a code point.
573     if (Hangul_SIni <= $u && $u <= Hangul_SFin) {
574     my $hang = $self->{overrideHangul};
575     my @hangulCE;
576     if ($hang) {
577     @hangulCE = map(pack(VCE_TEMPLATE, NON_VAR, @$_), &$hang($u));
578     }
579     elsif (!defined $hang) {
580     @hangulCE = $der->($u);
581     }
582     else {
583     my $max = $self->{maxlength};
584     my @decH = _decompHangul($u);
585    
586     if (@decH == 2) {
587     my $contract = join(CODE_SEP, @decH);
588     @decH = ($contract) if $map->{$contract};
589     } else { # must be <@decH == 3>
590     if ($max->{$decH[0]}) {
591     my $contract = join(CODE_SEP, @decH);
592     if ($map->{$contract}) {
593     @decH = ($contract);
594     } else {
595     $contract = join(CODE_SEP, @decH[0,1]);
596     $map->{$contract} and @decH = ($contract, $decH[2]);
597     }
598     # even if V's ignorable, LT contraction is not supported.
599     # If such a situatution were required, NFD should be used.
600     }
601     if (@decH == 3 && $max->{$decH[1]}) {
602     my $contract = join(CODE_SEP, @decH[1,2]);
603     $map->{$contract} and @decH = ($decH[0], $contract);
604     }
605     }
606    
607     @hangulCE = map({
608     $map->{$_} ? @{ $map->{$_} } : $der->($_);
609     } @decH);
610     }
611     return map _varCE($vbl, $_), @hangulCE;
612     }
613     elsif (CJK_UidIni <= $u && $u <= CJK_UidFin ||
614     CJK_ExtAIni <= $u && $u <= CJK_ExtAFin ||
615     CJK_ExtBIni <= $u && $u <= CJK_ExtBFin) {
616     my $cjk = $self->{overrideCJK};
617     return map _varCE($vbl, $_),
618     $cjk
619     ? map(pack(VCE_TEMPLATE, NON_VAR, @$_), &$cjk($u))
620     : defined $cjk && $self->{UCA_Version} <= 8 && $u <= BMP_Max
621     ? pack(VCE_TEMPLATE, NON_VAR, $u, Min2Wt, Min3Wt, $u)
622     : $der->($u);
623     }
624     else {
625     return map _varCE($vbl, $_), $der->($u);
626     }
627     }
628    
629    
630     ##
631     ## string sortkey = getSortKey(string arg)
632     ##
633     sub getSortKey
634     {
635     my $self = shift;
636     my $lev = $self->{level};
637     my $rEnt = $self->splitEnt(shift); # get an arrayref of JCPS
638     my $ver9 = $self->{UCA_Version} >= 9;
639     my $v2i = $ver9 && $self->{variable} ne 'non-ignorable';
640    
641     my @buf; # weight arrays
642     if ($self->{hangul_terminator}) {
643     my $preHST = '';
644     foreach my $jcps (@$rEnt) {
645     # weird things like VL, TL-contraction are not considered!
646     my $curHST = '';
647     foreach my $u (split /;/, $jcps) {
648     $curHST .= getHST($u);
649     }
650     if ($preHST && !$curHST || # hangul before non-hangul
651     $preHST =~ /L\z/ && $curHST =~ /^T/ ||
652     $preHST =~ /V\z/ && $curHST =~ /^L/ ||
653     $preHST =~ /T\z/ && $curHST =~ /^[LV]/) {
654    
655     push @buf, $self->getWtHangulTerm();
656     }
657     $preHST = $curHST;
658    
659     push @buf, $self->getWt($jcps);
660     }
661     $preHST # end at hangul
662     and push @buf, $self->getWtHangulTerm();
663     }
664     else {
665     foreach my $jcps (@$rEnt) {
666     push @buf, $self->getWt($jcps);
667     }
668     }
669    
670     # make sort key
671     my @ret = ([],[],[],[]);
672     my $last_is_variable;
673    
674     foreach my $vwt (@buf) {
675     my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
676     if ($v2i) {
677     if ($var) {
678     $last_is_variable = TRUE;
679     }
680     elsif (!$wt[0]) { # ignorable
681     next if $last_is_variable;
682     }
683     else {
684     $last_is_variable = FALSE;
685     }
686     }
687     foreach my $v (0..$lev-1) {
688     0 < $wt[$v] and push @{ $ret[$v] }, $wt[$v];
689     }
690     }
691    
692     # modification of tertiary weights
693     if ($self->{upper_before_lower}) {
694     foreach (@{ $ret[2] }) {
695     if (0x8 <= $_ && $_ <= 0xC) { $_ -= 6 } # lower
696     elsif (0x2 <= $_ && $_ <= 0x6) { $_ += 6 } # upper
697     elsif ($_ == 0x1C) { $_ += 1 } # square upper
698     elsif ($_ == 0x1D) { $_ -= 1 } # square lower
699     }
700     }
701     if ($self->{katakana_before_hiragana}) {
702     foreach (@{ $ret[2] }) {
703     if (0x0F <= $_ && $_ <= 0x13) { $_ -= 2 } # katakana
704     elsif (0x0D <= $_ && $_ <= 0x0E) { $_ += 5 } # hiragana
705     }
706     }
707    
708     if ($self->{backwardsFlag}) {
709     for (my $v = MinLevel; $v <= MaxLevel; $v++) {
710     if ($self->{backwardsFlag} & (1 << $v)) {
711     @{ $ret[$v-1] } = reverse @{ $ret[$v-1] };
712     }
713     }
714     }
715    
716     join LEVEL_SEP, map pack(KEY_TEMPLATE, @$_), @ret;
717     }
718    
719    
720     ##
721     ## int compare = cmp(string a, string b)
722     ##
723     sub cmp { $_[0]->getSortKey($_[1]) cmp $_[0]->getSortKey($_[2]) }
724     sub eq { $_[0]->getSortKey($_[1]) eq $_[0]->getSortKey($_[2]) }
725     sub ne { $_[0]->getSortKey($_[1]) ne $_[0]->getSortKey($_[2]) }
726     sub lt { $_[0]->getSortKey($_[1]) lt $_[0]->getSortKey($_[2]) }
727     sub le { $_[0]->getSortKey($_[1]) le $_[0]->getSortKey($_[2]) }
728     sub gt { $_[0]->getSortKey($_[1]) gt $_[0]->getSortKey($_[2]) }
729     sub ge { $_[0]->getSortKey($_[1]) ge $_[0]->getSortKey($_[2]) }
730    
731     ##
732     ## list[strings] sorted = sort(list[strings] arg)
733     ##
734     sub sort {
735     my $obj = shift;
736     return
737     map { $_->[1] }
738     sort{ $a->[0] cmp $b->[0] }
739     map [ $obj->getSortKey($_), $_ ], @_;
740     }
741    
742    
743     sub _derivCE_9 {
744     my $u = shift;
745     my $base =
746     (CJK_UidIni <= $u && $u <= CJK_UidFin)
747     ? 0xFB40 : # CJK
748     (CJK_ExtAIni <= $u && $u <= CJK_ExtAFin ||
749     CJK_ExtBIni <= $u && $u <= CJK_ExtBFin)
750     ? 0xFB80 # CJK ext.
751     : 0xFBC0; # others
752    
753     my $aaaa = $base + ($u >> 15);
754     my $bbbb = ($u & 0x7FFF) | 0x8000;
755     return
756     pack(VCE_TEMPLATE, NON_VAR, $aaaa, Min2Wt, Min3Wt, $u),
757     pack(VCE_TEMPLATE, NON_VAR, $bbbb, 0, 0, $u);
758     }
759    
760     sub _derivCE_8 {
761     my $code = shift;
762     my $aaaa = 0xFF80 + ($code >> 15);
763     my $bbbb = ($code & 0x7FFF) | 0x8000;
764     return
765     pack(VCE_TEMPLATE, NON_VAR, $aaaa, 2, 1, $code),
766     pack(VCE_TEMPLATE, NON_VAR, $bbbb, 0, 0, $code);
767     }
768    
769    
770     sub getWtHangulTerm {
771     my $self = shift;
772     return _varCE($self->{variable},
773     pack(VCE_TEMPLATE, NON_VAR, $self->{hangul_terminator}, 0,0,0));
774     }
775    
776    
777     ##
778     ## "hhhh hhhh hhhh" to (dddd, dddd, dddd)
779     ##
780     sub _getHexArray { map hex, $_[0] =~ /([0-9a-fA-F]+)/g }
781    
782     #
783     # $code *must* be in Hangul syllable.
784     # Check it before you enter here.
785     #
786     sub _decompHangul {
787     my $code = shift;
788     my $SIndex = $code - Hangul_SBase;
789     my $LIndex = int( $SIndex / Hangul_NCount);
790     my $VIndex = int(($SIndex % Hangul_NCount) / Hangul_TCount);
791     my $TIndex = $SIndex % Hangul_TCount;
792     return (
793     Hangul_LBase + $LIndex,
794     Hangul_VBase + $VIndex,
795     $TIndex ? (Hangul_TBase + $TIndex) : (),
796     );
797     }
798    
799     sub _isIllegal {
800     my $code = shift;
801     return ! defined $code # removed
802     || ($code < 0 || 0x10FFFF < $code) # out of range
803     || (($code & 0xFFFE) == 0xFFFE) # ??FFF[EF] (cf. utf8.c)
804     || (0xD800 <= $code && $code <= 0xDFFF) # unpaired surrogates
805     || (0xFDD0 <= $code && $code <= 0xFDEF) # other non-characters
806     ;
807     }
808    
809     # Hangul Syllable Type
810     sub getHST {
811     my $u = shift;
812     return
813     Hangul_LIni <= $u && $u <= Hangul_LFin || $u == Hangul_LFill ? "L" :
814     Hangul_VIni <= $u && $u <= Hangul_VFin ? "V" :
815     Hangul_TIni <= $u && $u <= Hangul_TFin ? "T" :
816     Hangul_SIni <= $u && $u <= Hangul_SFin ?
817     ($u - Hangul_SBase) % Hangul_TCount ? "LVT" : "LV" : "";
818     }
819    
820    
821     ##
822     ## bool _nonIgnorAtLevel(arrayref weights, int level)
823     ##
824     sub _nonIgnorAtLevel($$)
825     {
826     my $wt = shift;
827     return if ! defined $wt;
828     my $lv = shift;
829     return grep($wt->[$_-1] != 0, MinLevel..$lv) ? TRUE : FALSE;
830     }
831    
832     ##
833     ## bool _eqArray(
834     ## arrayref of arrayref[weights] source,
835     ## arrayref of arrayref[weights] substr,
836     ## int level)
837     ## * comparison of graphemes vs graphemes.
838     ## @$source >= @$substr must be true (check it before call this);
839     ##
840     sub _eqArray($$$)
841     {
842     my $source = shift;
843     my $substr = shift;
844     my $lev = shift;
845    
846     for my $g (0..@$substr-1){
847     # Do the $g'th graphemes have the same number of AV weigths?
848     return if @{ $source->[$g] } != @{ $substr->[$g] };
849    
850     for my $w (0..@{ $substr->[$g] }-1) {
851     for my $v (0..$lev-1) {
852     return if $source->[$g][$w][$v] != $substr->[$g][$w][$v];
853     }
854     }
855     }
856     return 1;
857     }
858    
859     ##
860     ## (int position, int length)
861     ## int position = index(string, substring, position, [undoc'ed grobal])
862     ##
863     ## With "grobal" (only for the list context),
864     ## returns list of arrayref[position, length].
865     ##
866     sub index
867     {
868     my $self = shift;
869     my $str = shift;
870     my $len = length($str);
871     my $subE = $self->splitEnt(shift);
872     my $pos = @_ ? shift : 0;
873     $pos = 0 if $pos < 0;
874     my $grob = shift;
875    
876     my $lev = $self->{level};
877     my $ver9 = $self->{UCA_Version} >= 9;
878     my $v2i = $self->{variable} ne 'non-ignorable';
879    
880     if (! @$subE) {
881     my $temp = $pos <= 0 ? 0 : $len <= $pos ? $len : $pos;
882     return $grob
883     ? map([$_, 0], $temp..$len)
884     : wantarray ? ($temp,0) : $temp;
885     }
886     $len < $pos
887     and return wantarray ? () : NOMATCHPOS;
888     my $strE = $self->splitEnt($pos ? substr($str, $pos) : $str, TRUE);
889     @$strE
890     or return wantarray ? () : NOMATCHPOS;
891    
892     my(@strWt, @iniPos, @finPos, @subWt, @g_ret);
893    
894     my $last_is_variable;
895     for my $vwt (map $self->getWt($_), @$subE) {
896     my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
897     my $to_be_pushed = _nonIgnorAtLevel(\@wt,$lev);
898    
899     if ($v2i && $ver9) {
900     if ($var) {
901     $last_is_variable = TRUE;
902     }
903     elsif (!$wt[0]) { # ignorable
904     $to_be_pushed = FALSE if $last_is_variable;
905     }
906     else {
907     $last_is_variable = FALSE;
908     }
909     }
910    
911     if (@subWt && !$var && !$wt[0]) {
912     push @{ $subWt[-1] }, \@wt if $to_be_pushed;
913     } else {
914     push @subWt, [ \@wt ];
915     }
916     }
917    
918     my $count = 0;
919     my $end = @$strE - 1;
920    
921     $last_is_variable = FALSE; # reuse
922     for (my $i = 0; $i <= $end; ) { # no $i++
923     my $found_base = 0;
924    
925     # fetch a grapheme
926     while ($i <= $end && $found_base == 0) {
927     for my $vwt ($self->getWt($strE->[$i][0])) {
928     my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
929     my $to_be_pushed = _nonIgnorAtLevel(\@wt,$lev);
930    
931     if ($v2i && $ver9) {
932     if ($var) {
933     $last_is_variable = TRUE;
934     }
935     elsif (!$wt[0]) { # ignorable
936     $to_be_pushed = FALSE if $last_is_variable;
937     }
938     else {
939     $last_is_variable = FALSE;
940     }
941     }
942    
943     if (@strWt && !$var && !$wt[0]) {
944     push @{ $strWt[-1] }, \@wt if $to_be_pushed;
945     $finPos[-1] = $strE->[$i][2];
946     } elsif ($to_be_pushed) {
947     push @strWt, [ \@wt ];
948     push @iniPos, $found_base ? NOMATCHPOS : $strE->[$i][1];
949     $finPos[-1] = NOMATCHPOS if $found_base;
950     push @finPos, $strE->[$i][2];
951     $found_base++;
952     }
953     # else ===> no-op
954     }
955     $i++;
956     }
957    
958     # try to match
959     while ( @strWt > @subWt || (@strWt == @subWt && $i > $end) ) {
960     if ($iniPos[0] != NOMATCHPOS &&
961     $finPos[$#subWt] != NOMATCHPOS &&
962     _eqArray(\@strWt, \@subWt, $lev)) {
963     my $temp = $iniPos[0] + $pos;
964    
965     if ($grob) {
966     push @g_ret, [$temp, $finPos[$#subWt] - $iniPos[0]];
967     splice @strWt, 0, $#subWt;
968     splice @iniPos, 0, $#subWt;
969     splice @finPos, 0, $#subWt;
970     }
971     else {
972     return wantarray
973     ? ($temp, $finPos[$#subWt] - $iniPos[0])
974     : $temp;
975     }
976     }
977     shift @strWt;
978     shift @iniPos;
979     shift @finPos;
980     }
981     }
982    
983     return $grob
984     ? @g_ret
985     : wantarray ? () : NOMATCHPOS;
986     }
987    
988     ##
989     ## scalarref to matching part = match(string, substring)
990     ##
991     sub match
992     {
993     my $self = shift;
994     if (my($pos,$len) = $self->index($_[0], $_[1])) {
995     my $temp = substr($_[0], $pos, $len);
996     return wantarray ? $temp : \$temp;
997     # An lvalue ref \substr should be avoided,
998     # since its value is affected by modification of its referent.
999     }
1000     else {
1001     return;
1002     }
1003     }
1004    
1005     ##
1006     ## arrayref matching parts = gmatch(string, substring)
1007     ##
1008     sub gmatch
1009     {
1010     my $self = shift;
1011     my $str = shift;
1012     my $sub = shift;
1013     return map substr($str, $_->[0], $_->[1]),
1014     $self->index($str, $sub, 0, 'g');
1015     }
1016    
1017     ##
1018     ## bool subst'ed = subst(string, substring, replace)
1019     ##
1020     sub subst
1021     {
1022     my $self = shift;
1023     my $code = ref $_[2] eq 'CODE' ? $_[2] : FALSE;
1024    
1025     if (my($pos,$len) = $self->index($_[0], $_[1])) {
1026     if ($code) {
1027     my $mat = substr($_[0], $pos, $len);
1028     substr($_[0], $pos, $len, $code->($mat));
1029     } else {
1030     substr($_[0], $pos, $len, $_[2]);
1031     }
1032     return TRUE;
1033     }
1034     else {
1035     return FALSE;
1036     }
1037     }
1038    
1039     ##
1040     ## int count = gsubst(string, substring, replace)
1041     ##
1042     sub gsubst
1043     {
1044     my $self = shift;
1045     my $code = ref $_[2] eq 'CODE' ? $_[2] : FALSE;
1046     my $cnt = 0;
1047    
1048     # Replacement is carried out from the end, then use reverse.
1049     for my $pos_len (reverse $self->index($_[0], $_[1], 0, 'g')) {
1050     if ($code) {
1051     my $mat = substr($_[0], $pos_len->[0], $pos_len->[1]);
1052     substr($_[0], $pos_len->[0], $pos_len->[1], $code->($mat));
1053     } else {
1054     substr($_[0], $pos_len->[0], $pos_len->[1], $_[2]);
1055     }
1056     $cnt++;
1057     }
1058     return $cnt;
1059     }
1060    
1061     1;
1062     __END__
1063    
1064     =head1 NAME
1065    
1066     Unicode::Collate - Unicode Collation Algorithm
1067    
1068     =head1 SYNOPSIS
1069    
1070     use Unicode::Collate;
1071    
1072     #construct
1073     $Collator = Unicode::Collate->new(%tailoring);
1074    
1075     #sort
1076     @sorted = $Collator->sort(@not_sorted);
1077    
1078     #compare
1079     $result = $Collator->cmp($a, $b); # returns 1, 0, or -1.
1080    
1081     # If %tailoring is false (i.e. empty),
1082     # $Collator should do the default collation.
1083    
1084     =head1 DESCRIPTION
1085    
1086     This module is an implementation
1087     of Unicode Technical Standard #10 (UTS #10)
1088     "Unicode Collation Algorithm."
1089    
1090     =head2 Constructor and Tailoring
1091    
1092     The C<new> method returns a collator object.
1093    
1094     $Collator = Unicode::Collate->new(
1095     UCA_Version => $UCA_Version,
1096     alternate => $alternate, # deprecated: use of 'variable' is recommended.
1097     backwards => $levelNumber, # or \@levelNumbers
1098     entry => $element,
1099     hangul_terminator => $term_primary_weight,
1100     ignoreName => qr/$ignoreName/,
1101     ignoreChar => qr/$ignoreChar/,
1102     katakana_before_hiragana => $bool,
1103     level => $collationLevel,
1104     normalization => $normalization_form,
1105     overrideCJK => \&overrideCJK,
1106     overrideHangul => \&overrideHangul,
1107     preprocess => \&preprocess,
1108     rearrange => \@charList,
1109     table => $filename,
1110     undefName => qr/$undefName/,
1111     undefChar => qr/$undefChar/,
1112     upper_before_lower => $bool,
1113     variable => $variable,
1114     );
1115    
1116     =over 4
1117    
1118     =item UCA_Version
1119    
1120     If the tracking version number of the older UCA is given,
1121     the older behavior of that tracking version is emulated on collating.
1122     If omitted, the return value of C<UCA_Version()> is used.
1123    
1124     The supported tracking version: 8, 9, or 11.
1125    
1126     B<This parameter may be removed in the future version,
1127     as switching the algorithm would affect the performance.>
1128    
1129     =item backwards
1130    
1131     -- see 3.1.2 French Accents, UTS #10.
1132    
1133     backwards => $levelNumber or \@levelNumbers
1134    
1135     Weights in reverse order; ex. level 2 (diacritic ordering) in French.
1136     If omitted, forwards at all the levels.
1137    
1138     =item entry
1139    
1140     -- see 3.1 Linguistic Features; 3.2.1 File Format, UTS #10.
1141    
1142     If the same character (or a sequence of characters) exists
1143     in the collation element table through C<table>,
1144     mapping to collation elements is overrided.
1145     If it does not exist, the mapping is defined additionally.
1146    
1147     entry => <<'ENTRY', # for DUCET v4.0.0 (allkeys-4.0.0.txt)
1148     0063 0068 ; [.0E6A.0020.0002.0063] # ch
1149     0043 0068 ; [.0E6A.0020.0007.0043] # Ch
1150     0043 0048 ; [.0E6A.0020.0008.0043] # CH
1151     006C 006C ; [.0F4C.0020.0002.006C] # ll
1152     004C 006C ; [.0F4C.0020.0007.004C] # Ll
1153     004C 004C ; [.0F4C.0020.0008.004C] # LL
1154     00F1 ; [.0F7B.0020.0002.00F1] # n-tilde
1155     006E 0303 ; [.0F7B.0020.0002.00F1] # n-tilde
1156     00D1 ; [.0F7B.0020.0008.00D1] # N-tilde
1157     004E 0303 ; [.0F7B.0020.0008.00D1] # N-tilde
1158     ENTRY
1159    
1160     entry => <<'ENTRY', # for DUCET v4.0.0 (allkeys-4.0.0.txt)
1161     00E6 ; [.0E33.0020.0002.00E6][.0E8B.0020.0002.00E6] # ae ligature as <a><e>
1162     00C6 ; [.0E33.0020.0008.00C6][.0E8B.0020.0008.00C6] # AE ligature as <A><E>
1163     ENTRY
1164    
1165     B<NOTE:> The code point in the UCA file format (before C<';'>)
1166     B<must> be a Unicode code point (defined as hexadecimal),
1167     but not a native code point.
1168     So C<0063> must always denote C<U+0063>,
1169     but not a character of C<"\x63">.
1170    
1171     Weighting may vary depending on collation element table.
1172     So ensure the weights defined in C<entry> will be consistent with
1173     those in the collation element table loaded via C<table>.
1174    
1175     In DUCET v4.0.0, primary weight of C<C> is C<0E60>
1176     and that of C<D> is C<0E6D>. So setting primary weight of C<CH> to C<0E6A>
1177     (as a value between C<0E60> and C<0E6D>)
1178     makes ordering as C<C E<lt> CH E<lt> D>.
1179     Exactly speaking DUCET already has some characters between C<C> and C<D>:
1180     C<small capital C> (C<U+1D04>) with primary weight C<0E64>,
1181     C<c-hook/C-hook> (C<U+0188/U+0187>) with C<0E65>,
1182     and C<c-curl> (C<U+0255>) with C<0E69>.
1183     Then primary weight C<0E6A> for C<CH> makes C<CH>
1184     ordered between C<c-curl> and C<D>.
1185    
1186     =item hangul_terminator
1187    
1188     -- see Condition B.2. in 7.1.4 Trailing Weights, UTS #10.
1189    
1190     If a true value is given (non-zero but should be positive),
1191     it will be added as a terminator primary weight to the end of
1192     every standard Hangul syllable. Secondary and any higher weights
1193     for terminator are set to zero.
1194     If the value is false or C<hangul_terminator> key does not exist,
1195     insertion of terminator weights will not be performed.
1196    
1197     Boundaries of Hangul syllables are determined
1198     according to conjoining Jamo behavior in F<the Unicode Standard>
1199     and F<HangulSyllableType.txt>.
1200    
1201     B<Implementation Note:>
1202     (1) For expansion mapping (Unicode character mapped
1203     to a sequence of collation elements), a terminator will not be added
1204     between collation elements, even if Hangul syllable boundary exists there.
1205     Addition of terminator is restricted to the next position
1206     to the last collation element.
1207    
1208     (2) Non-conjoining Hangul letters
1209     (Compatibility Jamo, halfwidth Jamo, and enclosed letters) are not
1210     automatically terminated with a terminator primary weight.
1211     These characters may need terminator included in a collation element
1212     table beforehand.
1213    
1214     =item ignoreName
1215    
1216     =item ignoreChar
1217    
1218     -- see Completely Ignorable, 3.2.2 Variable Weighting, UTS #10.
1219    
1220     Makes the entry in the table completely ignorable;
1221     i.e. as if the weights were zero at all level.
1222    
1223     E.g. when 'a' and 'e' are ignorable,
1224     'element' is equal to 'lament' (or 'lmnt').
1225    
1226     =item level
1227    
1228     -- see 4.3 Form a sort key for each string, UTS #10.
1229    
1230     Set the maximum level.
1231     Any higher levels than the specified one are ignored.
1232    
1233     Level 1: alphabetic ordering
1234     Level 2: diacritic ordering
1235     Level 3: case ordering
1236     Level 4: tie-breaking (e.g. in the case when variable is 'shifted')
1237    
1238     ex.level => 2,
1239    
1240     If omitted, the maximum is the 4th.
1241    
1242     =item normalization
1243    
1244     -- see 4.1 Normalize each input string, UTS #10.
1245    
1246     If specified, strings are normalized before preparation of sort keys
1247     (the normalization is executed after preprocess).
1248    
1249     A form name C<Unicode::Normalize::normalize()> accepts will be applied
1250     as C<$normalization_form>.
1251     Acceptable names include C<'NFD'>, C<'NFC'>, C<'NFKD'>, and C<'NFKC'>.
1252     See C<Unicode::Normalize::normalize()> for detail.
1253     If omitted, C<'NFD'> is used.
1254    
1255     C<normalization> is performed after C<preprocess> (if defined).
1256    
1257     Furthermore, special values, C<undef> and C<"prenormalized">, can be used,
1258     though they are not concerned with C<Unicode::Normalize::normalize()>.
1259    
1260     If C<undef> (not a string C<"undef">) is passed explicitly
1261     as the value for this key,
1262     any normalization is not carried out (this may make tailoring easier
1263     if any normalization is not desired). Under C<(normalization =E<gt> undef)>,
1264     only contiguous contractions are resolved;
1265     e.g. even if C<A-ring> (and C<A-ring-cedilla>) is ordered after C<Z>,
1266     C<A-cedilla-ring> would be primary equal to C<A>.
1267     In this point,
1268     C<(normalization =E<gt> undef, preprocess =E<gt> sub { NFD(shift) })>
1269     B<is not> equivalent to C<(normalization =E<gt> 'NFD')>.
1270    
1271     In the case of C<(normalization =E<gt> "prenormalized")>,
1272     any normalization is not performed, but
1273     non-contiguous contractions with combining characters are performed.
1274     Therefore
1275     C<(normalization =E<gt> 'prenormalized', preprocess =E<gt> sub { NFD(shift) })>
1276     B<is> equivalent to C<(normalization =E<gt> 'NFD')>.
1277     If source strings are finely prenormalized,
1278     C<(normalization =E<gt> 'prenormalized')> may save time for normalization.
1279    
1280     Except C<(normalization =E<gt> undef)>,
1281     B<Unicode::Normalize> is required (see also B<CAVEAT>).
1282    
1283     =item overrideCJK
1284    
1285     -- see 7.1 Derived Collation Elements, UTS #10.
1286    
1287     By default, CJK Unified Ideographs are ordered in Unicode codepoint order
1288     (but C<CJK Unified Ideographs> [C<U+4E00> to C<U+9FA5>] are lesser than
1289     C<CJK Unified Ideographs Extension> [C<U+3400> to C<U+4DB5> and
1290     C<U+20000> to C<U+2A6D6>].
1291    
1292     Through C<overrideCJK>, ordering of CJK Unified Ideographs can be overrided.
1293    
1294     ex. CJK Unified Ideographs in the JIS code point order.
1295    
1296     overrideCJK => sub {
1297     my $u = shift; # get a Unicode codepoint
1298     my $b = pack('n', $u); # to UTF-16BE
1299     my $s = your_unicode_to_sjis_converter($b); # convert
1300     my $n = unpack('n', $s); # convert sjis to short
1301     [ $n, 0x20, 0x2, $u ]; # return the collation element
1302     },
1303    
1304     ex. ignores all CJK Unified Ideographs.
1305    
1306     overrideCJK => sub {()}, # CODEREF returning empty list
1307    
1308     # where ->eq("Pe\x{4E00}rl", "Perl") is true
1309     # as U+4E00 is a CJK Unified Ideograph and to be ignorable.
1310    
1311     If C<undef> is passed explicitly as the value for this key,
1312     weights for CJK Unified Ideographs are treated as undefined.
1313     But assignment of weight for CJK Unified Ideographs
1314     in table or C<entry> is still valid.
1315    
1316     =item overrideHangul
1317    
1318     -- see 7.1 Derived Collation Elements, UTS #10.
1319    
1320     By default, Hangul Syllables are decomposed into Hangul Jamo,
1321     even if C<(normalization =E<gt> undef)>.
1322     But the mapping of Hangul Syllables may be overrided.
1323    
1324     This tag works like C<overrideCJK>, so see there for examples.
1325    
1326     If you want to override the mapping of Hangul Syllables,
1327     NFD, NFKD, and FCD are not appropriate,
1328     since they will decompose Hangul Syllables before overriding.
1329    
1330     If C<undef> is passed explicitly as the value for this key,
1331     weight for Hangul Syllables is treated as undefined
1332     without decomposition into Hangul Jamo.
1333     But definition of weight for Hangul Syllables
1334     in table or C<entry> is still valid.
1335    
1336     =item preprocess
1337    
1338     -- see 5.1 Preprocessing, UTS #10.
1339    
1340     If specified, the coderef is used to preprocess
1341     before the formation of sort keys.
1342    
1343     ex. dropping English articles, such as "a" or "the".
1344     Then, "the pen" is before "a pencil".
1345    
1346     preprocess => sub {
1347     my $str = shift;
1348     $str =~ s/\b(?:an?|the)\s+//gi;
1349     return $str;
1350     },
1351    
1352     C<preprocess> is performed before C<normalization> (if defined).
1353    
1354     =item rearrange
1355    
1356     -- see 3.1.3 Rearrangement, UTS #10.
1357    
1358     Characters that are not coded in logical order and to be rearranged.
1359     By default,
1360    
1361     rearrange => [ 0x0E40..0x0E44, 0x0EC0..0x0EC4 ],
1362    
1363     If you want to disallow any rearrangement,
1364     pass C<undef> or C<[]> (a reference to an empty list)
1365     as the value for this key.
1366    
1367     B<According to the version 9 of UCA, this parameter shall not be used;
1368     but it is not warned at present.>
1369    
1370     =item table
1371    
1372     -- see 3.2 Default Unicode Collation Element Table, UTS #10.
1373    
1374     You can use another collation element table if desired.
1375    
1376     The table file should locate in the F<Unicode/Collate> directory
1377     on C<@INC>. Say, if the filename is F<Foo.txt>
1378     the table file is searched as F<Unicode/Collate/Foo.txt> in <@INC>.
1379    
1380     By default, F<allkeys.txt> (as the filename of DUCET) is used.
1381    
1382     If C<undef> is passed explicitly as the value for this key,
1383     no file is read (but you can define collation elements via C<entry>).
1384    
1385     A typical way to define a collation element table
1386     without any file of table:
1387    
1388     $onlyABC = Unicode::Collate->new(
1389     table => undef,
1390     entry => << 'ENTRIES',
1391     0061 ; [.0101.0020.0002.0061] # LATIN SMALL LETTER A
1392     0041 ; [.0101.0020.0008.0041] # LATIN CAPITAL LETTER A
1393     0062 ; [.0102.0020.0002.0062] # LATIN SMALL LETTER B
1394     0042 ; [.0102.0020.0008.0042] # LATIN CAPITAL LETTER B
1395     0063 ; [.0103.0020.0002.0063] # LATIN SMALL LETTER C
1396     0043 ; [.0103.0020.0008.0043] # LATIN CAPITAL LETTER C
1397     ENTRIES
1398     );
1399    
1400     =item undefName
1401    
1402     =item undefChar
1403    
1404     -- see 6.3.4 Reducing the Repertoire, UTS #10.
1405    
1406     Undefines the collation element as if it were unassigned in the table.
1407     This reduces the size of the table.
1408     If an unassigned character appears in the string to be collated,
1409     the sort key is made from its codepoint
1410     as a single-character collation element,
1411     as it is greater than any other assigned collation elements
1412     (in the codepoint order among the unassigned characters).
1413     But, it'd be better to ignore characters
1414     unfamiliar to you and maybe never used.
1415    
1416     ex. Collation weights for beyond-BMP characters are not stored in object:
1417    
1418     undefChar => qr/[^\0-\x{fffd}]/,
1419    
1420     =item katakana_before_hiragana
1421    
1422     =item upper_before_lower
1423    
1424     -- see 6.6 Case Comparisons; 7.3.1 Tertiary Weight Table, UTS #10.
1425    
1426     By default, lowercase is before uppercase
1427     and hiragana is before katakana.
1428    
1429     If the tag is made true, this is reversed.
1430    
1431     B<NOTE>: These tags simplemindedly assume
1432     any lowercase/uppercase or hiragana/katakana distinctions
1433     must occur in level 3, and their weights at level 3
1434     must be same as those mentioned in 7.3.1, UTS #10.
1435     If you define your collation elements which violate this requirement,
1436     these tags don't work validly.
1437    
1438     =item variable
1439    
1440     =item alternate
1441    
1442     -- see 3.2.2 Variable Weighting, UTS #10.
1443    
1444     (the title in UCA version 8: Alternate Weighting)
1445    
1446     This key allows to variable weighting for variable collation elements,
1447     which are marked with an ASTERISK in the table
1448     (NOTE: Many punction marks and symbols are variable in F<allkeys.txt>).
1449    
1450     variable => 'blanked', 'non-ignorable', 'shifted', or 'shift-trimmed'.
1451    
1452     These names are case-insensitive.
1453     By default (if specification is omitted), 'shifted' is adopted.
1454    
1455     'Blanked' Variable elements are made ignorable at levels 1 through 3;
1456     considered at the 4th level.
1457    
1458     'Non-Ignorable' Variable elements are not reset to ignorable.
1459    
1460     'Shifted' Variable elements are made ignorable at levels 1 through 3
1461     their level 4 weight is replaced by the old level 1 weight.
1462     Level 4 weight for Non-Variable elements is 0xFFFF.
1463    
1464     'Shift-Trimmed' Same as 'shifted', but all FFFF's at the 4th level
1465     are trimmed.
1466    
1467     For backward compatibility, C<alternate> can be used as an alias
1468     for C<variable>.
1469    
1470     =back
1471    
1472     =head2 Methods for Collation
1473    
1474     =over 4
1475    
1476     =item C<@sorted = $Collator-E<gt>sort(@not_sorted)>
1477    
1478     Sorts a list of strings.
1479    
1480     =item C<$result = $Collator-E<gt>cmp($a, $b)>
1481    
1482     Returns 1 (when C<$a> is greater than C<$b>)
1483     or 0 (when C<$a> is equal to C<$b>)
1484     or -1 (when C<$a> is lesser than C<$b>).
1485    
1486     =item C<$result = $Collator-E<gt>eq($a, $b)>
1487    
1488     =item C<$result = $Collator-E<gt>ne($a, $b)>
1489    
1490     =item C<$result = $Collator-E<gt>lt($a, $b)>
1491    
1492     =item C<$result = $Collator-E<gt>le($a, $b)>
1493    
1494     =item C<$result = $Collator-E<gt>gt($a, $b)>
1495    
1496     =item C<$result = $Collator-E<gt>ge($a, $b)>
1497    
1498     They works like the same name operators as theirs.
1499    
1500     eq : whether $a is equal to $b.
1501     ne : whether $a is not equal to $b.
1502     lt : whether $a is lesser than $b.
1503     le : whether $a is lesser than $b or equal to $b.
1504     gt : whether $a is greater than $b.
1505     ge : whether $a is greater than $b or equal to $b.
1506    
1507     =item C<$sortKey = $Collator-E<gt>getSortKey($string)>
1508    
1509     -- see 4.3 Form a sort key for each string, UTS #10.
1510    
1511     Returns a sort key.
1512    
1513     You compare the sort keys using a binary comparison
1514     and get the result of the comparison of the strings using UCA.
1515    
1516     $Collator->getSortKey($a) cmp $Collator->getSortKey($b)
1517    
1518     is equivalent to
1519    
1520     $Collator->cmp($a, $b)
1521    
1522     =item C<$sortKeyForm = $Collator-E<gt>viewSortKey($string)>
1523    
1524     use Unicode::Collate;
1525     my $c = Unicode::Collate->new();
1526     print $c->viewSortKey("Perl"),"\n";
1527    
1528     # output:
1529     # [0B67 0A65 0B7F 0B03 | 0020 0020 0020 0020 | 0008 0002 0002 0002 | FFFF FFFF FFFF FFFF]
1530     # Level 1 Level 2 Level 3 Level 4
1531    
1532     (If C<UCA_Version> is 8, the output is slightly different.)
1533    
1534     =back
1535    
1536     =head2 Methods for Searching
1537    
1538     B<DISCLAIMER:> If C<preprocess> or C<normalization> tag is true
1539     for C<$Collator>, calling these methods (C<index>, C<match>, C<gmatch>,
1540     C<subst>, C<gsubst>) is croaked,
1541     as the position and the length might differ
1542     from those on the specified string.
1543     (And C<rearrange> and C<hangul_terminator> tags are neglected.)
1544    
1545     The C<match>, C<gmatch>, C<subst>, C<gsubst> methods work
1546     like C<m//>, C<m//g>, C<s///>, C<s///g>, respectively,
1547     but they are not aware of any pattern, but only a literal substring.
1548    
1549     =over 4
1550    
1551     =item C<$position = $Collator-E<gt>index($string, $substring[, $position])>
1552    
1553     =item C<($position, $length) = $Collator-E<gt>index($string, $substring[, $position])>
1554    
1555     If C<$substring> matches a part of C<$string>, returns
1556     the position of the first occurrence of the matching part in scalar context;
1557     in list context, returns a two-element list of
1558     the position and the length of the matching part.
1559    
1560     If C<$substring> does not match any part of C<$string>,
1561     returns C<-1> in scalar context and
1562     an empty list in list context.
1563    
1564     e.g. you say
1565    
1566     my $Collator = Unicode::Collate->new( normalization => undef, level => 1 );
1567     # (normalization => undef) is REQUIRED.
1568     my $str = "Ich muß studieren Perl.";
1569     my $sub = "MÜSS";
1570     my $match;
1571     if (my($pos,$len) = $Collator->index($str, $sub)) {
1572     $match = substr($str, $pos, $len);
1573     }
1574    
1575     and get C<"muß"> in C<$match> since C<"muß">
1576     is primary equal to C<"MÜSS">.
1577    
1578     =item C<$match_ref = $Collator-E<gt>match($string, $substring)>
1579    
1580     =item C<($match) = $Collator-E<gt>match($string, $substring)>
1581    
1582     If C<$substring> matches a part of C<$string>, in scalar context, returns
1583     B<a reference to> the first occurrence of the matching part
1584     (C<$match_ref> is always true if matches,
1585     since every reference is B<true>);
1586     in list context, returns the first occurrence of the matching part.
1587    
1588     If C<$substring> does not match any part of C<$string>,
1589     returns C<undef> in scalar context and
1590     an empty list in list context.
1591    
1592     e.g.
1593    
1594     if ($match_ref = $Collator->match($str, $sub)) { # scalar context
1595     print "matches [$$match_ref].\n";
1596     } else {
1597     print "doesn't match.\n";
1598     }
1599    
1600     or
1601    
1602     if (($match) = $Collator->match($str, $sub)) { # list context
1603     print "matches [$match].\n";
1604     } else {
1605     print "doesn't match.\n";
1606     }
1607    
1608     =item C<@match = $Collator-E<gt>gmatch($string, $substring)>
1609    
1610     If C<$substring> matches a part of C<$string>, returns
1611     all the matching parts (or matching count in scalar context).
1612    
1613     If C<$substring> does not match any part of C<$string>,
1614     returns an empty list.
1615    
1616     =item C<$count = $Collator-E<gt>subst($string, $substring, $replacement)>
1617    
1618     If C<$substring> matches a part of C<$string>,
1619     the first occurrence of the matching part is replaced by C<$replacement>
1620     (C<$string> is modified) and return C<$count> (always equals to C<1>).
1621    
1622     C<$replacement> can be a C<CODEREF>,
1623     taking the matching part as an argument,
1624     and returning a string to replace the matching part
1625     (a bit similar to C<s/(..)/$coderef-E<gt>($1)/e>).
1626    
1627     =item C<$count = $Collator-E<gt>gsubst($string, $substring, $replacement)>
1628    
1629     If C<$substring> matches a part of C<$string>,
1630     all the occurrences of the matching part is replaced by C<$replacement>
1631     (C<$string> is modified) and return C<$count>.
1632    
1633     C<$replacement> can be a C<CODEREF>,
1634     taking the matching part as an argument,
1635     and returning a string to replace the matching part
1636     (a bit similar to C<s/(..)/$coderef-E<gt>($1)/eg>).
1637    
1638     e.g.
1639    
1640     my $Collator = Unicode::Collate->new( normalization => undef, level => 1 );
1641     # (normalization => undef) is REQUIRED.
1642     my $str = "Camel ass came\x{301}l CAMEL horse cAm\0E\0L...";
1643     $Collator->gsubst($str, "camel", sub { "<b>$_[0]</b>" });
1644    
1645     # now $str is "<b>Camel</b> ass <b>came\x{301}l</b> <b>CAMEL</b> horse <b>cAm\0E\0L</b>...";
1646     # i.e., all the camels are made bold-faced.
1647    
1648     =back
1649    
1650     =head2 Other Methods
1651    
1652     =over 4
1653    
1654     =item C<%old_tailoring = $Collator-E<gt>change(%new_tailoring)>
1655    
1656     Change the value of specified keys and returns the changed part.
1657    
1658     $Collator = Unicode::Collate->new(level => 4);
1659    
1660     $Collator->eq("perl", "PERL"); # false
1661    
1662     %old = $Collator->change(level => 2); # returns (level => 4).
1663    
1664     $Collator->eq("perl", "PERL"); # true
1665    
1666     $Collator->change(%old); # returns (level => 2).
1667    
1668     $Collator->eq("perl", "PERL"); # false
1669    
1670     Not all C<(key,value)>s are allowed to be changed.
1671     See also C<@Unicode::Collate::ChangeOK> and C<@Unicode::Collate::ChangeNG>.
1672    
1673     In the scalar context, returns the modified collator
1674     (but it is B<not> a clone from the original).
1675    
1676     $Collator->change(level => 2)->eq("perl", "PERL"); # true
1677    
1678     $Collator->eq("perl", "PERL"); # true; now max level is 2nd.
1679    
1680     $Collator->change(level => 4)->eq("perl", "PERL"); # false
1681    
1682     =item C<$version = $Collator-E<gt>version()>
1683    
1684     Returns the version number (a string) of the Unicode Standard
1685     which the C<table> file used by the collator object is based on.
1686     If the table does not include a version line (starting with C<@version>),
1687     returns C<"unknown">.
1688    
1689     =item C<UCA_Version()>
1690    
1691     Returns the tracking version number of UTS #10 this module consults.
1692    
1693     =item C<Base_Unicode_Version()>
1694    
1695     Returns the version number of UTS #10 this module consults.
1696    
1697     =back
1698    
1699     =head2 EXPORT
1700    
1701     None by default.
1702    
1703     =head2 CAVEAT
1704    
1705     Use of the C<normalization> parameter requires
1706     the B<Unicode::Normalize> module.
1707    
1708     If you need not it (say, in the case when you need not
1709     handle any combining characters),
1710     assign C<normalization =E<gt> undef> explicitly.
1711    
1712     -- see 6.5 Avoiding Normalization, UTS #10.
1713    
1714     =head2 Conformance Test
1715    
1716     The Conformance Test for the UCA is available
1717     under L<http://www.unicode.org/Public/UCA/>.
1718    
1719     For F<CollationTest_SHIFTED.txt>,
1720     a collator via C<Unicode::Collate-E<gt>new( )> should be used;
1721     for F<CollationTest_NON_IGNORABLE.txt>, a collator via
1722     C<Unicode::Collate-E<gt>new(variable =E<gt> "non-ignorable", level =E<gt> 3)>.
1723    
1724     B<Unicode::Normalize is required to try The Conformance Test.>
1725    
1726     =head1 AUTHOR
1727    
1728     SADAHIRO Tomoyuki <SADAHIRO@cpan.org>
1729    
1730     http://homepage1.nifty.com/nomenclator/perl/
1731    
1732     Copyright(C) 2001-2004, SADAHIRO Tomoyuki. Japan. All rights reserved.
1733    
1734     This library is free software; you can redistribute it
1735     and/or modify it under the same terms as Perl itself.
1736    
1737     =head1 SEE ALSO
1738    
1739     =over 4
1740    
1741     =item Unicode Collation Algorithm - UTS #10
1742    
1743     L<http://www.unicode.org/reports/tr10/>
1744    
1745     =item The Default Unicode Collation Element Table (DUCET)
1746    
1747     L<http://www.unicode.org/Public/UCA/latest/allkeys.txt>
1748    
1749     =item The conformance test for the UCA
1750    
1751     L<http://www.unicode.org/Public/UCA/latest/CollationTest.html>
1752    
1753     L<http://www.unicode.org/Public/UCA/latest/CollationTest.zip>
1754    
1755     =item Hangul Syllable Type
1756    
1757     L<http://www.unicode.org/Public/UNIDATA/HangulSyllableType.txt>
1758    
1759     =item Unicode Normalization Forms - UAX #15
1760    
1761     L<http://www.unicode.org/reports/tr15/>
1762    
1763     =item L<Unicode::Normalize>
1764    
1765     =back
1766    
1767     =cut