File Coverage

blib/lib/XML/Template/Vars.pm
Criterion Covered Total %
statement 3 3 100.0
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 4 4 100.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             # XML::Template::Vars
3             #
4             # Copyright (c) 2002-2003 Jonathan A. Waxman
5             # All rights reserved.
6             #
7             # This program is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl itself.
9             ###############################################################################
10             package XML::Template::Vars;
11 1     1   22662 use base qw(XML::Template::Base);
  1         2  
  1         671  
12              
13             use strict;
14             use XML::Template::Config;
15             use Data::Dumper;
16              
17              
18             =pod
19              
20             =head1 NAME
21              
22             XML::Template::Vars - Module for handling XML::Template variables.
23              
24             =head1 SYNOPSIS
25              
26             This module is used for handling the various XML::Template data types and
27             variables. It is used to create and remove variable contexts and get,
28             set, and unset scalar, array, nested, and XPath variables.
29              
30             =head1 CONSTRUCTOR
31              
32             A constructor method C is provided by L. A list
33             of named configuration parameters may be passed to the constructor. The
34             constructor returns a reference to a new parser object or under if an
35             error occurred. If undef is returned, you can use the method C to
36             retrieve the error. For instance:
37              
38             my $parser = XML::Template::Vars->new (%config)
39             || die XML::Template::Vars->error;
40              
41             =head1 PRIVATE METHODS
42              
43             =head2 _init
44              
45             This method is the internal initialization function called from
46             L when a new vars object is created.
47              
48             =cut
49              
50             sub _init {
51             my $self = shift;
52             my %params = @_;
53              
54             print ref ($self) . "->_init\n" if $self->{_debug};
55              
56             $self->{_contexts} = [];
57             $self->{_xpath_objs} = {};
58              
59             # Create global context.
60             $self->create_context ();
61              
62             # Add configuration XPath variable to global context.
63             $self->set (Config => '');
64             $self->{_xpath_objs}->{Config} = XML::Template::Config->config ();
65              
66             return 1;
67             }
68              
69             =pod
70              
71             =head2 _set
72              
73             $self->_set ($context, %vars);
74              
75             This is the internal method for setting variables. The first parameter
76             specifies the variable context in which the variables will be set. The
77             remaining parameters comprise a hash of variable name/value pairs to
78             set. The variable names are in the format of actual XML::Template
79             variable names (e.g., C).
80              
81             =cut
82              
83             sub _set {
84             my $self = shift;
85             my ($context, %vars) = @_;
86              
87             # Set the given variables.
88             while (my ($var, $value) = each (%vars)) {
89             my $hash = $context;
90             my $i = 1;
91              
92             my @varparts = split (/(?
93             @varparts = map { $_ =~ s/\\\./\./g; $_ } @varparts;
94             foreach my $varpart (@varparts) {
95             my $index;
96             if ($varpart =~ /\[\d+\]$/) {
97             $varpart =~ s/\[(\d+)\]$//;
98             $index = $1;
99             }
100              
101             if ($i == scalar (@varparts)) {
102             if (defined $index) {
103             $hash->{$varpart}->[$index] = $value;
104             } else {
105             $hash->{$varpart} = $value;
106             }
107             } else {
108             if (defined $hash->{$varpart}) {
109             if (defined $index) {
110             $hash = $hash->{$varpart}->[$index];
111             } else {
112             $hash = $hash->{$varpart};
113             }
114             } else {
115             if (defined $index) {
116             $hash = $hash->{$varpart}->[$index] = {};
117             } else {
118             $hash = $hash->{$varpart} = {};
119             }
120             }
121             }
122             $i++;
123             }
124              
125             # Remove any cached XPath object for this variable.
126             $var =~ /^([^\/]+)/;
127             delete $self->{_xpath_objs}->{$1};
128             }
129              
130             return 1;
131             }
132              
133             =pod
134              
135             =head2 _unset
136              
137             $self->_unset ($context, @varparts);
138              
139             This method is the internal method for unsetting (deleting) variables.
140             The first parameter is the context in which to remove variables. The
141             remaining parameters are the individual parts of a nested variable. For
142             instance to remove the variable C, do
143              
144             $self->_unset ($context, 'hash1', 'hash2', 'varname');
145              
146             =cut
147              
148             sub _unset {
149             my $self = shift;
150             my ($hash, @varparts) = @_;
151              
152             my $varpart;
153             while (scalar (@varparts)) {
154             $varpart = shift (@varparts);
155              
156             if (defined $hash->{$varpart}) {
157             last if ref ($hash->{$varpart}) ne 'HASH';
158             $hash = $hash->{$varpart};
159             } else {
160             return;
161             }
162             }
163             delete $hash->{$varpart};
164             }
165              
166             =pod
167              
168             =head2
169              
170             my $value = $self->_get ($var);
171              
172             This method is the internal method for getting variables. The only
173             parameter names the variable to get. The name of the variable is in the
174             format of an XML::Template variable name.
175              
176             =cut
177              
178             sub _get {
179             my $self = shift;
180             my $var = shift;
181              
182             # $var =~ s/'([^"]*)'/backdot ($1)/gem;
183             my @varparts = split (/(?
184             @varparts = map { $_ =~ s/\\\./\./g; $_ } @varparts;
185              
186             # Look for the variable starting at the top of the context stack.
187             my $value;
188             foreach my $context (@{$self->{_contexts}}) {
189             $value = $context;
190             foreach my $tvarpart (@varparts) {
191             my $varpart = $tvarpart; # xxx
192             my ($index, $xpath);
193             if ($varpart =~ m[(?
194             $varpart =~ s[(?
195             $xpath = "/$1";
196             }
197             # $varpart =~ s/^'//; $varpart =~ s/'$//;
198             $varpart =~ s[\\/][/]g;
199             if ($varpart =~ /\[\d+\]$/) {
200             $varpart =~ s/\[(\d+)\]$//;
201             $index = $1;
202             }
203             if (exists $value->{$varpart}) {
204             if (defined $index) {
205             $value = $value->{$varpart}->[$index];
206             } else {
207             $value = $value->{$varpart};
208             }
209             if (defined $xpath && defined $value) {
210             # Get the XPath object from the cache or create a new one.
211             my $xp;
212             if (ref ($value) =~ /^XML::GDOME/) {
213             $xp = $value;
214             $xpath =~ s[^/][]; # Relativize xpath statement.
215             } else {
216             $var =~ /^([^\/]+)/;
217             my $fullvar = $1;
218              
219             if (exists $self->{_xpath_objs}->{$fullvar}) {
220             $xp = $self->{_xpath_objs}->{$fullvar};
221             } else {
222             my $parser = XML::GDOME->new ();
223             $xp = $parser->parse_string ($value);
224             $self->{_xpath_objs}->{$fullvar} = $xp;
225             }
226             }
227              
228             my @nodes = $xp->findnodes ($xpath);
229             $value = scalar (@nodes) > 1 ? \@nodes : $nodes[0];
230             }
231             } else {
232             undef $value;
233             last;
234             }
235             }
236             last if defined $value;
237             }
238              
239             return ($value);
240             }
241              
242             =pod
243              
244             =head1 PUBLIC METHODS
245              
246             =head2 create_context
247              
248             $vars->create_context ();
249              
250             This method creates a new variable context. Any variables added to this
251             context will shadow variables with the same name in previous contexts.
252              
253             =cut
254              
255             sub create_context {
256             my $self = shift;
257              
258             # Push a new context onto the context stack.
259             my %context = ();
260             unshift (@{$self->{_contexts}}, \%context);
261              
262             return (\%context);
263             }
264              
265             =pod
266              
267             =head2 delete_context
268              
269             $vars->delete_context ()
270              
271             This method deletes the current variable context.
272              
273             =cut
274              
275             sub delete_context {
276             my $self = shift;
277              
278             # Pop the context stack.
279             my $context = shift (@{$self->{_contexts}});
280              
281             return $context;
282             };
283              
284             =pod
285              
286             =head2 set
287              
288             $vars->set ('hash.varname[2]' => 'blah', 'varname2' => 'ick');
289              
290             This method is used to set variables in the current variable context.
291             The parameters comprise a hash of variable name/value pairs. The variable
292             names are in the format of actual XML::Template variable names.
293              
294             =cut
295              
296             sub set {
297             my $self = shift;
298             my %vars = @_;
299              
300             # Get the current context, or create one if there are none.
301             $self->_set ($self->{_contexts}->[0], %vars);
302              
303             return 1;
304             }
305              
306             =pod
307              
308             =head2 set_global
309              
310             $vars->set_global ('hash.varname[2]' => 'blah', varname2 => 'ick');
311              
312             This method sets global variables by setting them in the topmost variable
313             contest. The parameters comprise a hash containing variable name/value
314             pairs to set. The variable names are in the format of actual
315             XML::Template variable names.
316              
317             =cut
318              
319             sub set_global {
320             my $self = shift;
321             my %vars = @_;
322              
323             my $top = scalar (@{$self->{_contexts}});
324             my $context = $self->{_contexts}->[$top - 1];
325              
326             $self->_set ($context, %vars);
327              
328             return 1;
329             }
330              
331             =pod
332              
333             =head2 unset
334              
335             $vars->unset ('hash.varname', 'varname2');
336              
337             This method unsets (deletes) variables in the current variable context.
338             The parameters comprise an array containing the names of variables to
339             delete. The variable names are in the format of actual XML::Template
340             variable names.
341              
342             =cut
343              
344             sub unset {
345             my $self = shift;
346             my @vars = @_;
347              
348             foreach my $var (@vars) {
349             foreach my $context (@{$self->{_contexts}}) {
350             $self->_unset ($context, split ('\.', $var));
351             }
352             }
353              
354             return ('');
355             }
356              
357             =pod
358              
359             =head2 get
360              
361             my $value = $vars->get ('varname');
362             my @values = $vars->get ('hash.varname[2]/xpath', 'varname2');
363              
364             This method is used to get variable values. The parameters comprise an
365             array of names of variables to get. The variable names are in the format
366             of actual XML::Template variable names.
367              
368             =cut
369              
370             sub get {
371             my $self = shift;
372             my @vars = @_;
373              
374             my @values;
375              
376             foreach my $var (@vars) {
377             my $value = $self->_get ($var);
378             push (@values, $value);
379             }
380              
381             if (wantarray) {
382             return (@values);
383             } else {
384             # This is necessary to return undef properly.
385             if (scalar (@values) == 1) {
386             return ($values[0]);
387             } else {
388             return (join (',', @values));
389             }
390             }
391             }
392              
393             =pod
394              
395             =head2 get
396              
397             my $value = $vars->get ('varname');
398             my @values = $vars->get ('hash.varname[2]/xpath', 'varname2');
399              
400             Like C, this method is used to get variable values. However, this
401             method is XPath aware. That is, if a value is a GDOME object, it will be
402             converted to text. Currently, only GDOME is supported. I need to
403             implement a way to handle arbitrary XML parsers. The parameters comprise
404             an array of names of variables to get. The variable names are in the
405             format of actual XML::Template variable names.
406              
407             =cut
408              
409             sub get_xpath {
410             my $self = shift;
411              
412             if (wantarray) {
413             return $self->get (@_);
414             } else {
415             my $value = $self->get (@_);
416             if (ref ($value) =~ /^XML::GDOME/) {
417             if (ref ($value) eq 'XML::GDOME::Attr') {
418             $value = $value->string_value ();
419             } else {
420             $value = $value->toString ();
421             }
422             }
423             return $value;
424             }
425             }
426              
427             sub backslash {
428             my $self = shift;
429             my ($patt, $text) = @_;
430              
431             $text =~ s/(?
432             return $text;
433             }
434              
435             sub push {
436             my $self = shift;
437             my %vars = @_;
438              
439             while (my ($var, $push_values) = each (%vars)) {
440             my $value = $self->_get ($var);
441             if (ref ($value) eq 'ARRAY') {
442             CORE::push (@$value, @$push_values);
443             } else {
444             $self->set ($var => $push_values);
445             }
446             }
447              
448             return '';
449             }
450              
451             sub pop {
452             my $self = shift;
453             my $var = shift;
454              
455             my $value = $self->_get ($var);
456             if (ref ($value) eq 'ARRAY') {
457             return (CORE::pop (@$value));
458             } else {
459             return '';
460             }
461             }
462              
463             sub dump {
464             my $self = shift;
465              
466             my $i = 0;
467             foreach my $context (@{$self->{_contexts}}) {
468             print "Context $i:
\n";
469             while (my ($var, $value) = each (%$context)) {
470             print "$var: " . Dumper ($value);
471             }
472             $i++;
473             }
474             }
475              
476             =pod
477              
478             =head1 AUTHOR
479              
480             Jonathan Waxman
481             jowaxman@bbl.med.upenn.edu
482              
483             =head1 COPYRIGHT
484              
485             Copyright (c) 2002-2003 Jonathan A. Waxman
486             All rights reserved.
487              
488             This program is free software; you can redistribute it and/or
489             modify it under the same terms as Perl itself.
490              
491             =cut
492              
493              
494             1;