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