summaryrefslogtreecommitdiff
path: root/lib/Template/Iterator.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Template/Iterator.pm')
-rw-r--r--lib/Template/Iterator.pm446
1 files changed, 446 insertions, 0 deletions
diff --git a/lib/Template/Iterator.pm b/lib/Template/Iterator.pm
new file mode 100644
index 0000000..0063b6e
--- /dev/null
+++ b/lib/Template/Iterator.pm
@@ -0,0 +1,446 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Iterator
+#
+# DESCRIPTION
+#
+# Module defining an iterator class which is used by the FOREACH
+# directive for iterating through data sets. This may be
+# sub-classed to define more specific iterator types.
+#
+# An iterator is an object which provides a consistent way to
+# navigate through data which may have a complex underlying form.
+# This implementation uses the get_first() and get_next() methods to
+# iterate through a dataset. The get_first() method is called once
+# to perform any data initialisation and return the first value,
+# then get_next() is called repeatedly to return successive values.
+# Both these methods return a pair of values which are the data item
+# itself and a status code. The default implementation handles
+# iteration through an array (list) of elements which is passed by
+# reference to the constructor. An empty list is used if none is
+# passed. The module may be sub-classed to provide custom
+# implementations which iterate through any kind of data in any
+# manner as long as it can conforms to the get_first()/get_next()
+# interface. The object also implements the get_all() method for
+# returning all remaining elements as a list reference.
+#
+# For further information on iterators see "Design Patterns", by the
+# "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, John
+# Vlissides), Addision-Wesley, ISBN 0-201-63361-2.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Iterator.pm,v 2.59 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Iterator;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD ); # AUTO?
+use base qw( Template::Base );
+use Template::Constants;
+use Template::Exception;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.59 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\@target, \%options)
+#
+# Constructor method which creates and returns a reference to a new
+# Template::Iterator object. A reference to the target data (array
+# or hash) may be passed for the object to iterate through.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $data = shift || [ ];
+ my $params = shift || { };
+
+ if (ref $data eq 'HASH') {
+ # map a hash into a list of { key => ???, value => ??? } hashes,
+ # one for each key, sorted by keys
+ $data = [ map { { key => $_, value => $data->{ $_ } } }
+ sort keys %$data ];
+ }
+ elsif (UNIVERSAL::can($data, 'as_list')) {
+ $data = $data->as_list();
+ }
+ elsif (ref $data ne 'ARRAY') {
+ # coerce any non-list data into an array reference
+ $data = [ $data ] ;
+ }
+
+ bless {
+ _DATA => $data,
+ _ERROR => '',
+ }, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# get_first()
+#
+# Initialises the object for iterating through the target data set. The
+# first record is returned, if defined, along with the STATUS_OK value.
+# If there is no target data, or the data is an empty set, then undef
+# is returned with the STATUS_DONE value.
+#------------------------------------------------------------------------
+
+sub get_first {
+ my $self = shift;
+ my $data = $self->{ _DATA };
+
+ $self->{ _DATASET } = $self->{ _DATA };
+ my $size = scalar @$data;
+ my $index = 0;
+
+ return (undef, Template::Constants::STATUS_DONE) unless $size;
+
+ # initialise various counters, flags, etc.
+ @$self{ qw( SIZE MAX INDEX COUNT FIRST LAST ) }
+ = ( $size, $size - 1, $index, 1, 1, $size > 1 ? 0 : 1, undef );
+ @$self{ qw( PREV NEXT ) } = ( undef, $self->{ _DATASET }->[ $index + 1 ]);
+
+ return $self->{ _DATASET }->[ $index ];
+}
+
+
+
+#------------------------------------------------------------------------
+# get_next()
+#
+# Called repeatedly to access successive elements in the data set.
+# Should only be called after calling get_first() or a warning will
+# be raised and (undef, STATUS_DONE) returned.
+#------------------------------------------------------------------------
+
+sub get_next {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my $data = $self->{ _DATASET };
+
+ # warn about incorrect usage
+ unless (defined $index) {
+ my ($pack, $file, $line) = caller();
+ warn("iterator get_next() called before get_first() at $file line $line\n");
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ # update counters and flags
+ $index++;
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $index, $index + 1, 0, $index == $max ? 1 : 0 );
+ @$self{ qw( PREV NEXT ) } = @$data[ $index - 1, $index + 1 ];
+ return $data->[ $index ]; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# get_all()
+#
+# Method which returns all remaining items in the iterator as a Perl list
+# reference. May be called at any time in the life-cycle of the iterator.
+# The get_first() method will be called automatically if necessary, and
+# then subsequent get_next() calls are made, storing each returned
+# result until the list is exhausted.
+#------------------------------------------------------------------------
+
+sub get_all {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my @data;
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ $index++;
+ @data = @{ $self->{ _DATASET } } [ $index..$max ];
+
+ # update counters and flags
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $max, $max + 1, 0, 1 );
+
+ return \@data; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides access to internal fields (e.g. size, first, last, max, etc)
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ # alias NUMBER to COUNT for backwards compatability
+ $item = 'COUNT' if $item =~ /NUMBER/i;
+
+ return $self->{ uc $item };
+}
+
+
+#========================================================================
+# ----- PRIVATE DEBUG METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string detailing the internal state of
+# the iterator object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ join('',
+ " Data: ", $self->{ _DATA }, "\n",
+ " Index: ", $self->{ INDEX }, "\n",
+ "Number: ", $self->{ NUMBER }, "\n",
+ " Max: ", $self->{ MAX }, "\n",
+ " Size: ", $self->{ SIZE }, "\n",
+ " First: ", $self->{ FIRST }, "\n",
+ " Last: ", $self->{ LAST }, "\n",
+ "\n"
+ );
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Iterator - Data iterator used by the FOREACH directive
+
+=head1 SYNOPSIS
+
+ my $iter = Template::Iterator->new(\@data, \%options);
+
+=head1 DESCRIPTION
+
+The Template::Iterator module defines a generic data iterator for use
+by the FOREACH directive.
+
+It may be used as the base class for custom iterators.
+
+=head1 PUBLIC METHODS
+
+=head2 new($data)
+
+Constructor method. A reference to a list of values is passed as the
+first parameter. Subsequent calls to get_first() and get_next() calls
+will return each element from the list.
+
+ my $iter = Template::Iterator->new([ 'foo', 'bar', 'baz' ]);
+
+The constructor will also accept a reference to a hash array and will
+expand it into a list in which each entry is a hash array containing
+a 'key' and 'value' item, sorted according to the hash keys.
+
+ my $iter = Template::Iterator->new({
+ foo => 'Foo Item',
+ bar => 'Bar Item',
+ });
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([
+ { key => 'bar', value => 'Bar Item' },
+ { key => 'foo', value => 'Foo Item' },
+ ]);
+
+When passed a single item which is not an array reference, the constructor
+will automatically create a list containing that single item.
+
+ my $iter = Template::Iterator->new('foo');
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([ 'foo' ]);
+
+Note that a single item which is an object based on a blessed ARRAY
+references will NOT be treated as an array and will be folded into
+a list containing that one object reference.
+
+ my $list = bless [ 'foo', 'bar' ], 'MyListClass';
+ my $iter = Template::Iterator->new($list);
+
+equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+If the object provides an as_list() method then the Template::Iterator
+constructor will call that method to return the list of data. For example:
+
+ package MyListObject;
+
+ sub new {
+ my $class = shift;
+ bless [ @_ ], $class;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+This is then functionally equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+The iterator will return only one item, a reference to the MyListObject
+object, $list.
+
+By adding an as_list() method to the MyListObject class, we can force
+the Template::Iterator constructor to treat the object as a list and
+use the data contained within.
+
+ package MyListObject;
+
+ ...
+
+ sub as_list {
+ my $self = shift;
+ return $self;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+The iterator will now return the two item, 'foo' and 'bar', which the
+MyObjectList encapsulates.
+
+=head2 get_first()
+
+Returns a ($value, $error) pair for the first item in the iterator set.
+The $error returned may be zero or undefined to indicate a valid datum
+was successfully returned. Returns an error of STATUS_DONE if the list
+is empty.
+
+=head2 get_next()
+
+Returns a ($value, $error) pair for the next item in the iterator set.
+Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 get_all()
+
+Returns a (\@values, $error) pair for all remaining items in the iterator
+set. Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 size()
+
+Returns the size of the data set or undef if unknown.
+
+=head2 max()
+
+Returns the maximum index number (i.e. the index of the last element)
+which is equivalent to size() - 1.
+
+=head2 index()
+
+Returns the current index number which is in the range 0 to max().
+
+=head2 count()
+
+Returns the current iteration count in the range 1 to size(). This is
+equivalent to index() + 1. Note that number() is supported as an alias
+for count() for backwards compatability.
+
+=head2 first()
+
+Returns a boolean value to indicate if the iterator is currently on
+the first iteration of the set.
+
+=head2 last()
+
+Returns a boolean value to indicate if the iterator is currently on
+the last iteration of the set.
+
+=head2 prev()
+
+Returns the previous item in the data set, or undef if the iterator is
+on the first item.
+
+=head2 next()
+
+Returns the next item in the data set or undef if the iterator is on the
+last item.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.59, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>