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