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