File Coverage

blib/lib/Text/PORE/Node/Container.pm
Criterion Covered Total %
statement 71 110 64.5
branch 11 36 30.5
condition 2 6 33.3
subroutine 9 10 90.0
pod 0 7 0.0
total 93 169 55.0


line stmt bran cond sub pod time code
1             # ContainerTagNode --
2             # tag_type (scalar): type of tag
3             # pairs (hash): attribute-value pairs
4             # body (array ref): template enclosed within tags (Node stack)
5             package Text::PORE::Node::Container;
6            
7 1     1   5 use Text::PORE::Node::Attr;
  1         2  
  1         25  
8 1     1   732 use Text::PORE::Table;
  1         3  
  1         30  
9 1     1   7 use strict;
  1         3  
  1         1636  
10            
11             @Text::PORE::Node::Container::ISA = qw(Text::PORE::Node::Attr);
12            
13             my %ContainerFunctions = (
14             'list' => 'ListTagFunc',
15             'context' => 'ContextTagFunc',
16             'link' => 'LinkTagFunc',
17             );
18            
19             sub new {
20 3     3 0 6 my $type = shift;
21 3         6 my $lineno = shift;
22 3         5 my $tag_type = shift;
23 3         7 my $pairs = shift;
24 3         5 my $body = shift;
25            
26 3   33     20 my $self = bless {}, ref($type) || $type;
27            
28 3         18 $self = $self->SUPER::new($lineno, $tag_type, $pairs);
29            
30 3         9 $self->{'body'} = $body;
31            
32 3   33     19 bless $self, ref($type) || $type;
33             }
34            
35             sub setBody {
36 3     3 0 288 my $self = shift;
37 3         7 my $body = shift;
38            
39 3         12 $self->{'body'} = $body;
40             }
41            
42             sub traverse {
43 3     3 0 7 my $self = shift;
44 3         6 my $globals = shift;
45            
46 3 50       17 $self->output("[$self->{'tag_type'}:$self->{'lineno'}]")
47             if $self->getDebug;
48            
49             # lookup method name
50 3         13 my ($method) = $ContainerFunctions{$self->{'tag_type'}};
51            
52             # execute that method, collect it's errors
53 3 50       8 if ($method) {
54 3         15 $self->error($self->$method($globals));
55             } else {
56 0         0 $self->error("Unsupported tag [$self->{'tag_type'}]");
57             }
58            
59 3         10 $self->errorDump();
60             }
61            
62             sub ListTagFunc {
63 2     2 0 4 my $self = shift;
64 2         6 my $globals = shift;
65            
66 2         6 my $body = $self->{'body'};
67            
68 2         6 my ($attr) = $self->{'attrs'}{'attr'};
69 2         9 my (@range) = $self->DetermineRange();;
70 2         8 my ($objects) = $self->retrieveSlot($globals, $attr);
71            
72 2         6 my ($index_name) = $self->{'attrs'}{'index'};
73 2         4 my ($index_tmp);
74             my ($index);
75            
76 0         0 my ($context_tmp);
77            
78 2 50       52 if (ref($objects) !~ /ARRAY/) {
79 0         0 $self->error("The attribute '$attr' of current object " .
80             "is not a list.");
81 0         0 return $self->errorDump();
82             }
83            
84             # quit if we don't have a list of objects
85 2 50       11 unless (scalar @$objects) {
86 0         0 $self->error("Attempt to loop over empty list");
87 0         0 return $self->errorDump();
88             }
89            
90             # set up the range over which to loop, default is everything
91 2 50       6 unless (scalar @range) {
92 2         9 @range = 0 .. $#$objects;
93             }
94            
95             # if they want to use an index variable, set it up
96 2 50       103 if (defined $index_name) {
97             # inform them if they will have a naming conflict
98             # note that they can redefine index variables as many times as
99             # they want, and this code will store them all due to the call
100             # stack
101 0 0       0 if (defined $globals->{'_index'}->GetAttribute($index_name)) {
102 0         0 $self->error("Temporary redefinition of index variable ".
103             "'$index_name'");
104             }
105 0         0 $index_tmp = $globals->{'_index'}->GetAttribute($index_name);
106             }
107            
108             # store the current context to be restored later
109 2         8 $context_tmp = $globals->GetAttribute('_context');
110            
111             # loop over each index specified
112 2         5 foreach $index (@range) {
113            
114             # complain about indexes that are out of range, and skip them
115 8 50       21 if ($index > $#$objects) {
116 0         0 $self->error("Subscript ". $index + 1 ." out of range, ".
117             $#$objects + 1 . " max");
118 0         0 next;
119             }
120            
121             # update their index variable, if they have one
122             # note that we have to add 1 to it
123 8 50       13 if (defined $index_name) {
124 0         0 $globals->{'_index'}->LoadAttributes($index_name, $index + 1);
125             }
126            
127             # process the body of the tag
128             # note that this passes all previously defined indicies
129             # TODO - should check $objects[$index]->isa(Text::PORE::Object)
130 8         28 $globals->LoadAttributes('_context' => $objects->[$index]);
131 8         24 $self->error($body->traverse($globals));
132             # TODO - should check for errors on return
133             }
134            
135             # restore the original context
136 2         24 $globals->LoadAttributes('_context', $context_tmp);
137            
138             # restore any previously held value of their index variable.
139             # note that if it was not defined before, this will not define it
140             # (which is what we want)
141 2 50       7 if (defined $index_name) {
142 0         0 $globals->{'_index'}->LoadAttributes($index_name, $index_tmp);
143             }
144            
145 2         7 return $self->errorDump();
146             }
147            
148             # ContextTagFunc: changes context of object to given attribute of current
149             # context object
150             # tag:
151             sub ContextTagFunc {
152 1     1 0 4 my $self = shift;
153 1         3 my $globals = shift;
154            
155 1         3 my $body = $self->{'body'};
156 1         4 my %attr = %{$self->{'attrs'}};
  1         7  
157            
158 1         64 my $context;
159             my $context_tmp;
160            
161 1         4 my ($attr_name) = $attr{'attr'};
162 1         8 $context = $self->retrieveSlot($globals, $attr_name);
163            
164             # TODO - same as in ListTagFunc
165 1 50       5 if (! $context) {
166 0         0 $self->error("Current object [$context] has no '$attr_name' attribute");
167 0         0 return $self->errorDump();
168             }
169             # TODO - same as in ListTagFunc
170 1 50       3 if (! ref($context)) {
171 0         0 $self->error("The attribute '$attr_name' of object $context is not an object.");
172 0         0 return $self->errorDump();
173             }
174            
175 1         6 $context_tmp = $globals->GetAttribute('_context');
176 1         4 $globals->LoadAttributes('_context' => $context);
177 1         6 $self->error($body->traverse($globals));
178 1         6 $globals->LoadAttributes('_context' => $context_tmp);
179            
180 1         9 return $self->errorDump();
181             }
182            
183             # LinkTagFunc: outputs an HREF link to the attribute of the current object.
184             # Returns an error if this attribute is not itself an object.
185             # tag:
186             sub LinkTagFunc {
187 0     0 0 0 my $self = shift;
188 0         0 my $globals = shift;
189            
190 0         0 my $body = $self->{'body'};
191 0         0 my %attr = $self->{'attrs'};
192            
193 0         0 my ($attr_name) = $attr{'attr'};
194 0         0 my ($object) = $self->retrieveSlot($globals, $attr_name);
195            
196 0 0       0 if (! $object) {
    0          
197 0         0 $self->error("Current object has no '$attr_name' attribute");
198             }
199             elsif (! ref($object)) {
200 0         0 $self->error("The attribute '$attr_name' of current object ".
201             "is not an object.");
202             }
203             else {
204 0         0 $self->output('');
205            
206 0         0 $self->error($body->traverse($globals));
207            
208 0         0 $self->output('');
209             }
210            
211 0         0 return $self->errorDump();
212             }
213            
214             sub DetermineRange {
215 2     2 0 3 my $self = shift;
216 2         6 my $tmp = $self->{'attrs'}{'range'};
217 2         3 my @list;
218            
219 2         4 $_ = $tmp;
220 2         6 while ($_) {
221 0         0 s/^\s*,?\s*//;
222            
223             # Note: we must subtract from indecies to compensate for
224             # differences in array first element (0 or 1)
225 0 0       0 s/^(\d+)\s*-\s*(\d+)// && do {
226 0 0       0 push (@list, ($1<$2) ? $1-1..$2-1 : reverse $2-1..$1-1);
227 0         0 redo;
228             };
229 0 0       0 s/^(\d+)// && do {
230 0         0 push (@list, $1-1);
231 0         0 redo;
232             };
233 0 0       0 s/^(\D+)// && do {
234 0         0 $self->error("Bad range spec '$1'");
235             };
236             }
237            
238 2         7 @list;
239             }
240            
241             1;