package PROP::Conditions::Foreign;

use strict;
use UNIVERSAL qw/isa/;
use PROP::Link;
use Hash::Util qw/lock_keys/;
use PROP::Exception::IllegalArgument;

sub new {
    my ($class, $link, $relationship, $expressions, $bindings) = @_;
    my $self = bless({}, $class);
   
    my $err_msg;

    $err_msg = "more arguments passed than expected" if scalar(@_) == 6;

    $err_msg = "was expecting an array reference as fourth argument"
	unless ref($bindings) eq 'ARRAY' or not defined $bindings;

    $err_msg = "was expecting an array reference as third argument"
	unless ref($expressions) eq 'ARRAY' or not defined $expressions;

    $err_msg = "was expecting either 'parents' or 'children' as second argument"
	unless $relationship eq 'parents' or $relationship eq 'children';

    $err_msg = "was expecting a PROP::Link object as first argument"
	unless isa($link, 'PROP::Link');

    die new PROP::Exception::IllegalArgument($err_msg) if $err_msg;

    $self->{-link}         = $link;
    $self->{-relationship} = $relationship;
    $self->{-expressions}  = $expressions;
    $self->{-bindings}     = $bindings;

    lock_keys(%$self);

    push(@{$self->{-expressions}},
	 'p.' . $link->get_parent_class()->get_pk_name() .
	 ' = ' .
	 'l.' . $link->get_parent_field_name());

    push(@{$self->{-expressions}},
	 'c.' . $link->get_child_class()->get_pk_name() .
	 ' = ' .
	 'l.' . $link->get_child_field_name());

    return $self;
}

sub get_link {
    my ($self) = @_;
    return $self->{-link};
}

sub get_relationship {
    my ($self) = @_;
    return $self->{-relationship};
}

sub get_expressions {
    my ($self) = @_;
    return $self->{-expressions};
}

sub get_bindings {
    my ($self) = @_;
    return $self->{-bindings};
}

1;


=head1 Name

PROP::Conditions::Foreign

=head1 Synopsis

 $fc = new PROP::Conditions::Foreign($link, $relationship,
				    $expressions, $bindings);

=head1 Usage

This class serves as a specifier for PROP::Query::Object objects,
allowing users of the API to specify that objects be loaded only if
they have relatives that satisfy specific conditions.  For example,
you might wish to load objects of type Foo only if they have children
of type Bar that have biz fields of value greater than five.  An array
reference of zero or more PROP::Conditions::Foreign objects may be
passed into the constructor of a PROP::Query::Object object to
accomplish this.  Each PROP::Conditions::Foreign object specifies one
or more constraints on one relationship.

The first argument to the constructor is a PROP::Link object
which specifies the relationship of interest.

The second argument to the constructor specifies on what aspect of the
relationship we wish to impose a constraint, and it must be the string
value "parents" or the string value "children".  The former indicates
that we wish to specify that loaded objects have parents that satisfy
certain conditions, and the latter indicates that we wish to specify
that the loaded objects have children that satisfy certain conditions.

The third argument is a list of expressions that are used to spell out
the constraints.  Field names must be bound to tables explicitly using
the aliases 'p', 'c', and 'l'.  The 'p' alias refers to the parent
table in the relationship, the 'c' alias to the child table, and the
'l' alias to the link table.  When the underlying code eventually
generates real SQL code, these aliases will be properly modified to
deal with the fact that a PROP::Query::Object can accept multiple
PROP::Conditions::Foreign objects, each of which used the same table
aliasing.

The fourth argument argument to the constructor is an array of values
that are to be bound to the variables in the expressions specified by
the third argument.

=head1 Author

Andrew Gibbs (awgibbs@awgibbs.com,andrew.gibbs@nist.gov)

=head1 Legalese

This software was developed at the National Institute of Standards and
Technology by employees of the Federal Government in the course of
their official duties. Pursuant to title 17 Section 105 of the United
States Code this software is not subject to copyright protection and
is in the public domain. PROP is an experimental system. NIST
assumes no responsibility whatsoever for its use by other parties, and
makes no guarantees, expressed or implied, about its quality,
reliability, or any other characteristic. We would appreciate
acknowledgement if the software is used.  This software can be
redistributed and/or modified freely provided that any derivative
works bear some notice that they are derived from it, and any modified
versions bear some notice that they have been modified.
