/usr/local/CPAN/Xmldoom/Xmldoom/Criteria/Search.pm
package Xmldoom::Criteria::Search;
use Xmldoom::Criteria;
use Xmldoom::Criteria::Comparison;
use Xmldoom::Criteria::Attribute;
use Xmldoom::Criteria::Property;
use Xmldoom::Criteria::Literal;
use DBIx::Romani::Query::Select;
use DBIx::Romani::Query::Where;
use DBIx::Romani::Query::Comparison;
use DBIx::Romani::Query::SQL::Column;
use strict;
use Data::Dumper;
sub new
{
my $class = shift;
my $args = shift;
my $type;
if ( ref($args) eq 'HASH' )
{
$type = $args->{type};
}
else
{
$type = $args;
}
my $self = {
type => $type || $Xmldoom::Criteria::AND,
comparisons => [ ]
};
bless $self, $class;
return $self;
}
sub get_type { return shift->{type}; }
sub get_comparisons { return shift->{comparisons}; }
sub set_type
{
my ($self, $type) = @_;
$self->{type} = $type;
}
sub _add
{
my $self = shift;
my $comp;
if ( $_[0]->isa( 'Xmldoom::Criteria::Search' ) )
{
$comp = shift;
}
else
{
$comp = Xmldoom::Criteria::Comparison->new( @_ );
}
push @{$self->{comparisons}}, $comp;
}
sub add
{
my ($self, $lval, $rval, $type) = @_;
if ( $lval->isa( 'Xmldoom::Criteria::Search' ) )
{
$self->_add( $lval );
}
else
{
$self->add_prop( $lval, $rval, $type );
}
}
sub add_attr
{
my ($self, $attr, $value, $type) = @_;
my $rval;
if ( ref($value) eq 'ARRAY' )
{
$rval = [ ];
foreach my $i ( @$value )
{
push @$rval, Xmldoom::Criteria::Literal->new( $i );
}
}
elsif ( defined $value )
{
$rval = Xmldoom::Criteria::Literal->new( $value );
}
$self->_add(
Xmldoom::Criteria::Attribute->new( $attr ),
$rval,
$type
);
}
sub add_prop
{
my ($self, $prop, $value, $type) = @_;
my $rval;
if ( ref($value) eq 'ARRAY' )
{
$rval = [ ];
foreach my $i ( @$value )
{
push @$rval, Xmldoom::Criteria::Literal->new( $i );
}
}
elsif ( defined $value )
{
$rval = Xmldoom::Criteria::Literal->new( $value );
}
$self->_add(
Xmldoom::Criteria::Property->new( $prop ),
$rval,
$type
);
}
sub join_attr
{
my ($self, $attr1, $attr2, $type) = @_;
$self->_add(
Xmldoom::Criteria::Attribute->new( $attr1 ),
Xmldoom::Criteria::Attribute->new( $attr2 ),
$type
);
}
sub join_prop
{
my ($self, $prop1, $prop2, $type) = @_;
$self->_add(
Xmldoom::Criteria::Property->new( $prop1 ),
Xmldoom::Criteria::Property->new( $prop2 ),
$type
);
}
sub get_tables
{
my ($self, $database) = @_;
my $table_hash = { };
foreach my $comp ( @{$self->get_comparisons()} )
{
foreach my $table_name ( @{$comp->get_tables($database)} )
{
$table_hash->{$table_name} = 1;
}
}
my @table_names = keys %$table_hash;
return \@table_names;
}
sub generate
{
my $self = shift;
my $args = shift;
my $database;
if ( ref($args) eq 'HASH' )
{
$database = $args->{database};
}
else
{
$database = $args;
}
# actually make the where statment
my $where = DBIx::Romani::Query::Where->new( $self->get_type() );
foreach my $comp ( @{$self->get_comparisons()} )
{
$where->add( $comp->generate($database) );
}
# reduce it if possible.
if ( scalar @{$where->get_values()} == 1 )
{
# if there is only one, then return only that
return $where->get_values()->[0];
}
elsif ( scalar @{$where->get_values()} == 0 )
{
# no search. we're grabbing everything?
return undef;
}
return $where;
}
sub generate_description
{
my $self = shift;
my $args = shift;
my $database;
my $object_name;
if ( ref($args) eq 'HASH' )
{
$database = $args->{database};
$object_name = $args->{object_name};
}
else
{
$database = $args;
$object_name = shift;
}
my $object = $database->get_object( $object_name );
my %comps;
# group by the lvalues
foreach my $comp ( @{$self->get_comparisons()} )
{
my $lval = $comp->get_lval();
my $name;
my %res = (
type => $comp->get_type(),
rval => $comp->get_rval(),
lval => $lval,
);
# name
if ( $lval->isa('Xmldoom::Criteria::Property') )
{
if ( $lval->get_object_name() eq $object_name )
{
$name = $object->get_property( $lval->get_property_name() )->get_description();
}
else
{
die "Unimplemented.";
}
$res{prop} = $database->get_object( $lval->get_object_name() )->get_property( $lval->get_property_name() );
}
elsif ( $lval->isa('Xmldoom::Criteria::Attribute') )
{
$name = sprintf "%s.%s", $lval->get_table_name(), $lval->get_column_name();
}
if ( not defined $comps{$name} )
{
$comps{$name} = [ ];
}
push @{$comps{$name}}, \%res;
}
my $text;
if ( $self->get_type() eq 'AND' )
{
my @texts;
while( my ($name, $value) = each %comps )
{
my @chunks;
foreach my $comp ( @$value )
{
my $value = $comp->{rval}->[0]->get_value( $database, $comp->{lval} );
my $op;
my $desc = $comp->{prop}->get_value_description( $value );
if ( $desc )
{
$value = $desc;
}
if ( $comp->{type} eq $Xmldoom::Criteria::EQUAL )
{
$op = "is $value";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::NOT_EQUAL )
{
$op = "isn't $value";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::GREATER_THAN )
{
$op = "is greater than $value";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::GREATER_EQUAL )
{
$op = "is $value or greater";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::LESS_THAN )
{
$op = "is less than $value";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::LESS_EQUAL )
{
$op = "is $value or less";
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::LIKE )
{
my $pure = $value;
$pure =~ s/\%//g;
if ( $value =~ /^\%.*\%$/ )
{
$op = "contains $pure";
}
elsif ( $value =~ /^\%.*/ )
{
$op = "ends with $pure";
}
elsif ( $value =~ /^.*\%/ )
{
$op = "begins with $pure";
}
else
{
$op = "is similar to $pure";
}
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::NOT_LIKE )
{
my $pure = $value;
$pure =~ s/\%//g;
if ( $value =~ /^\%.*\%$/ )
{
$op = "doesn't contain $pure";
}
elsif ( $value =~ /^\%.*/ )
{
$op = "doesn't end with $pure";
}
elsif ( $value =~ /^.*\%/ )
{
$op = "doesn't begin with $pure";
}
else
{
$op = "isn't similar to $pure";
}
}
elsif ( $comp->{type} eq $Xmldoom::Criteria::BETWEEN )
{
my $value2 = $comp->{rval}->[1]->get_value( $database, $comp->{lval} );
$op = "is between $value and $value2";
}
else
{
die "Unimplemented.";
}
push @chunks, $op;
}
push @texts, sprintf( "%s %s", $name, join(' but ', @chunks ) );
}
$text = join ', and ', @texts;
}
else
{
# TODO: we should group these as 'comp1 (or comp2 or comp3)' in the text.
die "Unimplemented.";
}
return $text;
}
sub clone
{
my $self = shift;
my $search = Xmldoom::Criteria::Search->new( $self->get_type() );
foreach my $comp ( @{$self->get_comparisons()} )
{
push @{$search->{comparisons}}, $comp->clone();
}
return $search;
}
1;