File Coverage

blib/lib/Parse/Plain.pm
Criterion Covered Total %
statement 332 492 67.4
branch 110 226 48.6
condition 6 12 50.0
subroutine 27 35 77.1
pod 27 27 100.0
total 502 792 63.3


line stmt bran cond sub pod time code
1             package Parse::Plain;
2              
3             require 5.005;
4 1     1   51542 use strict;
  1         2  
  1         36  
5              
6              
7             BEGIN
8             {
9 1     1   6 use Exporter;
  1         2  
  1         45  
10 1     1   5 use Carp;
  1         6  
  1         87  
11 1     1   5 use vars qw( $VERSION $lcnt_max $ssec );
  1         25  
  1         201  
12              
13 1     1   31915 $VERSION = "3.03";
14             }
15              
16              
17             # constructor
18             # [I] $template (mandatory): template filename
19             # $lcnt_max (optional) : number of attempts to open file
20             # $s_sec (optional) : number of seconds to sleep between
21             # attemts if file can't be opened
22             sub new
23             {
24 4     4 1 887 my $type = shift;
25 4         5 my ($template, $lcnt, @lines, $line, $block, $block_open,
26             $s_block, @bl_stack, @bl_name_stack);
27 4         8 my $self = {};
28              
29 4         9 ($template, $lcnt_max, $ssec) = @_;
30              
31 4         12 $self->{'text'} = ''; # input
32 4         9 $self->{'hparse'} = {}; # hash of tags - values
33 4         6 $self->{'gparse'} = {}; # hash of global tags - values
34 4         9 $self->{'hblock'} = {}; # hash of blocks
35 4         6 $self->{'oblock'} = {}; # original values of blocks
36 4         21 $self->{'cback'} = {}; # callback references
37 4         6 $self->{'parsed'} = undef; # output
38            
39 4 50 33     14 if ((defined $lcnt_max) && ($lcnt_max !~ /^\d+$/)) {
40 0         0 &_my_error('$lcnt_max must be number');
41             }
42 4 50       10 $lcnt_max = 5 unless ($lcnt_max);
43              
44 4 50 33     33 if ((defined $lcnt_max) && ($lcnt_max !~ /^\d+$/)) {
45 0         0 &_my_error('$ssec must be number');
46             }
47 4 50       9 $ssec = 1 unless ($ssec);
48            
49 4         5 @lines = @{&_load_file($template)};
  4         14  
50              
51 4         14 $block = \$self->{'text'};
52 4         6 $block_open = '';
53 4         7 foreach $line(@lines) {
54 59 100       142 if ($line =~ m/^\s*{{\s*([\!\w\d\.-_]+)$/) {
55 8 100       21 push @bl_name_stack, $block_open
56             if ($block_open);
57              
58 8 100       26 if (substr($1, 0, 1) eq '!') {
59 1         2 $s_block = 1;
60 1         3 $block_open = substr($1, 1);
61             } else {
62 7         9 $s_block = 0;
63 7         12 $block_open = $1;
64             }
65              
66 8 100       21 chomp $$block if ($$block);
67 8 100       24 $$block .= ('%%!' . $block_open . '%%')
68             unless ($s_block);
69 8         11 push @bl_stack, $block;
70 8         701 $block = \$self->{'hblock'}->{$block_open};
71 8         14 next;
72             }
73 51 100 66     139 if (($line =~ m/^\s*}}(.*)$/) && $block_open) {
74 8 100 66     34 chomp $$block if ((!$1) && ($$block));
75 8         12 $block = pop @bl_stack;
76 8         13 $block_open = pop @bl_name_stack;
77 8 100       23 $line = ($1 ? $1 . "\n" : '');
78 8         9 redo;
79             }
80 43         73 $$block .= $line;
81             }
82            
83 4 50       10 if ($block_open) {
84 0         0 &_my_error("in $template: block not closed");
85             }
86            
87 4         6 foreach (keys(%{$self->{'hblock'}})) {
  4         14  
88 8         34 $self->{'oblock'}->{$_} = $self->{'hblock'}->{$_};
89             }
90            
91 4         14 $self->{'cback'}->{'INCLUDE'} = \&_include_file;
92            
93 4         23 return bless $self, $type;
94             }
95              
96              
97             # set tags in %hparse
98             # [I] either ($tag, $val) pair or $hash_ref containing { $tag => $val } pairs
99             # [O] hash_ref containing { $tagname => $new_value, ... }
100             sub set_tag
101             {
102 8     8 1 591 my $self = shift;
103 8         12 my ($tag, $val, $res);
104            
105 8 50       18 unless ($_[0]) {
106 0         0 &_my_error('required parameter missed');
107             }
108            
109 8         10 $res = {};
110              
111 8 100       25 if (ref($_[0]) eq 'HASH') {
    50          
112 1         2 foreach $tag(keys(%{$_[0]})) {
  1         4  
113 3         4 $val = $_[0]->{$tag};
114            
115 3 50       13 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
116 0         0 $self->{'hparse'}->{$tag} = $val->parse;
117             } else {
118 3         6 $self->{'hparse'}->{$tag} = $val;
119             }
120 3         8 $res->{$tag} = $self->{'hparse'}->{$tag};
121             }
122             } elsif (!ref($_[0])) {
123 7         13 ($tag, $val) = @_;
124            
125 7 100       29 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
126 1         3 $self->{'hparse'}->{$tag} = $val->parse;
127             } else {
128 6         22 $self->{'hparse'}->{$tag} = $val;
129             }
130 7         17 $res->{$tag} = $self->{'hparse'}->{$tag};
131             } else {
132 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
133             }
134            
135 8         21 return $res;
136             }
137              
138              
139             # retrieve tags from %hparse
140             # [I] @tags or [$tag1, $tag2, ...]
141             # [O] [$val1, $val2, ...]
142             sub get_tag
143             {
144 7     7 1 13 my $self = shift;
145 7         8 my ($res, $key);
146              
147 7 50       21 unless ($_[0]) {
148 0         0 &_my_error('required parameter missed');
149             }
150            
151 7         10 $res = [];
152            
153             # to avoid mess I support either arrayref or list not both mixed!
154 7 100       24 if (ref($_[0]) eq 'ARRAY') {
    50          
155 1         2 foreach $key(@{$_[0]}) {
  1         4  
156 3         8 push @$res, $self->{'hparse'}->{$key};
157             }
158             } elsif (!ref($_[0])) {
159 6         15 while (@_) {
160 8         30 $key = shift;
161 8         27 push @$res, $self->{'hparse'}->{$key};
162             }
163             } else {
164 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
165             }
166              
167 7         33 return $res;
168             }
169              
170              
171             # append values to tags
172             # [I] either ($tag, $val) pair or $hash_ref containing { $tag => $val } pairs
173             # [O] hash_ref with { $tagname => $new_val, ... }
174             sub push_tag
175             {
176 2     2 1 12 my $self = shift;
177 2         3 my ($tag, $val, $res);
178              
179 2 50       5 unless ($_[0]) {
180 0         0 &_my_error('required parameter missed');
181             }
182            
183 2         3 $res = {};
184              
185 2 100       8 if (ref($_[0]) eq 'HASH') {
    50          
186 1         1 foreach $tag(keys(%{$_[0]})) {
  1         3  
187 2         5 $val = $_[0]->{$tag};
188              
189 2 50       9 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
190 0         0 $self->{'hparse'}->{$tag} .= $val->parse;
191             } else {
192 2         4 $self->{'hparse'}->{$tag} .= $val;
193             }
194            
195 2         7 $res->{$tag} = $self->{'hparse'}->{$tag};
196             }
197             } elsif (!ref($_[0])) {
198 1         2 ($tag, $val) = @_;
199            
200 1 50       8 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
201 0         0 $self->{'hparse'}->{$tag} .= $val->parse;
202             } else {
203 1         3 $self->{'hparse'}->{$tag} .= $val;
204             }
205            
206 1         3 $res->{$tag} = $self->{'hparse'}->{$tag};
207             } else {
208 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
209             }
210            
211 2         6 return $res;
212             }
213              
214              
215             # append tags to passed values and store result in tags
216             # [I] either ($tag, $val) pair or $hash_ref containing { $tag => $val } pairs
217             # [O] hash_ref of new values
218             sub unshift_tag
219             {
220 2     2 1 9 my $self = shift;
221 2         2 my ($tag, $val, $res);
222              
223 2 50       5 unless ($_[0]) {
224 0         0 &_my_error('required parameter missed');
225             }
226              
227 2         3 $res = {};
228            
229 2 100       7 if (ref($_[0]) eq 'HASH') {
    50          
230 1         2 foreach $tag(keys(%{$_[0]})) {
  1         3  
231 2         4 $val = $_[0]->{$tag};
232              
233 2 50       12 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
234 0         0 $self->{'hparse'}->{$tag} =
235             $val->parse . $self->{'hparse'}->{$tag};
236             } else {
237 2         5 $self->{'hparse'}->{$tag} =
238             $val . $self->{'hparse'}->{$tag};
239             }
240            
241 2         6 $res->{$tag} = $self->{'hparse'}->{$tag};
242             }
243             } elsif (!ref($_[0])) {
244 1         2 ($tag, $val) = @_;
245              
246 1 50       5 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
247 0         0 $self->{'hparse'}->{$tag} =
248             $val->parse . $self->{'hparse'}->{$tag};
249             } else {
250 1         3 $self->{'hparse'}->{$tag} =
251             $val . $self->{'hparse'}->{$tag};
252             }
253            
254 1         3 $res->{$tag} = $self->{'hparse'}->{$tag};
255             } else {
256 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
257             }
258            
259 2         7 return $res;
260             }
261              
262              
263             # block src/res accessor, required for backwards compatibility with 2.x
264             # if block hasn't been parse()'d yet or has been unparse()'d then
265             # block_src() used else block_res()
266             # [I] scalar blockname to get or list (blockname, val) to set value
267             # [O] same as block_src() / block_res()
268             sub block
269             {
270 5     5 1 9 my $self = shift;
271 5         7 my ($bl);
272              
273 5         7 $bl = $_[0];
274 5 50       13 unless ($bl) {
275 0         0 &_my_error('required parameter missed');
276             }
277            
278 5 100       17 if (defined $self->{'hparse'}->{'!' . $bl}) {
279 4         12 return $self->block_res(@_);
280             } else {
281 1         10 return $self->block_src(@_);
282             }
283            
284 0         0 &_my_error('control flow must never reach here');
285             }
286              
287              
288             # block source accessor
289             # [I] either block name (to get block value)
290             # or array_ref of block names to get their values
291             # or ($block, $val) to set $val to $block
292             # or hash_ref of { $block => $val, ... } pairs
293             # [O] hash_ref with (new) values of blocks
294             sub block_src
295             {
296 7     7 1 14 my $self = shift;
297 7         18 my ($bl, $val, $res, @arr);
298            
299 7         21 @arr = @_;
300 7 50       16 unless ($arr[0]) {
301 0         0 &_my_error('required parameter missed');
302             }
303              
304 7         11 $res = {};
305 7 50       88 if (ref($arr[0]) eq 'ARRAY') { # get block vals from arr_ref
    100          
    50          
306 0         0 foreach $bl(@{$arr[0]}) {
  0         0  
307 0         0 $res->{$bl} = $self->{'hblock'}->{$bl};
308             }
309             } elsif (ref($arr[0]) eq 'HASH') { # set block val from hash_ref
310 1         3 foreach $bl(keys(%{$arr[0]})) {
  1         6  
311 1         3 $val = $arr[0]->{$bl};
312            
313 1 50       14 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
314 0         0 $self->{'hblock'}->{$bl} = $val->parse;
315             } else {
316 1         4 $self->{'hblock'}->{$bl} = $val;
317             }
318            
319 1         4 $res->{$bl} = $self->{'hblock'}->{$bl};
320             }
321             } elsif (!ref($arr[0])) { # no refs, for backwards-compatibility
322 6         8 ($bl, $val) = @arr;
323            
324 6 50       14 if ($val) {
325 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
326 0         0 $self->{'hblock'}->{$bl} = $val->parse;
327             } else {
328 0         0 $self->{'hblock'}->{$bl} = $val;
329             }
330             }
331            
332 6         15 $res->{$bl} = $self->{'hblock'}->{$bl};
333             } else {
334 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
335             }
336            
337 7         33 return $res;
338             }
339              
340              
341             # block result accessor
342             # [I] either block name (to get block value)
343             # or array_ref of block names to get their values
344             # or ($block, $val) to set $val to $block
345             # or hash_ref of { $block => $val, ... } pairs
346             # [O] hash_ref with (new) values of blocks
347             sub block_res
348             {
349 7     7 1 20 my $self = shift;
350 7         8 my ($bl, $blf, $val, $res, @arr);
351            
352 7         15 @arr = @_;
353 7 50       16 unless ($arr[0]) {
354 0         0 &_my_error('required parameter missed');
355             }
356              
357 7         11 $res = {};
358            
359 7 50       31 if (ref($arr[0]) eq 'ARRAY') { # get block vals from arr_ref
    50          
    50          
360 0         0 foreach $bl(@{$arr[0]}) {
  0         0  
361 0         0 $blf = '!' . $bl;
362            
363 0 0       0 if (defined $self->{'hparse'}->{$blf}) {
364 0         0 $res->{$bl} = $self->{'hparse'}->{$blf};
365             } else {
366 0         0 $res->{$bl} = undef;
367             }
368             }
369             } elsif (ref($arr[0]) eq 'HASH') { # set block val from hash_ref
370 0         0 foreach $bl(keys(%{$arr[0]})) {
  0         0  
371 0         0 $val = $arr[0]->{$bl};
372 0         0 $blf = '!' . $bl;
373            
374 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
375 0         0 $self->{'hparse'}->{$blf} = $val->parse;
376             } else {
377 0         0 $self->{'hparse'}->{$blf} = $val;
378             }
379            
380 0         0 $res->{$bl} = $self->{'hparse'}->{$blf};
381             }
382             } elsif (!ref($arr[0])) { # no refs, for backwards-compatibility
383 7         9 ($bl, $val) = @arr;
384 7         12 $blf = '!' . $bl;
385            
386 7 50       17 if ($val) {
387 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
388 0         0 $self->{'hparse'}->{$blf} = $val->parse;
389             } else {
390 0         0 $self->{'hparse'}->{$blf} = $val;
391             }
392             }
393            
394 7 100       21 if (defined $self->{'hparse'}->{$blf}) {
395 5         16 $res->{$bl} = $self->{'hparse'}->{$blf};
396             } else {
397 2         6 $res->{$bl} = undef;
398             }
399             } else {
400 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
401             }
402            
403 7         68 return $res;
404             }
405              
406              
407             # append values to src / res blocks
408             # required for backwards compatibility with 2.x
409             # if block hasn't been parse()'d yet or has been unparse()'d then
410             # push_block_src() used else push_block_res()
411             # [I] list (blockname, val)
412             # [O] same as push_block_src() / push_block_res()
413             sub push_block
414             {
415 0     0 1 0 my $self = shift;
416 0         0 my ($bl);
417              
418 0         0 $bl = $_[0];
419 0 0       0 unless ($bl) {
420 0         0 &_my_error('required parameter missed');
421             }
422            
423 0 0       0 if (defined $self->{'hparse'}->{'!' . $bl}) {
424 0         0 return $self->push_block_res(@_);
425             } else {
426 0         0 return $self->push_block_src(@_);
427             }
428            
429 0         0 &_my_error('control flow must never reach here');
430             }
431              
432              
433             # append values to blocks sources
434             # [I] either ($block, $val) or $hash_ref with { $block => $val, ... } pairs
435             # [O] hash_ref of new values
436             sub push_block_src
437             {
438 1     1 1 2 my $self = shift;
439 1         1 my ($block, $val, $res);
440              
441 1 50       4 unless ($_[0]) {
442 0         0 &_my_error('required parameter missed');
443             }
444            
445 1         2 $res = {};
446              
447 1 50       4 if (ref($_[0]) eq 'HASH') {
    0          
448 1         2 foreach $block(keys(%{$_[0]})) {
  1         4  
449 1         2 $val = $_[0]->{$block};
450            
451 1 50       6 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
452 0         0 $self->{'hblock'}->{$block} .= $val->parse;
453             } else {
454 1         3 $self->{'hblock'}->{$block} .= $val;
455             }
456            
457 1         4 $res->{$block} = $self->{'hblock'}->{$block};
458             }
459             } elsif (!ref($_[0])) {
460 0         0 ($block, $val) = @_;
461            
462 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
463 0         0 $self->{'hblock'}->{$block} .= $val->parse;
464             } else {
465 0         0 $self->{'hblock'}->{$block} .= $val;
466             }
467            
468 0         0 $res->{$block} = $self->{'hblock'}->{$block};
469             } else {
470 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
471             }
472            
473 1         3 return $res;
474             }
475              
476              
477             # append values to blocks results
478             # [I] either ($block, $val) or $hash_ref with { $block => $val, ... } pairs
479             # [O] hash_ref of new values
480             sub push_block_res
481             {
482 1     1 1 2 my $self = shift;
483 1         2 my ($block, $blockf, $val, $res);
484              
485 1 50       4 unless ($_[0]) {
486 0         0 &_my_error('required parameter missed');
487             }
488            
489 1         3 $res = {};
490              
491 1 50       5 if (ref($_[0]) eq 'HASH') {
    0          
492 1         2 foreach $block(keys(%{$_[0]})) {
  1         5  
493 1         2 $val = $_[0]->{$block};
494 1         2 $blockf = '!' . $block;
495            
496 1 50       7 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
497 0         0 $self->{'hparse'}->{$blockf} .= $val->parse;
498             } else {
499 1         6 $self->{'hparse'}->{$blockf} .= $val;
500             }
501            
502 1         5 $res->{$block} = $self->{'hparse'}->{$blockf};
503             }
504             } elsif (!ref($_[0])) {
505 0         0 ($block, $val) = @_;
506 0         0 $blockf = '!' . $block;
507            
508 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
509 0         0 $self->{'hparse'}->{$blockf} .= $val->parse;
510             } else {
511 0         0 $self->{'hparse'}->{$blockf} .= $val;
512             }
513            
514 0         0 $res->{$block} = $self->{'hparse'}->{$blockf};
515             } else {
516 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
517             }
518            
519 1         4 return $res;
520             }
521              
522              
523             # push values to src / res blocks
524             # required for backwards compatibility with 2.x
525             # if block hasn't been parse()'d yet or has been unparse()'d then
526             # unshift_block_src() used else unshift_block_res()
527             # [I] list (blockname, val)
528             # [O] same as unshift_block_src() / unshift_block_res()
529             sub unshift_block
530             {
531 0     0 1 0 my $self = shift;
532 0         0 my ($bl);
533              
534 0         0 $bl = $_[0];
535 0 0       0 unless ($bl) {
536 0         0 &_my_error('required parameter missed');
537             }
538            
539 0 0       0 if (defined $self->{'hparse'}->{'!' . $bl}) {
540 0         0 return $self->unshift_block_res(@_);
541             } else {
542 0         0 return $self->unshift_block_src(@_);
543             }
544            
545 0         0 &_my_error('control flow must never reach here');
546             }
547              
548              
549             # append block(s) sources to passed values and store
550             # result back into blocks sources
551             # [I] either ($block, $val) or $hash_ref with { $block => $val, ... } pairs
552             # [O] if hash_ref was passed then hash_ref of new values else just new value
553             sub unshift_block_src
554             {
555 1     1 1 2 my $self = shift;
556 1         2 my ($block, $val, $res);
557              
558 1 50       3 unless ($_[0]) {
559 0         0 &_my_error('required parameter missed');
560             }
561            
562 1         2 $res = {};
563            
564 1 50       5 if (ref($_[0]) eq 'HASH') {
    0          
565 1         1 foreach $block(keys(%{$_[0]})) {
  1         4  
566 1         2 $val = $_[0]->{$block};
567            
568 1 50       6 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
569 0         0 $self->{'hblock'}->{$block} =
570             $val->parse . $self->{'hblock'}->{$block};
571             } else {
572 1         4 $self->{'hblock'}->{$block} =
573             $val . $self->{'hblock'}->{$block};
574             }
575            
576 1         4 $res->{$block} = $self->{'hblock'}->{$block};
577             }
578             } elsif (!ref($_[0])) {
579 0         0 ($block, $val) = @_;
580            
581 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
582 0         0 $self->{'hblock'}->{$block} =
583             $val->parse . $self->{'hblock'}->{$block};
584             } else {
585 0         0 $self->{'hblock'}->{$block} =
586             $val . $self->{'hblock'}->{$block};
587             }
588            
589 0         0 $res->{$block} = $self->{'hblock'}->{$block};
590             } else {
591 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
592             }
593            
594 1         4 return $res;
595             }
596              
597              
598             # append blocks results to passed values and
599             # store block results back into blocks
600             # [I] either ($block, $val) or $hash_ref with { $block => $val, ... } pairs
601             # [O] if hash_ref was passed then hash_ref of new values else just new value
602             sub unshift_block_res
603             {
604 1     1 1 2 my $self = shift;
605 1         3 my ($block, $blockf, $val, $res);
606              
607 1 50       3 unless ($_[0]) {
608 0         0 &_my_error('required parameter missed');
609             }
610            
611 1         3 $res = {};
612            
613 1 50       5 if (ref($_[0]) eq 'HASH') {
    0          
614 1         1 foreach $block(keys(%{$_[0]})) {
  1         5  
615 1         3 $val = $_[0]->{$block};
616 1         3 $blockf = '!' . $block;
617            
618 1 50       7 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
619 0         0 $self->{'hparse'}->{$blockf} =
620             $val->parse . $self->{'hparse'}->{$blockf};
621             } else {
622 1         7 $self->{'hparse'}->{$blockf} =
623             $val . $self->{'hparse'}->{$blockf};
624             }
625            
626 1         4 $res->{$block} = $self->{'hparse'}->{$blockf};
627             }
628             } elsif (!ref($_[0])) {
629 0         0 ($block, $val) = @_;
630 0         0 $blockf = '!' . $block;
631            
632 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
633 0         0 $self->{'hparse'}->{$blockf} =
634             $val->parse . $self->{'hparse'}->{$blockf};
635             } else {
636 0         0 $self->{'hparse'}->{$blockf} =
637             $val . $self->{'hparse'}->{$blockf};
638             }
639            
640 0         0 $res->{$block} = $self->{'hparse'}->{$blockf};
641             } else {
642 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
643             }
644            
645 1         4 return $res;
646             }
647              
648              
649             # resets blocks sources to it's original values (as in template)
650             # [I] array_ref or list with block names
651             # [O] hash_ref of original block values
652             sub reset_block_src
653             {
654 3     3 1 5 my $self = shift;
655 3         3 my ($res, $block, @arr);
656              
657 3         7 @arr = @_;
658 3         4 $block = shift @arr;
659 3 50       8 unless ($block) {
660 0         0 &_my_error('required parameter missed');
661             }
662            
663 3         5 $res = {};
664            
665 3 100       11 if (ref($block) eq 'ARRAY') {
    50          
666 2         4 foreach (@$block) {
667 9         20 $self->{'hblock'}->{$_} = $self->{'oblock'}->{$_};
668 9         19 $res->{$_} = $self->{'hblock'}->{$_};
669             }
670             } elsif (!ref($block)) {
671 1         6 while ($block) {
672 1         9 $self->{'hblock'}->{$block} =
673             $self->{'oblock'}->{$block};
674 1         3 $res->{$block} = $self->{'hblock'}->{$block};
675 1         5 $block = shift @arr;
676             }
677             } else {
678 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
679             }
680              
681 3         10 return $res;
682             }
683              
684              
685             # reset source values for all blocks
686             # [I] none
687             # [O] hash_ref of original block values
688             sub reset_block_src_all
689             {
690 1     1 1 1 my $self = shift;
691            
692 1         7 return $self->reset_block_src($self->enum_blocks());
693             }
694              
695              
696             # get original block values (as in the source template)
697             # [I] either list or array_ref of block names
698             # [O] hash_ref of original block values
699             sub get_oblock
700             {
701 1     1 1 2 my $self = shift;
702 1         3 my (@arr) = @_;
703 1         2 my ($res);
704            
705 1         2 $res = {};
706              
707 1 50       5 unless ($arr[0]) {
708 0         0 &_my_error('required parameter missed');
709             }
710            
711 1 50       4 if (ref($arr[0]) eq 'ARRAY') {
    0          
712 1         2 foreach (@{$arr[0]}) {
  1         5  
713 1         6 $res->{$_} = $self->{'oblock'}->{$_};
714             }
715             } elsif (!ref($arr[0])) {
716 0         0 foreach (@arr) {
717 0         0 $res->{$_} = $self->{'oblock'}->{$_};
718             }
719             } else {
720 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
721             }
722              
723 1         6 return $res;
724             }
725              
726              
727             # enumarate all blocks in template
728             # [I] none
729             # [O] array_ref with block names
730             sub enum_blocks
731             {
732 1     1 1 3 my $self = shift;
733 1         2 my ($res);
734            
735 1         2 $res = [];
736            
737 1         2 foreach (keys %{$self->{'oblock'}}) {
  1         6  
738 8         12 push @$res, $_;
739             }
740            
741 1         5 return $res;
742             }
743              
744              
745             # set self->{'text'}, don't use unless absolutely sure
746             # about what you are doing
747             # [I] new value to set
748             # [O] new value
749             sub set_text
750             {
751 0     0 1 0 my ($self, $val) = @_;
752              
753 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
754 0         0 $self->{'text'} = $val->parse;
755             } else {
756 0         0 $self->{'text'} = $val;
757             }
758            
759 0         0 return $self->{'text'};
760             }
761              
762              
763             # return self->{'text'}
764             # [I] none
765             # [O] $self->{'text'}
766             sub get_text
767             {
768 0     0 1 0 my $self = shift;
769              
770 0         0 return $self->{'text'};
771             }
772              
773              
774             # set parsing result to specified value; DON'T use unless you are
775             # absolutely sure about what you're doing
776             # [I] new value for result
777             # [O] new value for result
778             sub set_parsed
779             {
780 0     0 1 0 my ($self, $val) = @_;
781            
782 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
783 0         0 $self->{'parsed'} = $val->parse;
784             } else {
785 0         0 $self->{'parsed'} = $val;
786             }
787            
788 0         0 return $self->{'parsed'};
789             }
790              
791              
792             # global tags accessor, sets or gets tags that are global for any block
793             # [I] either ($gtag, $val) pair or
794             # $hash_ref containing { $gtag => $val } pairs or
795             # scalar $gtag to get it's value or
796             # arrayref [ $gtag1, $gtag2, ... ] to get their values
797             # [O] hash_ref containing { $gtag => $new_value, ... }
798             sub gtag
799             {
800 2     2 1 12 my $self = shift;
801 2         3 my ($gtag, $val, $res, @arr);
802            
803 2         5 @arr = @_;
804 2 50       4 unless ($arr[0]) {
805 0         0 &_my_error('required parameter missed');
806             }
807              
808 2         4 $res = {};
809            
810 2 100       9 if (ref($arr[0]) eq 'ARRAY') { # get gtag values
    50          
    50          
811 1         2 foreach $gtag(@{$arr[0]}) {
  1         3  
812 1         4 $res->{$gtag} = $self->{'gparse'}->{$gtag};
813             }
814             } elsif (ref($arr[0]) eq 'HASH') { # set gtags from hash_ref
815 0         0 foreach $gtag(keys(%{$arr[0]})) {
  0         0  
816 0         0 $val = $arr[0]->{$gtag};
817            
818 0 0       0 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
819 0         0 $self->{'gparse'}->{$gtag} = $val->parse;
820             } else {
821 0         0 $self->{'gparse'}->{$gtag} = $val;
822             }
823            
824 0         0 $res->{$gtag} = $self->{'gparse'}->{$gtag};
825             }
826             } elsif (!ref($arr[0])) { # no refs, for backwards-compatibility
827 1         2 ($gtag, $val) = @arr;
828            
829 1 50       4 if ($val) {
830 1 50       5 if (UNIVERSAL::isa($val, 'Parse::Plain')) {
831 0         0 $self->{'gparse'}->{$gtag} = $val->parse;
832             } else {
833 1         4 $self->{'gparse'}->{$gtag} = $val;
834             }
835             }
836            
837 1         3 $res->{$gtag} = $self->{'gparse'}->{$gtag};
838             } else {
839 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
840             }
841            
842 2         24 return $res;
843             }
844              
845              
846             # set callbacks
847             # [I] either hashref of { 'name' => {coderef}, ... }
848             # or pair name, coderef to callback
849             # [O] none
850             sub callback
851             {
852 1     1 1 12 my $self = shift;
853 1         2 my (@arr, $tmp);
854              
855 1         2 @arr = @_;
856            
857 1 50       4 if (ref($arr[0]) eq 'HASH') { # hashref
    50          
858 0         0 foreach $tmp(keys(%{$arr[0]})) {
  0         0  
859 0 0       0 &_my_error('colons not allowed in callback tagnames: '
860             . $tmp) if ($tmp =~ /:/);
861            
862 0         0 $self->{'cback'}->{$tmp} = $arr[0]->{$tmp};
863             }
864             } elsif (!ref($arr[0])) { # no refs
865 1 50       5 &_my_error('colons not allowed in callback tagname: '
866             . $arr[0]) if ($arr[0] =~ /:/);
867              
868 1         5 $self->{'cback'}->{$arr[0]} = $arr[1];
869             } else {
870 0         0 &_my_error('unsupported argument type: ' . ref($arr[0]));
871             }
872            
873 1         3 return;
874             }
875              
876              
877             # parse template or block
878             # [I] if none parses outermost block, if $block param is specified then
879             # block is parsed and $hparse{$block} is appended with result; if also
880             # $href hash reference is specified the block is parsed using $href; if
881             # also $usehparse is TRUE, then block will be parsed using
882             # 'hparse' hash as well.
883             # [O] parsing results
884             sub parse
885             {
886 17     17 1 38 my ($self, $block, $href, $usehparse) = @_;
887 17         17 my ($res, $lref, $cback, $W);
888            
889 17         24 $lref = {};
890            
891 17 100       65 if ($href) {
892 9         27 foreach (keys %$href) {
893 1 50       8 if (UNIVERSAL::isa($href->{$_}, 'Parse::Plain')) {
894 1         8 $lref->{$_} = $href->{$_}->parse;
895             } else {
896 0         0 $lref->{$_} = $href->{$_};
897             }
898             }
899             }
900            
901 17 100       79 if (!$href) {
    50          
902 8         10 foreach (keys %{$self->{'hparse'}}) {
  8         70  
903 41         93 $lref->{$_} = $self->{'hparse'}->{$_};
904             }
905             } elsif ($usehparse) {
906 9         10 foreach (keys %{$self->{'hparse'}}) {
  9         34  
907 73 50       227 $lref->{$_} = $self->{'hparse'}->{$_}
908             unless (defined $lref->{$_});
909             }
910             }
911              
912 17         34 foreach (keys %{$self->{'gparse'}}) {
  17         41  
913 14 50       71 $lref->{$_} = $self->{'gparse'}->{$_}
914             unless (defined $lref->{$_});
915             # gparse has least priority
916             }
917              
918 17         42 $W = $^W;
919 17         23 $^W = 0;
920 17 100       37 if ($block) {
921 13         28 $res = $self->{'hblock'}->{$block};
922 13         13 foreach $cback(keys %{$self->{'cback'}}) {
  13         33  
923 26 50       1702 $res =~ s/%{2}($cback)\:([\w\d\.\(\)\*\&\^\$\\\/:;,_-]*)%{2}/&{$self->{'cback'}->{$1}}($2)/ge
  0         0  
  0         0  
924             if (ref($self->{'cback'}->{$cback}) eq 'CODE'); }
925 13         93 $res =~ s/%{2}([\w\d\.\(\)\*\&\^\$\\\/:;,_-]+)%{2}/$lref->{$1}/g;
926 13         56 $res =~ s/%{2}(\![\w\d\.\(\)\*\&\^\$\\\/:;,_-]+)%{2}/$self->{'hparse'}->{$1}/g;
927 13         47 $self->{'hparse'}->{'!' . $block} .= $res;
928             } else {
929 4 100       10 if (defined $self->{'parsed'}) {
930 1         2 $^W = $W;
931 1         9 return $self->{'parsed'};
932             }
933 3         7 $self->{'parsed'} = $self->{'text'};
934 3         4 foreach $cback(keys %{$self->{'cback'}}) {
  3         10  
935 4 50       231 $self->{'parsed'} =~ s/%{2}($cback)\:([\w\d\.\(\)\*\&\^\$\\\/:;,_-]*)%{2}/&{$self->{'cback'}->{$1}}($2)/ge
  3         5  
  3         15  
936             if (ref($self->{'cback'}->{$cback}) eq 'CODE');
937             }
938 3         34 $self->{'parsed'} =~ s/%{2}([\w\d\.\(\)\*\&\^\$\\\/:;,_-]+)%{2}/$lref->{$1}/g;
939 3         14 $self->{'parsed'} =~ s/%{2}(\![\w\d\.\(\)\*\&\^\$\\\/:;,_-]+)%{2}/$self->{'hparse'}->{$1}/g;
940 3         8 $res = $self->{'parsed'};
941             }
942 16         33 $^W = $W;
943            
944 16         72 return $res;
945             }
946              
947              
948             # reset parsed blocks
949             # [I] none to reset outermost block
950             # array or arrayref of block names to reset blocks
951             # to current values of block sources
952             # [O] previous value of text or hash_ref with previous
953             # values of blocks
954             sub unparse
955             {
956 1     1 1 2 my $self = shift;
957 1         2 my ($tmp, $key, $keyf);
958              
959 1 50       3 if ($#_ == -1) {
960 0         0 $tmp = $self->{'parsed'};
961 0         0 $self->{'parsed'} = undef;
962             } else {
963 1         2 $tmp = {};
964            
965 1 50       4 if (ref($_[0]) eq 'ARRAY') {
    0          
966 1         2 foreach $key(@{$_[0]}) {
  1         3  
967 1         3 $keyf = '!' . $key;
968            
969 1 50       5 if (defined $self->{'hparse'}->{$keyf}) {
970 1         3 $tmp->{$key} =
971             $self->{'hparse'}->{$keyf};
972 1         5 $self->{'hparse'}->{$keyf} = undef;
973             }
974             }
975             } elsif (!ref($_[0])) {
976 0         0 while (@_) {
977 0         0 $key = shift;
978 0         0 $keyf = '!' . $key;
979            
980 0 0       0 if (defined $self->{'hparse'}->{$keyf}) {
981 0         0 $tmp->{$key} =
982             $self->{'hparse'}->{$keyf};
983 0         0 $self->{'hparse'}->{$keyf} = undef;
984             }
985             }
986             } else {
987 0         0 &_my_error('unsupported argument type: ' . ref($_[0]));
988             }
989             }
990            
991 1         6 return $tmp;
992             }
993              
994              
995             # unparse() all blocks including outermost
996             # [I] none
997             # [O] hash_ref with previous values of blocks except outermost (text)
998             sub unparse_all
999             {
1000 0     0 1 0 my $self = shift;
1001            
1002 0         0 $self->unparse();
1003 0         0 return $self->unparse($self->enum_blocks());
1004             }
1005              
1006              
1007             # print parsing results, if template already parsed prints it
1008             # otherwise parse template first
1009             # [I] none
1010             # [O] parsing results
1011             sub output
1012             {
1013 0     0 1 0 my $self = shift;
1014              
1015 0         0 print $self->parse;
1016              
1017 0         0 return $self->{'parsed'};
1018             }
1019              
1020              
1021             # callback for including templates recursively via %%include:filename.tmpl%%
1022             # not method, not exported
1023             # [I] filename
1024             # [O] file contents as scalar
1025             sub _include_file
1026             {
1027 2     2   4 my $arg = shift;
1028 2         3 my ($cnt);
1029            
1030 2 50       6 return '' unless ($arg);
1031            
1032 2         4 $cnt = join('', @{&_load_file($arg)});
  2         6  
1033 2         5 $cnt =~ s/%{2}INCLUDE:([\w\d\.\(\)\&\^\$\\\/;,_-]+)%{2}/&_include_file($1)/ge;
  0         0  
1034            
1035 2         13 return $cnt;
1036             }
1037              
1038              
1039             # read file from disk, not method, not exported
1040             # [I] filename
1041             # [O] reference to array of lines
1042             sub _load_file
1043             {
1044 6     6   9 my $filename = shift;
1045 6         7 my ($lcnt, @lines);
1046              
1047 6 50       134 unless (-f $filename) {
1048 0         0 &_my_error("template not found: $filename");
1049             }
1050              
1051 6 50       97 unless (-r $filename) {
1052 0         0 &_my_error("template not readable: $filename");
1053             }
1054            
1055 6         9 $lcnt = 0;
1056 6         8 while (1) {
1057 6 50       208 if (open(TMPL, '<' . $filename)) {
    0          
1058 6         149 @lines = ;
1059 6         78 close(TMPL);
1060 6         13 last;
1061             } elsif ($lcnt >= $lcnt_max) {
1062 0         0 &_my_error("loop counter ($lcnt_max) exceeded " .
1063             "while opening file $filename");
1064             } else {
1065 0         0 $lcnt++;
1066 0 0       0 sleep $ssec if ($ssec);
1067             }
1068             }
1069            
1070 6         35 return \@lines;
1071             }
1072              
1073              
1074             # die with specified message.
1075             # [I] error_message
1076             # [O] none
1077             sub _my_error
1078             {
1079 0     0     my $msg = shift;
1080 0           my @caller;
1081              
1082 0           @caller = caller(0);
1083              
1084 0           croak "Parse::Plain $caller[1]:$caller[2] in $caller[3]: $msg";
1085              
1086 0           return;
1087             }
1088              
1089              
1090             1;
1091              
1092              
1093             __END__