Added more tests for conditionals. Need to fix linking in POD. - Intermediate commit!
[p5sagit/Excel-Template.git] / lib / Excel / Template / Context.pm
CommitLineData
d0eafc11 1package Excel::Template::Context;
2
3use strict;
4
5BEGIN {
6 use vars qw(@ISA);
7 @ISA = qw(Excel::Template::Base);
8
9 use Excel::Template::Base;
10}
11
12use Excel::Template::Format;
13
14# This is a helper object. It is not instantiated by the user, nor does it
15# represent an XML object. Rather, every container will use this object to
16# maintain the context for its children.
17
8c63e224 18my %isAbsolute = map { $_ => ~~1 } qw(
d0eafc11 19 ROW
20 COL
21);
22
23sub new
24{
25 my $class = shift;
26 my $self = $class->SUPER::new(@_);
27
28 $self->{ACTIVE_WORKSHEET} = undef;
29 $self->{ACTIVE_FORMAT} = Excel::Template::Format->blank_format($self);
37513eae 30 $self->{WORKSHEET_NAMES} = undef;
d0eafc11 31
8fd01531 32 $self->{__MARKS} = {};
33
e976988f 34 # Removed NAME_MAP until I figure out what the heck it's for
35 for (qw( STACK PARAM_MAP ))
c11fa570 36 {
d01e4722 37 next if defined $self->{$_} && ref $self->{$_} eq 'ARRAY';
c11fa570 38 $self->{$_} = [];
39 }
d0eafc11 40
41 $self->{$_} = 0 for keys %isAbsolute;
42
43 return $self;
44}
45
8c63e224 46sub use_unicode { $_[0]->{UNICODE} && 1 }
47
d0eafc11 48sub _find_param_in_map
49{
50 my $self = shift;
51 my ($map, $param, $depth) = @_;
52 $param = uc $param;
53 $depth ||= 0;
54
37513eae 55 my ($val, $found);
d0eafc11 56 for my $map (reverse @{$self->{$map}})
57 {
58 next unless exists $map->{$param};
59 $depth--, next if $depth;
60
8c63e224 61 $found = ~~1;
d0eafc11 62 $val = $map->{$param};
63 last;
64 }
65
66 die "Parameter '$param' not found\n"
67 if !$found && $self->{DIE_ON_NO_PARAM};
68
69 return $val;
70}
71
72sub param
73{
74 my $self = shift;
75 $self->_find_param_in_map(
76 'PARAM_MAP',
77 @_,
78 );
79}
80
e976988f 81#sub named_param
82#{
83# my $self = shift;
84# $self->_find_param_in_map(
85# 'NAME_MAP',
86# @_,
87# );
88#}
d0eafc11 89
90sub resolve
91{
92 my $self = shift;
93 my ($obj, $key, $depth) = @_;
94 $key = uc $key;
95 $depth ||= 0;
96
97 my $obj_val = $obj->{$key};
98
99 $obj_val = $self->param($1)
100 if $obj_val =~ /^\$(\S+)$/o;
101
102#GGG Remove this once NAME_MAP is working
103# $obj_val = $self->named_param($1)
104# if $obj_val =~ /^\\(\S+)$/o;
105
106#GGG Does this adequately test values to make sure they're legal??
107 # A value is defined as:
108 # 1) An optional operator (+, -, *, or /)
109 # 2) A decimal number
110
111#GGG Convert this to use //x
8fd01531 112 my ($op, $val) = $obj_val =~ m/^\s*([\+\*\/\-])?\s*([\d.]*\d)\s*$/oi;
d0eafc11 113
114 # Unless it's a relative value, we have what we came for.
115 return $obj_val unless $op;
116
117 my $prev_val = $isAbsolute{$key}
118 ? $self->{$key}
119 : $self->get($obj, $key, $depth + 1);
120
121 return $obj_val unless defined $prev_val;
122 return $prev_val unless defined $obj_val;
123
124 # Prevent divide-by-zero issues.
125 return $val if $op eq '/' and $val == 0;
126
127 my $new_val;
128 for ($op)
129 {
130 /^\+$/ && do { $new_val = ($prev_val + $val); last; };
131 /^\-$/ && do { $new_val = ($prev_val - $val); last; };
132 /^\*$/ && do { $new_val = ($prev_val * $val); last; };
133 /^\/$/ && do { $new_val = ($prev_val / $val); last; };
134
135 die "Unknown operator '$op' in arithmetic resolve\n";
136 }
137
138 return $new_val if defined $new_val;
139 return;
140}
141
142sub enter_scope
143{
144 my $self = shift;
145 my ($obj) = @_;
146
147 push @{$self->{STACK}}, $obj;
148
149 for my $key (keys %isAbsolute)
150 {
151 next unless exists $obj->{$key};
152 $self->{$key} = $self->resolve($obj, $key);
153 }
154
8c63e224 155 return ~~1;
d0eafc11 156}
157
158sub exit_scope
159{
160 my $self = shift;
161 my ($obj, $no_delta) = @_;
162
163 unless ($no_delta)
164 {
165 my $deltas = $obj->deltas($self);
166 $self->{$_} += $deltas->{$_} for keys %$deltas;
167 }
168
169 pop @{$self->{STACK}};
170
8c63e224 171 return ~~1;
d0eafc11 172}
173
174sub get
175{
176 my $self = shift;
177 my ($dummy, $key, $depth) = @_;
178 $depth ||= 0;
179 $key = uc $key;
180
181 return unless @{$self->{STACK}};
182
183 my $obj = $self->{STACK}[-1];
184
185 return $self->{$key} if $isAbsolute{$key};
186
187 my $val = undef;
188 my $this_depth = $depth;
189 foreach my $e (reverse @{$self->{STACK}})
190 {
191 next unless exists $e->{$key};
192 next if $this_depth-- > 0;
193
194 $val = $self->resolve($e, $key, $depth);
195 last;
196 }
197
198 $val = $self->{$key} unless defined $val;
199 return $val unless defined $val;
200
201 return $self->param($1, $depth) if $val =~ /^\$(\S+)$/o;
202
203 return $val;
204}
205
206sub active_format
207{
208 my $self = shift;
209
210 $self->{ACTIVE_FORMAT} = $_[0]
211 if @_;
212
213 $self->{ACTIVE_FORMAT};
214}
215
216sub new_worksheet
217{
218 my $self = shift;
37513eae 219 my ($worksheet) = @_;
d0eafc11 220
221 $self->{ROW} = $self->{COL} = 0;
37513eae 222 $self->{REFERENCES} = {};
223
224 my $name = $self->get( $worksheet, 'NAME' );
225
226 if ( defined $name && length $name )
227 {
228 if ( exists $self->{WORKSHEET_NAMES}{$name} )
229 {
230 $name = '';
231 }
232 else
233 {
234 $self->{WORKSHEET_NAMES}{$name} = undef;
235 }
236 }
237 else
238 {
239 $name = '';
240 }
d0eafc11 241
8fd01531 242 return $self->active_worksheet(
37513eae 243 $self->{XLS}->add_worksheet( $name ),
d0eafc11 244 );
245}
246
8fd01531 247sub mark
248{
249 my $self = shift;
250
251 if ( @_ > 1 )
252 {
253 my %args = @_;
254
255 @{$self->{__MARKS}}{keys %args} = values %args;
256 }
257
258 return $self->{__MARKS}{$_[0]}
259}
260
d0eafc11 261sub active_worksheet
262{
263 my $self = shift;
264
265 $self->{ACTIVE_WORKSHEET} = $_[0]
266 if @_;
267
268 $self->{ACTIVE_WORKSHEET};
269}
270
37513eae 271sub add_reference
272{
273 my $self = shift;
274 my ($ref, $row, $col) = @_;
275
276 $self->{REFERENCES}{$ref} ||= [];
277
278 push @{$self->{REFERENCES}{$ref}}, [ $row, $col ];
279
8c63e224 280 return ~~1;
37513eae 281}
282
283sub get_all_references
284{
285 my $self = shift;
286 my $ref = uc shift;
287
288 $self->{REFERENCES}{$ref} ||= [];
289
290 return @{ $self->{REFERENCES}{$ref} };
291}
292
293sub get_last_reference
294{
295 my $self = shift;
296 my $ref = uc shift;
297
298 $self->{REFERENCES}{$ref} ||= [];
299
300 return @{ $self->{REFERENCES}{$ref}[-1] };
301}
302
d0eafc11 3031;
304__END__
305
306=head1 NAME
307
308Excel::Template::Context
309
310=head1 PURPOSE
311
c11fa570 312This is a helper node that provides the global context for the nodes do their processing within. It provides attribute scoping, parameter resolution, and other very nice things.
313
314Documentation is provided for if you wish to subclass another node.
315
d0eafc11 316=head1 NODE NAME
317
c11fa570 318None
319
d0eafc11 320=head1 INHERITANCE
321
c11fa570 322None
323
d0eafc11 324=head1 ATTRIBUTES
325
c11fa570 326None
327
d0eafc11 328=head1 CHILDREN
329
c11fa570 330None
331
d0eafc11 332=head1 AFFECTS
333
c11fa570 334Everything
335
d0eafc11 336=head1 DEPENDENCIES
337
c11fa570 338None
339
340=head1 METHODS
341
342=head2 active_format
343
344=head2 active_worksheet
345
346=head2 add_reference
347
348=head2 get
349
350=head2 get_all_references
351
352=head2 get_last_reference
353
8fd01531 354=head2 mark
c11fa570 355
356=head2 new_worksheet
357
358=head2 param
359
360=head2 use_unicode
d0eafc11 361
362=head1 AUTHOR
363
c09684ff 364Rob Kinyon (rob.kinyon@gmail.com)
d0eafc11 365
366=head1 SEE ALSO
367
368=cut