Improved tests and documentation. Fixed a few bugs in register()
[p5sagit/Excel-Template.git] / lib / Excel / Template / Iterator.pm
1 package Excel::Template::Iterator;
2
3 use strict;
4
5 BEGIN {
6     use vars qw(@ISA);
7     @ISA = qw(Excel::Template::Base);
8
9     use Excel::Template::Base;
10 }
11
12 sub new
13 {
14     my $class = shift;
15     my $self = $class->SUPER::new(@_);
16
17     unless (Excel::Template::Factory::isa($self->{CONTEXT}, 'CONTEXT'))
18     {
19         die "Internal Error: No context object passed to ", __PACKAGE__, $/;
20     }
21
22     $self->{MAXITERS} ||= 0;
23
24     # This is the index we will work on NEXT, in whatever direction the
25     # iterator is going.
26     $self->{INDEX} = -1;
27
28     # This is a short-circuit parameter to let the iterator function in a
29     # null state.
30     $self->{NO_PARAMS} = 0;
31     unless ($self->{NAME} =~ /\w/)
32     {
33         $self->{NO_PARAMS} = 1;
34
35         warn "INTERNAL ERROR: 'NAME' was blank was blank when passed to ", __PACKAGE__, $/ if $^W;
36
37         return $self;
38     }
39
40     # Cache the reference to the appropriate data.
41     $self->{DATA} = $self->{CONTEXT}->param($self->{NAME});
42
43     unless (UNIVERSAL::isa($self->{DATA}, 'ARRAY'))
44     {
45         $self->{NO_PARAMS} = 1;
46         warn "'$self->{NAME}' does not have a list of parameters", $/ if $^W;
47
48         return $self;
49     }
50
51     unless (@{$self->{DATA}})
52     {
53         $self->{NO_PARAMS} = 1;
54     }
55
56     $self->{MAX_INDEX} = $#{$self->{DATA}};
57
58     return $self;
59 }
60
61 sub enter_scope
62 {
63     my $self = shift;
64
65     return 0 if $self->{NO_PARAMS};
66
67     for my $x ($self->{DATA}[$self->{INDEX}])
68     {
69         $x->{uc $_} = delete $x->{$_} for keys %$x;
70     }
71
72     push @{$self->{CONTEXT}{PARAM_MAP}}, $self->{DATA}[$self->{INDEX}];
73
74     return 1;
75 }
76
77 sub exit_scope
78 {
79     my $self = shift;
80
81     return 0 if $self->{NO_PARAMS};
82
83     # There has to be the base parameter map and at least the one that
84     # Iterator::enter_scope() added on top.
85     @{$self->{CONTEXT}{PARAM_MAP}} > 1 ||
86         die "Internal Error: ", __PACKAGE__, "'s internal param_map off!", $/;
87
88     pop @{$self->{CONTEXT}{PARAM_MAP}};
89
90     return 1;
91 }
92
93 sub can_continue
94 {
95     my $self = shift;
96
97     return 0 if $self->{NO_PARAMS};
98
99     return 1 if $self->more_params;
100
101     return 0;
102 }
103
104 sub more_params
105 {
106     my $self = shift;
107
108     return 0 if $self->{NO_PARAMS};
109
110     return 1 if $self->{MAX_INDEX} > $self->{INDEX};
111
112     return 0;
113 }
114
115 # Call this method BEFORE incrementing the index to the next value.
116 sub _do_globals
117 {
118     my $self = shift;
119
120     my $data = $self->{DATA}[$self->{INDEX}];
121
122     # Perl's arrays are 0-indexed. Thus, the first element is at index "0".
123     # This means that odd-numbered elements are at even indices, and vice-versa.
124     # This also means that MAX (the number of elements in the array) can never
125     # be the value of an index. It is NOT the last index in the array.
126
127     $data->{'__FIRST__'} ||= ($self->{INDEX} == 0);
128     $data->{'__INNER__'} ||= (0 < $self->{INDEX} && $self->{INDEX} < $self->{MAX_INDEX});
129     $data->{'__LAST__'}  ||= ($self->{INDEX} == $self->{MAX_INDEX});
130     $data->{'__ODD__'}   ||= !($self->{INDEX} % 2);
131
132     return 1;
133 }
134
135 sub next
136 {
137     my $self = shift;
138
139     return 0 if $self->{NO_PARAMS};
140
141     return 0 unless $self->more_params;
142
143     $self->exit_scope;
144
145     $self->{INDEX}++;
146
147     $self->_do_globals;
148
149     $self->enter_scope;
150
151     return 1;
152 }
153
154 # This method doesn't seem to be used ...
155 # If it is reinstated, here's the POD for it
156 #=head2 back_up
157 #
158 #Go to the previous iteration of the loop
159 #
160 #sub back_up
161 #{
162 #    my $self = shift;
163 #
164 #    return 0 if $self->{NO_PARAMS};
165 #
166 #    $self->exit_scope;
167 #
168 #    $self->{INDEX}--;
169 #
170 #    $self->_do_globals;
171 #
172 #    $self->enter_scope;
173 #
174 #    return 1;
175 #}
176
177 # This method doesn't seem to be used ...
178 # If it is reinstated, here's the POD for it
179 #=head2 reset
180 #
181 #Resets the iterator
182 #
183 #sub reset
184 #{
185 #    my $self = shift;
186 #
187 #    return 0 if $self->{NO_PARAMS};
188 #
189 #    $self->{INDEX} = -1;
190 #
191 #    return 1;
192 #}
193
194 1;
195 __END__
196
197 =head1 NAME
198
199 Excel::Template::Iterator
200
201 =head1 PURPOSE
202
203 This is meant for internal use only. Documentation is provided for subclassing.
204
205 =head1 NODE NAME
206
207 None
208
209 =head1 INHERITANCE
210
211 None
212
213 =head1 ATTRIBUTES
214
215 None
216
217 =head1 CHILDREN
218
219 None
220
221 =head1 AFFECTS
222
223 This is a helper class for LOOP
224
225 =head1 DEPENDENCIES
226
227 None
228
229 =head1 METHODS
230
231 =head2 can_continue
232
233 Determines if the iterator can continue.
234
235 Currently, this wraps more_params(), but there other possible situations, such as the page ending.
236
237 =head2 more_params
238
239 Determines if the iterator for the loop has more parameters that it can consume
240
241 =head2 next
242
243 Go to the next iteration of the loop
244
245 =head1 AUTHOR
246
247 Rob Kinyon (rob.kinyon@gmail.com)
248
249 =head1 SEE ALSO
250
251 LOOP
252
253 =cut