package PDF::FromHTML::Template::Iterator;
use strict;
BEGIN {
use vars qw(@ISA);
@ISA = qw(PDF::FromHTML::Template::Base);
use PDF::FromHTML::Template::Base;
}
sub new
{
my $class = shift;
my $self = $class->SUPER::new(@_);
unless (PDF::FromHTML::Template::Factory::isa($self->{CONTEXT}, 'CONTEXT'))
{
die "Internal Error: No context object passed to ", __PACKAGE__, $/;
}
$self->{MAXITERS} ||= 0;
# This is the index we will work on NEXT, in whatever direction the
# iterator is going.
$self->{INDEX} = -1;
# This will always increment because it's tracking how many iterations
# have happened on this page, regardless of the direction the iterator
# is traveling.
$self->{ITERS_THIS_PAGE} = 0;
# This is a short-circuit parameter to let the iterator function in a
# null state.
$self->{NO_PARAMS} = 0;
unless ($self->{NAME} =~ /\w/)
{
$self->{NO_PARAMS} = 1;
warn "INTERNAL ERROR: 'NAME' was blank was blank when passed to ", __PACKAGE__, $/;
return $self;
}
# Cache the reference to the appropriate data.
$self->{DATA} = $self->{CONTEXT}->param($self->{NAME});
unless (UNIVERSAL::isa($self->{DATA}, 'ARRAY'))
{
$self->{NO_PARAMS} = 1;
warn "'$self->{NAME}' does not have a list of parameters", $/;
return $self;
}
unless (@{$self->{DATA}})
{
$self->{NO_PARAMS} = 1;
}
$self->{MAX_INDEX} = $#{$self->{DATA}};
return $self;
}
sub enter_scope
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
for my $x ($self->{DATA}[$self->{INDEX}])
{
$x->{uc $_} = delete $x->{$_} for keys %$x;
}
push @{$self->{CONTEXT}{PARAM_MAP}}, $self->{DATA}[$self->{INDEX}];
return 1;
}
sub exit_scope
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
# There has to be the base parameter map and at least the one that
# Iterator::enter_scope() added on top.
@{$self->{CONTEXT}{PARAM_MAP}} > 1 ||
die "Internal Error: ", __PACKAGE__, "'s internal param_map off!", $/;
pop @{$self->{CONTEXT}{PARAM_MAP}};
return 1;
}
sub can_continue
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
return 1 if $self->more_params &&
$self->more_space &&
$self->more_iters;
return 0;
}
sub more_iters
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
return 1 unless $self->{MAXITERS};
return 1 if $self->{MAXITERS} > $self->{ITERS_THIS_PAGE};
return 0;
}
sub more_params
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
return 1 if $self->{MAX_INDEX} > $self->{INDEX};
return 0;
}
sub more_space
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
return 1 if $self->{CONTEXT}->get($self, 'Y') >= ($self->{CONTEXT}->get($self, 'END_Y'));
return 0;
}
# Call this method BEFORE incrementing the index to the next value.
sub _do_globals
{
my $self = shift;
my $data = $self->{DATA}[$self->{INDEX}];
# Perl's arrays are 0-indexed. Thus, the first element is at index "0".
# This means that odd-numbered elements are at even indices, and vice-versa.
# This also means that MAX (the number of elements in the array) can never
# be the value of an index. It is NOT the last index in the array.
$data->{'__FIRST__'} ||= ($self->{INDEX} == 0);
$data->{'__INNER__'} ||= (0 < $self->{INDEX} && $self->{INDEX} < $self->{MAX_INDEX});
$data->{'__LAST__'} ||= ($self->{INDEX} == $self->{MAX_INDEX});
$data->{'__ODD__'} ||= !($self->{INDEX} % 2);
return 1;
}
sub next
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
return 0 unless $self->more_params;
$self->exit_scope;
$self->{INDEX}++;
$self->{ITERS_THIS_PAGE}++;
$self->_do_globals;
$self->enter_scope;
return 1;
}
sub back_up
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
$self->exit_scope;
$self->{INDEX}--;
$self->{ITERS_THIS_PAGE}++;
$self->_do_globals;
$self->enter_scope;
return 1;
}
sub reset
{
my $self = shift;
return 0 if $self->{NO_PARAMS};
$self->{INDEX} = -1;
$self->{ITERS_THIS_PAGE} = 0;
return 1;
}
1;
__END__