/usr/local/CPAN/Sort-DataTypes/Sort/DataTypes.pm
package Sort::DataTypes;
# Copyright (c) 2007-2011 Sullivan Beck. All rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
###############################################################################
$VERSION = "3.01";
require 5.000;
require Exporter;
use Storable qw(dclone);
use warnings;
our %methods = ('alphabetic' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'numerical' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'alphanum' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'random' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'version' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'date' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'ip' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'nosort' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
},
'function' => { 'type' => 'unambiguous',
'args' => [],
'reverse' => 0,
'args' => [ { 'type' => 'function',
},
{ 'type' => 'string',
},
],
},
'length' => { 'type' => 'ambiguous',
'args' => [],
'alt' => 'alphabetic',
'altargs' => [],
'reverse' => 0,
},
'split' => { 'type' => 'split',
'args' => [ { 'type' => 'member',
'values' => [ 'lms', 'rms' ],
'default' => 'lms',
},
{
'type' => 'regexp',
'default' => '\s+',
}
],
'alt' => 'alphabetic',
'altargs' => [],
'reverse' => 0,
},
'domain' => { 'type' => 'wrapper' },
'numdomain' => { 'type' => 'wrapper' },
'path' => { 'type' => 'wrapper' },
'numpath' => { 'type' => 'wrapper' },
'partial' => { 'type' => 'partial',
'args' => [ {
'type' => 'regexp',
'default' => '\s+',
}
],
'alt' => 'alphabetic',
'altargs' => [],
'reverse' => 0,
},
'line' => { 'type' => 'wrapper' },
'numline' => { 'type' => 'wrapper' },
);
my @all_methods = map { ("sort_$_", "sort_rev_$_", "cmp_$_", "cmp_rev_$_") } keys %methods;
@ISA = qw(Exporter);
@EXPORT_OK = (
qw(sort_valid_method
sort_by_method
cmp_valid_method
cmp_by_method
),
@all_methods);
%EXPORT_TAGS = (all => \@EXPORT_OK);
foreach my $meth (keys %methods) {
$methods{$meth}{'function'} = $meth;
$methods{"rev_$meth"} = dclone($methods{$meth});
$methods{"rev_$meth"}{'reverse'} = 1;
}
use strict;
###############################################################################
###############################################################################
sub sort_valid_method {
my($method) = @_;
return (exists $methods{$method} ? 1 : 0);
}
sub cmp_valid_method {
my($method) = @_;
return (exists $methods{$method} ? 1 : 0);
}
sub sort_by_method {
my($method,$list,@args) = @_;
return if (! sort_valid_method($method));
no strict 'refs';
my $func = "sort_$method";
return &$func($list,@args);
}
sub cmp_by_method {
my($method,$list,@args) = @_;
return if (! cmp_valid_method($method));
no strict 'refs';
my $func = "cmp_$method";
return &$func($list,@args);
}
###############################################################################
# UNAMBIGUOUS METHODS
###############################################################################
sub sort_numerical {
return _sort('','numerical',@_);
}
sub cmp_numerical {
return _cmp('','numerical',@_);
}
sub sort_rev_numerical {
return _sort('rev','numerical',@_);
}
sub cmp_rev_numerical {
return _cmp('rev','numerical',@_);
}
sub _numerical {
my($ele) = @_;
return 0 if (! defined($ele) || ref($ele));
return 1 if ($ele =~ /^[+-]?\d+\.?\d*$/ ||
$ele =~ /^[+-]?\.\d+$/);
return 0;
}
sub _cmp_numerical {
my($x,$y) = @_;
return ($x <=> $y);
}
###############################################################################
sub sort_alphabetic {
return _sort('','alphabetic',@_);
}
sub cmp_alphabetic {
return _cmp('','alphabetic',@_);
}
sub sort_rev_alphabetic {
return _sort('rev','alphabetic',@_);
}
sub cmp_rev_alphabetic {
return _cmp('rev','alphabetic',@_);
}
sub _alphabetic {
my($ele) = @_;
return 1 if (! ref($ele));
return 0;
}
sub _cmp_alphabetic {
my($x,$y) = @_;
return ($x cmp $y);
}
###############################################################################
sub sort_alphanum {
return _sort('','alphanum',@_);
}
sub cmp_alphanum {
return _cmp('','alphanum',@_);
}
sub sort_rev_alphanum {
return _sort('rev','alphanum',@_);
}
sub cmp_rev_alphanum {
return _cmp('rev','alphanum',@_);
}
sub _alphanum {
my($ele) = @_;
return 1 if (! ref($ele));
return 0;
}
sub _cmp_alphanum {
my($x,$y) = @_;
if (_numerical($x) && _numerical($y)) {
return ($x <=> $y);
} else {
return ($x cmp $y);
}
}
###############################################################################
{
my $randomized = 0;
sub _randomize {
$randomized = 1;
srand(time);
}
sub sort_random {
_randomize() if (! $randomized);
return _sort('','random',@_);
}
sub cmp_random {
_randomize() if (! $randomized);
return _cmp('','random',@_);
}
sub sort_rev_random {
_randomize() if (! $randomized);
return _sort('','random',@_);
}
sub cmp_rev_random {
_randomize() if (! $randomized);
return _cmp('','random',@_);
}
sub _random {
my($ele) = @_;
return 1;
}
sub _cmp_random {
my($x,$y) = @_;
return int(rand(3)) - 1;
}
}
###############################################################################
sub sort_version {
return _sort('','version',@_);
}
sub cmp_version {
return _cmp('','version',@_);
}
sub sort_rev_version {
return _sort('rev','version',@_);
}
sub cmp_rev_version {
return _cmp('rev','version',@_);
}
sub _version {
my($ele) = @_;
return 1 if (! ref($ele));
return 0;
}
sub _cmp_version {
my($x,$y) = @_;
my(@x,@y);
(@x)=split(/\./,$x);
(@y)=split(/\./,$y);
while (@x) {
return 1 if (! @y);
my $xx=shift(@x);
my $yy=shift(@y);
if ($xx =~ /^(\d+)(.*)$/) {
my($xv,$xs) = ($1+0,$2);
if ($yy =~ /^(\d+)(.*)$/) {
my($yv,$ys) = ($1+0,$2);
my $ret = ($xv <=> $yv);
return $ret if ($ret);
return -1 if ($xs && ! $ys);
return 1 if ($ys && ! $xs);
$ret = ($xx cmp $yy);
return $ret if ($ret);
} else {
return 1;
}
} elsif ($yy =~ /^(\d+)(.*)$/) {
return -1;
} elsif ($xx || $yy) {
my $ret=($xx cmp $yy);
return $ret if ($ret);
}
}
return -1 if (@y);
return 0;
}
###############################################################################
{
my $date_init = 0;
my %cache;
sub sort_date {
%cache = ();
if (! $date_init) {
require Date::Manip;
Date::Manip::Date_Init();
$date_init = 1;
}
return _sort('','date',@_);
}
sub cmp_date {
%cache = ();
if (! $date_init) {
require Date::Manip;
Date::Manip::Date_Init();
$date_init = 1;
}
return _cmp('','date',@_);
}
sub sort_rev_date {
%cache = ();
if (! $date_init) {
require Date::Manip;
Date::Manip::Date_Init();
$date_init = 1;
}
return _sort('rev','date',@_);
}
sub cmp_rev_date {
%cache = ();
if (! $date_init) {
require Date::Manip;
Date::Manip::Date_Init();
$date_init = 1;
}
return _cmp('rev','date',@_);
}
sub _date {
my($ele) = @_;
$cache{$ele} = Date::Manip::ParseDate($ele) if (! exists $cache{$ele});
return 1 if ($cache{$ele});
return 0;
}
sub _cmp_date {
my($x,$y) = @_;
$cache{$x} = Date::Manip::ParseDate($x) if (! exists $cache{$x});
$cache{$y} = Date::Manip::ParseDate($y) if (! exists $cache{$y});
return $cache{$x} cmp $cache{$y};
}
}
###############################################################################
sub sort_ip {
return _sort('','ip',@_);
}
sub cmp_ip {
return _cmp('','ip',@_);
}
sub sort_rev_ip {
return _sort('rev','ip',@_);
}
sub cmp_rev_ip {
return _cmp('rev','ip',@_);
}
sub _ip {
my($ele) = @_;
return 0 unless ($ele =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)(?:\/([0-9]+))?$/);
my ($a,$b,$c,$d,$m) = ($1,$2,$3,$4,$5);
return 0 if ($a > 255 ||
$b > 255 ||
$c > 255 ||
$d > 255 ||
$m > 32);
return 1;
}
sub _cmp_ip {
my($x,$y) = @_;
my(@x,@y);
$x =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)(?:\/([0-9]+))?$/;
@x = ($1,$2,$3,$4,$5);
$y =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)(?:\/([0-9]+))?$/;
@y = ($1,$2,$3,$4,$5);
return ($x[0] <=> $y[0] ||
$x[1] <=> $y[1] ||
$x[2] <=> $y[2] ||
$x[3] <=> $y[3] ||
(defined $x[4] && ! defined $y[4] && 1) ||
(! defined $x[4] && defined $y[4] && -1) ||
(defined $x[4] && defined $y[4] && $x[4] <=> $y[4]) ||
0);
}
###############################################################################
sub sort_nosort {
return 1;
}
sub cmp_nosort {
return -1;
}
sub sort_rev_nosort {
return 1;
}
sub cmp_rev_nosort {
return -1;
}
###############################################################################
sub sort_function {
my $caller = ( caller )[0];
return _sort('','function',@_,$caller);
}
sub cmp_function {
my $caller = ( caller )[0];
return _cmp('','function',@_,$caller);
}
sub sort_rev_function {
my $caller = ( caller )[0];
return _sort('rev','function',@_,$caller);
}
sub cmp_rev_function {
my $caller = ( caller )[0];
return _cmp('rev','function',@_,$caller);
}
sub _function {
return 1;
}
sub _cmp_function {
my($x,$y,$args) = @_;
my ($func,$caller) = @$args;
no strict 'refs';
if (ref($func) eq 'CODE' || $func =~ /::/) {
return &$func($x,$y);
} else {
$func = $caller . "::$func";
return &$func($x,$y);
}
}
###############################################################################
# AMBIGUOUS METHODS
###############################################################################
sub sort_length {
return _sort('','length',@_);
}
sub cmp_length {
return _cmp('','length',@_);
}
sub sort_rev_length {
return _sort('rev','length',@_);
}
sub cmp_rev_length {
return _cmp('rev','length',@_);
}
sub _length {
my($ele) = @_;
return 1 if (! ref($ele));
return 0;
}
sub _cmp_length {
my($x,$y) = @_;
return ( length($x) <=> length($y) );
}
###############################################################################
# SPLIT-ELEMENT METHODS
###############################################################################
sub sort_split {
return _sort_split('','split',@_);
}
sub cmp_split {
return _cmp_split('','split',@_);
}
sub sort_rev_split {
return _sort_split('rev','split',@_);
}
sub cmp_rev_split {
return _cmp_split('rev','split',@_);
}
###############################################################################
sub sort_domain {
my($listref,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return sort_split($listref,'rms',@args);
} else {
return sort_split($listref,'rms',qr/\./,@args);
}
}
sub cmp_domain {
my($x,$y,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return cmp_split($x.$y,'rms',@args);
} else {
return cmp_split($x,$y,'rms',qr/\./,@args);
}
}
sub sort_rev_domain {
my($listref,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return sort_rev_split($listref,'rms',@args);
} else {
return sort_rev_split($listref,'rms',qr/\./,@args);
}
}
sub cmp_rev_domain {
my($x,$y,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return cmp_rev_split($x,$y,'rms',@args);
} else {
return cmp_rev_split($x,$y,'rms',qr/\./,@args);
}
}
###############################################################################
sub sort_numdomain {
my($listref,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = sort_split($listref,'rms',@args);
} else {
$ret = sort_split($listref,'rms',qr/\./,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub cmp_numdomain {
my($x,$y,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = cmp_split($x,$y,'rms',@args);
} else {
$ret = cmp_split($x,$y,'rms',qr/\./,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub sort_rev_numdomain {
my($listref,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = sort_rev_split($listref,'rms',@args);
} else {
$ret = sort_rev_split($listref,'rms',qr/\./,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub cmp_rev_numdomain {
my($x,$y,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = cmp_rev_split($x,$y,'rms',@args);
} else {
$ret = cmp_rev_split($x,$y,'rms',qr/\./,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
###############################################################################
sub sort_path {
my($listref,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return sort_split($listref,'lms',@args);
} else {
return sort_split($listref,'lms',qr/\//,@args);
}
}
sub cmp_path {
my($x,$y,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return cmp_split($x.$y,'lms',@args);
} else {
return cmp_split($x,$y,'lms',qr/\//,@args);
}
}
sub sort_rev_path {
my($listref,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return sort_rev_split($listref,'lms',@args);
} else {
return sort_rev_split($listref,'lms',qr/\//,@args);
}
}
sub cmp_rev_path {
my($x,$y,@args) = @_;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
return cmp_rev_split($x,$y,'lms',@args);
} else {
return cmp_rev_split($x,$y,'lms',qr/\//,@args);
}
}
###############################################################################
sub sort_numpath {
my($listref,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = sort_split($listref,'lms',@args);
} else {
$ret = sort_split($listref,'lms',qr/\//,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub cmp_numpath {
my($x,$y,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = cmp_split($x,$y,'lms',@args);
} else {
$ret = cmp_split($x,$y,'lms',qr/\//,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub sort_rev_numpath {
my($listref,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = sort_rev_split($listref,'lms',@args);
} else {
$ret = sort_rev_split($listref,'lms',qr/\//,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
sub cmp_rev_numpath {
my($x,$y,@args) = @_;
$methods{'split'}{'alt'} = 'alphanum';
$methods{'rev_split'}{'alt'} = 'alphanum';
my $ret;
if (@args && (ref($args[0]) eq '' || ref($args[0]) eq 'Regexp')) {
$ret = cmp_rev_split($x,$y,'lms',@args);
} else {
$ret = cmp_rev_split($x,$y,'lms',qr/\//,@args);
}
$methods{'split'}{'alt'} = 'alphabetic';
$methods{'rev_split'}{'alt'} = 'alphabetic';
return $ret;
}
###############################################################################
# PARTIAL-ELEMENT METHODS
###############################################################################
sub sort_partial {
return _sort_partial('','partial',@_);
}
sub cmp_partial {
return _cmp_partial('','partial',@_);
}
sub sort_rev_partial {
return _sort_partial('rev','partial',@_);
}
sub cmp_rev_partial {
return _cmp_partial('rev','partial',@_);
}
###############################################################################
sub sort_line {
my($listref,$n,@args) = @_;
my $sep;
if (@args && (ref($args[0]) eq 'Regexp' || ! ref($args[0]))) {
$sep = shift(@args);
}
my $hash;
if (@args) {
$hash = shift(@args);
}
my @a = ($listref);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphabetic']]);
return sort_partial(@a);
}
sub cmp_line {
my($x,$y,$n,$sep) = @_;
my @a = ($x,$y);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphabetic']]);
return cmp_partial(@a);
}
sub sort_rev_line {
my($listref,$n,@args) = @_;
my $sep;
if (@args && (ref($args[0]) eq 'Regexp' || ! ref($args[0]))) {
$sep = shift(@args);
}
my $hash;
if (@args) {
$hash = shift(@args);
}
my @a = ($listref);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphabetic']]);
return sort_rev_partial(@a);
}
sub cmp_rev_line {
my($x,$y,$n,$sep) = @_;
my @a = ($x,$y);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphabetic']]);
return cmp_rev_partial(@a);
}
###############################################################################
sub sort_numline {
my($listref,$n,@args) = @_;
my $sep;
if (@args && (ref($args[0]) eq 'Regexp' || ! ref($args[0]))) {
$sep = shift(@args);
}
my $hash;
if (@args) {
$hash = shift(@args);
}
my @a = ($listref);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphanum']]);
return sort_partial(@a);
}
sub cmp_numline {
my($x,$y,$n,$sep) = @_;
my @a = ($x,$y);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphanum']]);
return cmp_partial(@a);
}
sub sort_rev_numline {
my($listref,$n,@args) = @_;
my $sep;
if (@args && (ref($args[0]) eq 'Regexp' || ! ref($args[0]))) {
$sep = shift(@args);
}
my $hash;
if (@args) {
$hash = shift(@args);
}
my @a = ($listref);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphanum']]);
return sort_rev_partial(@a);
}
sub cmp_rev_numline {
my($x,$y,$n,$sep) = @_;
my @a = ($x,$y);
push(@a,$sep) if (defined $sep);
push(@a,[$n,['alphanum']]);
return cmp_rev_partial(@a);
}
###############################################################################
###############################################################################
# Only used for ambiguous/unambiguous comparisons.
sub _sort {
my($rev,$method,@args) = @_;
my($err,$list,$args,$hash,@extra) = _args_sort($method,@args);
return undef if ($err);
return 1 if (! @$list);
# Sort the list.
my @list;
if (defined $hash) {
@list = sort { __cmp($rev,$method,$$hash{$a},$$hash{$b},$args,@extra) } @$list;
} else {
@list = sort { __cmp($rev,$method,$a,$b,$args,@extra) } @$list;
}
# Done
@$list = @list;
return 1;
}
# Only used for ambiguous/unambiguous comparisons.
sub _cmp {
my($rev,$method,@args) = @_;
my($err,$x,$y,$args,@extra) = _args_cmp($method,@args);
return undef if ($err);
return __cmp($rev,$method,$x,$y,$args,@extra);
}
# Only used for ambiguous/unambiguous comparisons.
sub __cmp {
my($rev,$method,$x,$y,$args,@extra) = @_;
no strict 'refs';
# Compare the two elements
my($func,$cmp,$flag,$sort_type);
$func = $methods{$method}{'function'};
$cmp = "_cmp_$func";
$flag = &$cmp($x,$y,$args);
return $flag if (! defined($flag));
$sort_type = $methods{$method}{'type'};
while (! $flag && (@extra || $sort_type ne 'unambiguous')) {
if (@extra) {
if (ref($extra[0]) eq 'ARRAY') {
my(@args);
($method,@args) = @{ shift(@extra) };
if (! exists $methods{$method}) {
warn "ERROR: alternate sort error - invalid method: $method\n";
return undef;
}
my($err,$method_args) = _args_method_args($method,\@args);
return undef if ($err);
if (@args) {
warn "ERROR: alternate sort error - invalid arguments: @args\n";
return undef;
}
$args = $method_args;
} else {
warn "ERROR: alternate sort error - invalid definition\n";
return undef;
}
} else {
$method = $methods{$method}{'alt'};
$args = $methods{$method}{'altargs'};
}
$func = $methods{$method}{'function'};
$cmp = "_cmp_$func";
$flag = &$cmp($x,$y,$args);
return $flag if (! defined($flag));
$sort_type = $methods{$method}{'type'};
}
# If it's reverse...
if ($rev || $methods{$method}{'reverse'}) {
$flag *= -1;
}
# Done
return $flag;
}
###############################################################################
sub _sort_split {
my($rev,$method,@args) = @_;
my($err,$list,$args,$hash,@extra) = _args_sort($method,@args);
return undef if ($err);
return 1 if (! @$list);
# Sort the list.
my @list;
if (defined $hash) {
@list = sort { __cmp_split($rev,$method,$$hash{$a},$$hash{$b},$args,@extra) } @$list;
} else {
@list = sort { __cmp_split($rev,$method,$a,$b,$args,@extra) } @$list;
}
# Done
@$list = @list;
return 1;
}
sub _cmp_split {
my($rev,$method,@args) = @_;
my($err,$x,$y,$args,@extra) = _args_cmp($method,@args);
return undef if ($err);
return __cmp_split($rev,$method,$x,$y,$args,@extra);
}
sub __cmp_split {
my($rev,$method,$x,$y,$args,@extra) = @_;
no strict 'refs';
# Compare the two elements
my(@x,@y,$ms,$re);
($ms,$re) = @$args;
@x = split($re,$x);
@y = split($re,$y);
my $flag = 0;
my $sort_method;
PIECE: while (! $flag && (@x || @y)) {
if ($ms eq 'rms') {
$x = pop(@x);
$y = pop(@y);
} else {
$x = shift(@x);
$y = shift(@y);
}
$sort_method = $method;
# Handle the case where one (or both) is missing
if (! defined $x || $x eq '') {
if (! defined $y || $y eq '') {
$flag = 0;
next PIECE;
} else {
$flag = -1;
last PIECE;
}
} elsif (! defined $y || $y eq '') {
$flag = 1;
last PIECE;
}
# Compare two pieces
my $sort_type = 'split';
while (! $flag && (@extra || $sort_type ne 'unambiguous')) {
if (@extra) {
if (ref($extra[0]) eq 'ARRAY') {
my(@args);
($sort_method,@args) = @{ shift(@extra) };
if (! exists $methods{$sort_method}) {
warn "ERROR: alternate sort error - invalid method: $sort_method\n";
return undef;
}
my($err,$method_args) = _args_method_args($sort_method,\@args);
return undef if ($err);
if (@args) {
warn "ERROR: alternate sort error - invalid arguments: @args\n";
return undef;
}
$args = $method_args;
} else {
warn "ERROR: alternate sort error - invalid definition\n";
return undef;
}
} else {
$args = $methods{$sort_method}{'altargs'};
$sort_method = $methods{$sort_method}{'alt'};
}
my $func = $methods{$sort_method}{'function'};
my $cmp = "_cmp_$func";
$flag = &$cmp($x,$y,$args);
return $flag if (! defined($flag));
$sort_type = $methods{$sort_method}{'type'};
}
}
# If it's reverse...
if ($rev || $methods{$sort_method}{'reverse'}) {
$flag *= -1;
}
# Done
return $flag;
}
###############################################################################
sub _sort_partial {
my($rev,$method,@args) = @_;
my($err,$list,$args,@extra) = _args_sort($method,@args);
return undef if ($err);
return 1 if (! @$list);
# Sort the list.
my @list;
@list = sort { __cmp_partial($rev,$method,$a,$b,$args,@extra) } @$list;
# Done
@$list = @list;
return 1;
}
sub _cmp_partial {
my($rev,$method,@args) = @_;
my($err,$x,$y,$args,@extra) = _args_cmp($method,@args);
return undef if ($err);
return __cmp_partial($rev,$method,$x,$y,$args,@extra);
}
sub __cmp_partial {
my($rev,$method,$x,$y,$args,@extra) = @_;
no strict 'refs';
# Compare the two elements
my(@x,@y,$re);
$re = $$args[0];
@x = split($re,$x);
@y = split($re,$y);
my $flag = 0;
my $sort_method;
FIELD: foreach my $field_args (@extra) {
my @field_args = @$field_args;
# Get $n
if (! @field_args) {
warn "ERROR: field args error - field number required\n";
return undef;
}
my $n = shift(@field_args);
if ($n !~ /^\d+$/) {
warn "ERROR: field args error - field number must be integer: $n\n";
return undef;
}
# Get $hash
my $hash;
if (@field_args && ref($field_args[0]) eq 'HASH') {
$hash = shift(@field_args);
} else {
$hash = undef;
}
# Get the Nth fields (and handle the undef cases)
my($a,$b);
if (@x < $n || ! defined $x[$n-1] || $x[$n-1] eq '') {
if (@y < $n || ! defined $y[$n-1] || $y[$n-1] eq '') {
$flag = 0;
next FIELD;
} else {
$flag = -1;
last FIELD;
}
} elsif (@y < $n || ! defined $y[$n-1] || $y[$n-1] eq '') {
$flag = 1;
last FIELD;
}
$a = $x[$n-1];
$b = $y[$n-1];
# Handle $hash (if defined)
if (defined $hash) {
if (! exists $$hash{$a}) {
if (! exists $$hash{$b}) {
$flag = 0;
next FIELD;
} else {
$flag = -1;
last FIELD;
}
} elsif (! exists $$hash{$b}) {
$flag = 1;
last FIELD;
}
$a = $$hash{$a};
$b = $$hash{$b};
}
# Compare two fields
$sort_method = 'partial';
my $sort_type = 'partial';
METHOD: while (! $flag && (@field_args || $sort_type ne 'unambiguous')) {
if (@field_args) {
if (ref($field_args[0]) eq 'ARRAY') {
my(@args);
($sort_method,@args) = @{ shift(@field_args) };
if (! exists $methods{$sort_method}) {
warn "ERROR: alternate sort error - invalid method: $sort_method\n";
return undef;
}
my($err,$method_args) = _args_method_args($sort_method,\@args);
return undef if ($err);
if (@args) {
warn "ERROR: alternate sort error - invalid arguments: @args\n";
return undef;
}
$args = $method_args;
} else {
warn "ERROR: alternate sort error - invalid definition\n";
return undef;
}
} else {
$args = $methods{$sort_method}{'altargs'};
$sort_method = $methods{$sort_method}{'alt'};
}
my $func = $methods{$sort_method}{'function'};
my $cmp = "_cmp_$func";
$flag = &$cmp($a,$b,$args);
return $flag if (! defined($flag));
$sort_type = $methods{$sort_method}{'type'};
}
last FIELD if ($flag);
}
# If it's reverse...
if ($rev || $methods{$sort_method}{'reverse'}) {
$flag *= -1;
}
# Done
return $flag;
}
###############################################################################
sub _args_sort {
my(@args) = @_;
my(@ret,$err,$method,$listref,$method_args,$hash);
# Method
if (! @args) {
warn "ERROR: sort argument error - method required\n";
return (1);
}
$method = shift(@args);
if (! exists $methods{$method}) {
warn "ERROR: sort argument error - invalid method: $method\n";
return (1);
}
# Listref
if (! @args) {
warn "ERROR: sort argument error - listref expected\n";
return (1);
}
if (ref($args[0]) eq 'ARRAY') {
$listref = shift(@args);
} else {
warn "ERROR: sort argument error - listref expected\n";
return (1);
}
push(@ret,$listref);
# Method arguments
($err,$method_args) = _args_method_args($method,\@args);
return (1) if ($err);
push(@ret,$method_args);
# Hash
if ($method ne 'partial') {
if (@args && ref($args[0]) eq 'HASH') {
$hash = shift(@args);
} else {
$hash = undef;
}
push(@ret,$hash);
}
# Extra
if ($method eq 'unambiguous' && @args) {
warn "ERROR: sort argument error - unexpected arguments: @args\n";
return (1);
}
return (0,@ret,@args);
}
# ($method,$x,$y,@method_args,@extra)
#
sub _args_cmp {
my(@args) = @_;
my(@ret,$err,$method,$x,$y,$method_args,$hash);
# Method
if (! @args) {
warn "ERROR: cmp argument error - method required\n";
return (1);
}
$method = shift(@args);
if (! exists $methods{$method}) {
warn "ERROR: cmp argument error - invalid method: $method\n";
return (1);
}
# X,Y
if (@args < 2) {
warn "ERROR: cmp argument error - two elements expected\n";
return (1);
}
$x = shift(@args);
$y = shift(@args);
push(@ret,$x,$y);
# Method arguments
($err,$method_args) = _args_method_args($method,\@args);
return (1) if ($err);
push(@ret,$method_args);
# Extra
if ($method eq 'unambiguous' && @args) {
warn "ERROR: cmp argument error - unexpected arguments: @args\n";
return (1);
}
return (0,@ret,@args);
}
sub _args_method_args {
my($method,$args) = @_;
my @method_args;
my @expected = @{ $methods{$method}{'args'} };
foreach my $expected (@expected) {
my $type = $$expected{'type'};
if ($type eq 'member') {
my %vals = map { $_,1 } @{ $$expected{'values'} };
if (@$args && exists $vals{ $$args[0] }) {
push(@method_args,shift(@$args));
} else {
push(@method_args,$$expected{'default'});
}
} elsif ($type eq 'regexp') {
if (@$args) {
if (ref($$args[0]) eq 'Regexp') {
push(@method_args,shift(@$args));
} elsif (! ref($$args[0])) {
my $re = shift(@$args);
push(@method_args,qr/$re/);
} else {
my $re = $$expected{'default'};
push(@method_args,qr/$re/);
}
} else {
my $re = $$expected{'default'};
push(@method_args,qr/$re/);
}
} elsif ($type eq 'function') {
if (@$args && (ref($$args[0]) eq 'CODE' || ! ref($$args[0]))) {
push(@method_args,shift(@$args));
} else {
die "ERROR: invalid argument - function required\n";
}
} elsif ($type eq 'string') {
if (@$args && ! ref($$args[0])) {
push(@method_args,shift(@$args));
} elsif (exists $$expected{'default'}) {
push(@method_args,$$expected{'default'});
} else {
die "ERROR: invalid argument - string required\n";
}
} else {
die "ERROR: invalid argument descriptor: $type\n";
}
}
return (0,\@method_args);
}
1;
# Local Variables:
# mode: cperl
# indent-tabs-mode: nil
# cperl-indent-level: 3
# cperl-continued-statement-offset: 2
# cperl-continued-brace-offset: 0
# cperl-brace-offset: 0
# cperl-brace-imaginary-offset: 0
# cperl-label-offset: -2
# End: