| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Copyrights 2003,2007-2013 by [Mark Overmeer]. | 
| 2 |  |  |  |  |  |  | #  For other contributors see ChangeLog. | 
| 3 |  |  |  |  |  |  | # See the manual pages for details on the licensing terms. | 
| 4 |  |  |  |  |  |  | # Pod stripped from pm file by OODoc 2.00. | 
| 5 | 10 |  |  | 10 |  | 320056 | use strict; | 
|  | 10 |  |  |  |  | 25 |  | 
|  | 10 |  |  |  |  | 401 |  | 
| 6 | 10 |  |  | 10 |  | 54 | use warnings; | 
|  | 10 |  |  |  |  | 18 |  | 
|  | 10 |  |  |  |  | 429 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | package OODoc::Template; | 
| 9 | 10 |  |  | 10 |  | 91 | use vars '$VERSION'; | 
|  | 10 |  |  |  |  | 22 |  | 
|  | 10 |  |  |  |  | 653 |  | 
| 10 |  |  |  |  |  |  | $VERSION = '0.16'; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 10 |  |  | 10 |  | 9878 | use Log::Report  'oodoc-template'; | 
|  | 10 |  |  |  |  | 1410614 |  | 
|  | 10 |  |  |  |  | 76 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 10 |  |  | 10 |  | 3424 | use IO::File     (); | 
|  | 10 |  |  |  |  | 20 |  | 
|  | 10 |  |  |  |  | 158 |  | 
| 16 | 10 |  |  | 10 |  | 54 | use File::Spec   (); | 
|  | 10 |  |  |  |  | 18 |  | 
|  | 10 |  |  |  |  | 210 |  | 
| 17 | 10 |  |  | 10 |  | 12068 | use Data::Dumper qw(Dumper); | 
|  | 10 |  |  |  |  | 72381 |  | 
|  | 10 |  |  |  |  | 1004 |  | 
| 18 | 10 |  |  | 10 |  | 106 | use Scalar::Util qw(weaken); | 
|  | 10 |  |  |  |  | 18 |  | 
|  | 10 |  |  |  |  | 33083 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | my @default_markers = ('', ''); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub new(@) | 
| 24 | 11 |  |  | 11 | 1 | 4067 | {   my ($class, %args) = @_; | 
| 25 | 11 |  |  |  |  | 85 | (bless {}, $class)->init(\%args); | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | sub init($) | 
| 29 | 11 |  |  | 11 | 0 | 28 | {   my ($self, $args) = @_; | 
| 30 |  |  |  |  |  |  |  | 
| 31 | 11 |  |  |  |  | 77 | $self->{cached}     = {}; | 
| 32 | 11 |  |  |  |  | 36 | $self->{macros}     = {}; | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 11 |  |  |  |  | 21 | my $s = $self; weaken $s;   # avoid circular ref | 
|  | 11 |  |  |  |  | 62 |  | 
| 35 | 11 |  | 50 | 7 |  | 138 | $args->{template} ||= sub { $s->includeTemplate(@_) }; | 
|  | 7 |  |  |  |  | 25 |  | 
| 36 | 11 |  | 50 | 1 |  | 188 | $args->{macro}    ||= sub { $s->defineMacro(@_) }; | 
|  | 1 |  |  |  |  | 5 |  | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 11 |  | 100 |  |  | 90 | $args->{search}   ||= '.'; | 
| 39 | 11 |  | 100 |  |  | 71 | $args->{markers}  ||= \@default_markers; | 
| 40 | 11 |  | 50 | 2 |  | 101 | $args->{define}   ||= sub { shift; (1, @_) }; | 
|  | 2 |  |  |  |  | 3 |  | 
|  | 2 |  |  |  |  | 11 |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 11 |  |  |  |  | 60 | $self->pushValues($args); | 
| 43 | 11 |  |  |  |  | 48 | $self; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | sub process($) | 
| 48 | 112 |  |  | 112 | 1 | 144934 | {   my ($self, $templ) = (shift, shift); | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 112 | 100 |  |  |  | 736 | my $values = @_==1 ? shift : @_ ? {@_} : {}; | 
|  |  | 100 |  |  |  |  |  | 
| 51 |  |  |  |  |  |  |  | 
| 52 | 112 | 100 |  |  |  | 720 | my $tree     # parse with real copy | 
|  |  | 100 |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | = ref $templ eq 'SCALAR' ? $self->parseTemplate($$templ) | 
| 54 |  |  |  |  |  |  | : ref $templ eq 'ARRAY'  ? $templ | 
| 55 |  |  |  |  |  |  | :                          $self->parseTemplate("$templ"); | 
| 56 |  |  |  |  |  |  |  | 
| 57 | 112 | 50 |  |  |  | 265 | defined $tree | 
| 58 |  |  |  |  |  |  | or return (); | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 112 | 100 |  |  |  | 376 | $self->pushValues($values) | 
| 61 |  |  |  |  |  |  | if keys %$values; | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 112 |  |  |  |  | 143 | my @output; | 
| 64 | 112 |  |  |  |  | 218 | foreach my $node (@$tree) | 
| 65 | 338 | 100 |  |  |  | 647 | {   unless(ref $node) | 
| 66 | 225 |  |  |  |  | 348 | {   push @output, $node; | 
| 67 | 225 |  |  |  |  | 385 | next; | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 113 |  |  |  |  | 239 | my ($tag, $attr, $then, $else) = @$node; | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 113 |  |  |  |  | 123 | my %attrs; | 
| 73 | 113 |  |  |  |  | 381 | while(my($k, $v) = each %$attr) | 
| 74 | 5 |  |  |  |  | 16 | {   $attrs{$k} = ref $v ne 'ARRAY' ? $v | 
| 75 | 17 | 100 |  |  |  | 109 | : @$v==1 ? scalar $self->valueFor(@{$v->[0]}) | 
| 76 |  |  |  |  |  |  | : join '', | 
| 77 | 53 | 100 |  |  |  | 258 | map {ref $_ eq 'ARRAY' ? scalar $self->valueFor(@$_) : $_} | 
|  |  | 100 |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | @$v; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 113 |  |  |  |  | 404 | (my $value, my $attrs, $then, $else) | 
| 82 |  |  |  |  |  |  | = $self->valueFor($tag, \%attrs, $then, $else); | 
| 83 |  |  |  |  |  |  |  | 
| 84 | 113 | 100 | 100 |  |  | 943 | unless(defined $then || defined $else) | 
| 85 | 74 | 100 |  |  |  | 162 | {   defined $value | 
| 86 |  |  |  |  |  |  | or next; | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 60 | 50 | 33 |  |  | 335 | ref $value ne 'ARRAY' && ref $value ne 'HASH' | 
| 89 |  |  |  |  |  |  | or error __x"value for {tag} is {value}, must be single" | 
| 90 |  |  |  |  |  |  | , tag => $tag, value => $value; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 60 |  |  |  |  | 85 | push @output, $value; | 
| 93 | 60 |  |  |  |  | 217 | next; | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 39 |  | 66 |  |  | 211 | my $take_else | 
| 97 |  |  |  |  |  |  | = !defined $value || (ref $value eq 'ARRAY' && @$value==0); | 
| 98 |  |  |  |  |  |  |  | 
| 99 | 39 | 100 |  |  |  | 82 | my $container = $take_else ? $else : $then; | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 39 | 100 |  |  |  | 105 | defined $container | 
| 102 |  |  |  |  |  |  | or next; | 
| 103 |  |  |  |  |  |  |  | 
| 104 | 27 | 100 |  |  |  | 77 | $self->pushValues($attrs) if keys %$attrs; | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 27 | 100 |  |  |  | 97 | if($take_else) | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 107 | 6 |  |  |  |  | 15 | {    my ($nest_out, $nest_tree) = $self->process($container); | 
| 108 | 6 |  |  |  |  | 13 | push @output, $nest_out; | 
| 109 | 6 |  |  |  |  | 11 | $node->[3] = $nest_tree; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | elsif(ref $value eq 'HASH') | 
| 112 | 3 |  |  |  |  | 8 | {    my ($nest_out, $nest_tree) = $self->process($container, $value); | 
| 113 | 3 |  |  |  |  | 5 | push @output, $nest_out; | 
| 114 | 3 |  |  |  |  | 8 | $node->[2] = $nest_tree; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  | elsif(ref $value eq 'ARRAY') | 
| 117 | 11 |  |  |  |  | 19 | {    foreach my $data (@$value) | 
| 118 | 16 |  |  |  |  | 88 | {   my ($nest_out, $nest_tree) = $self->process($container, $data); | 
| 119 | 16 |  |  |  |  | 28 | push @output, $nest_out; | 
| 120 | 16 |  |  |  |  | 76 | $node->[2] = $nest_tree; | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  | else | 
| 124 | 7 |  |  |  |  | 65 | {    my ($nest_out, $nest_tree) = $self->process($container); | 
| 125 | 7 |  |  |  |  | 15 | push @output, $nest_out; | 
| 126 | 7 |  |  |  |  | 14 | $node->[2] = $nest_tree; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 27 | 100 |  |  |  | 108 | $self->popValues if keys %$attrs; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 112 | 100 |  |  |  | 447 | $self->popValues if keys %$values; | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 112 | 100 |  |  |  | 682 | wantarray ? (join('', @output), $tree)  # LIST context | 
|  |  | 100 |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | : defined wantarray ? join('', @output)           # SCALAR context | 
| 136 |  |  |  |  |  |  | :                     print @output;              # VOID context | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | sub processFile($;@) | 
| 141 | 6 |  |  | 6 | 1 | 13 | {   my ($self, $filename) = (shift, shift); | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 6 | 50 |  |  |  | 16 | my $values = @_==1 ? shift : {@_}; | 
| 144 | 6 |  | 66 |  |  | 34 | $values->{source} ||= $filename; | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 6 |  |  |  |  | 12 | my $cache  = $self->{cached}; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 6 |  |  |  |  | 8 | my ($output, $tree, $template); | 
| 149 | 6 | 100 |  |  |  | 30 | if(exists $cache->{$filename}) | 
|  |  | 100 |  |  |  |  |  | 
| 150 | 4 |  |  |  |  | 7 | {   $tree   = $cache->{$filename}; | 
| 151 | 4 | 50 |  |  |  | 29 | $output = $self->process($tree, $values) | 
| 152 |  |  |  |  |  |  | if defined $tree; | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  | elsif($template = $self->loadFile($filename)) | 
| 155 | 1 |  |  |  |  | 133 | {   ($output, $tree) = $self->process($template, $values); | 
| 156 | 1 |  |  |  |  | 4 | $cache->{$filename} = $tree; | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | else | 
| 159 | 1 |  |  |  |  | 5 | {   $tree = $cache->{$filename} = undef; | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 6 | 50 | 66 |  |  | 23 | defined $tree || defined wantarray | 
| 163 |  |  |  |  |  |  | or error __x"cannot find template file {fn}", fn => $filename; | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 6 | 50 |  |  |  | 42 | wantarray ? ($output, $tree)  # LIST context | 
|  |  | 50 |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | : defined wantarray ? $output           # SCALAR context | 
| 167 |  |  |  |  |  |  | :                     print $output;    # VOID context | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | sub defineMacro($$$$) | 
| 172 | 1 |  |  | 1 | 1 | 3 | {   my ($self, $tag, $attrs, $then, $else) = @_; | 
| 173 | 1 | 50 |  |  |  | 6 | my $name = delete $attrs->{name} | 
| 174 |  |  |  |  |  |  | or error __x"macro requires a name"; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 1 | 50 |  |  |  | 4 | defined $else | 
| 177 |  |  |  |  |  |  | and error __x"macros cannot have an else part ({macro})",macro => $name; | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 1 |  |  |  |  | 3 | my %attrs = %$attrs;   # for closure | 
| 180 | 1 |  |  |  |  | 6 | $attrs{markers} = $self->valueFor('markers'); | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | $self->{macros}{$name} = | 
| 183 | 2 |  |  | 2 |  | 3 | sub { my ($tag, $at) = @_; | 
| 184 | 2 |  |  |  |  | 25 | $self->process($then, +{%attrs, %$at}); | 
| 185 | 1 |  |  |  |  | 13 | }; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 1 |  |  |  |  | 6 | (); | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub valueFor($;$$$) | 
| 193 | 236 |  |  | 236 | 1 | 431 | {   my ($self, $tag, $attrs, $then, $else) = @_; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | #warn "Looking for $tag"; | 
| 196 |  |  |  |  |  |  | #warn Dumper $self->{values}; | 
| 197 | 236 |  |  |  |  | 856 | for(my $set = $self->{values}; defined $set; $set = $set->{NEXT}) | 
| 198 | 284 |  |  |  |  | 479 | {   my $v = $set->{$tag}; | 
| 199 |  |  |  |  |  |  |  | 
| 200 | 284 | 100 |  |  |  | 817 | if(defined $v) | 
| 201 |  |  |  |  |  |  | {   # HASH  defines container | 
| 202 |  |  |  |  |  |  | # ARRAY defines container loop | 
| 203 |  |  |  |  |  |  | # object or other things can be stored as well, but may get | 
| 204 |  |  |  |  |  |  | # stringified. | 
| 205 | 212 | 100 |  |  |  | 953 | return wantarray ? ($v, $attrs, $then, $else) : $v | 
|  |  | 100 |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | if ref $v ne 'CODE'; | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | return wantarray | 
| 209 | 33 | 100 |  |  |  | 124 | ? $v->($tag, $attrs, $then, $else) | 
| 210 |  |  |  |  |  |  | : ($v->($tag, $attrs, $then, $else))[0] | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 | 72 | 50 |  |  |  | 396 | return wantarray ? (undef, $attrs, $then, $else) : undef | 
|  |  | 100 |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | if exists $set->{$tag}; | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 68 |  |  |  |  | 109 | my $code = $set->{DYNAMIC}; | 
| 217 | 68 | 50 |  |  |  | 240 | if(defined $code) | 
| 218 | 0 |  |  |  |  | 0 | {   my ($value, @other) = $code->($tag, $attrs, $then, $else); | 
| 219 | 0 | 0 |  |  |  | 0 | return wantarray ? ($value, @other) : $value | 
|  |  | 0 |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | if defined $value; | 
| 221 |  |  |  |  |  |  | # and continue the search otherwise | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 20 | 50 |  |  |  | 96 | wantarray ? (undef, $attrs, $then, $else) : undef; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | sub allValuesFor($;$$$) | 
| 230 | 2 |  |  | 2 | 1 | 4 | {   my ($self, $tag, $attrs, $then, $else) = @_; | 
| 231 | 2 |  |  |  |  | 4 | my @values; | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 2 |  |  |  |  | 7 | for(my $set = $self->{values}; defined $set; $set = $set->{NEXT}) | 
| 234 |  |  |  |  |  |  | { | 
| 235 | 4 | 100 |  |  |  | 137 | if(defined(my $v = $set->{$tag})) | 
| 236 | 2 | 50 |  |  |  | 9 | {   my $t = ref $v eq 'CODE' ? $v->($tag, $attrs, $then, $else) : $v; | 
| 237 | 2 | 50 |  |  |  | 7 | push @values, $t if defined $t; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 4 | 50 |  |  |  | 17 | if(defined(my $code = $set->{DYNAMIC})) | 
| 241 | 0 |  |  |  |  | 0 | {   my $t = $code->($tag, $attrs, $then, $else); | 
| 242 | 0 | 0 |  |  |  | 0 | push @values, $t if defined $t; | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 2 |  |  |  |  | 7 | @values; | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | sub pushValues($) | 
| 251 | 68 |  |  | 68 | 1 | 110 | {   my ($self, $attrs) = @_; | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 68 | 100 |  |  |  | 197 | if(my $markers = $attrs->{markers}) | 
| 254 | 2 |  |  |  |  | 4 | {   my @markers = ref $markers eq 'ARRAY' ? @$markers | 
| 255 | 14 | 100 |  |  |  | 97 | : map {s/\\\,//g; $_} split /(?!<\\)\,\s*/, $markers; | 
|  | 2 |  |  |  |  | 6 |  | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 14 | 100 |  |  |  | 54 | push @markers, $markers[0] . '/' | 
| 258 |  |  |  |  |  |  | if @markers==2; | 
| 259 |  |  |  |  |  |  |  | 
| 260 | 14 | 100 |  |  |  | 54 | push @markers, $markers[1] | 
| 261 |  |  |  |  |  |  | if @markers==3; | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 56 | 100 |  |  |  | 690 | $attrs->{markers} | 
| 264 | 14 |  |  |  |  | 33 | = [ map { ref $_ eq 'Regexp' ? $_ : qr/\Q$_/ } @markers ]; | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 68 | 100 |  |  |  | 201 | if(my $search = $attrs->{search}) | 
| 268 | 11 | 50 |  |  |  | 103 | {   $attrs->{search} = [ split /\:/, $search ] | 
| 269 |  |  |  |  |  |  | if ref $search ne 'ARRAY'; | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 68 |  |  |  |  | 359 | $self->{values} = { %$attrs, NEXT => $self->{values} }; | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | sub popValues() | 
| 277 | 57 |  |  | 57 | 1 | 79 | {   my $self = shift; | 
| 278 | 57 |  |  |  |  | 157 | $self->{values} = $self->{values}{NEXT}; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | sub includeTemplate($$$) | 
| 283 | 7 |  |  | 7 | 1 | 15 | {   my ($self, $tag, $attrs, $then, $else) = @_; | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 7 | 50 | 33 |  |  | 48 | defined $then || defined $else | 
| 286 |  |  |  |  |  |  | and error __x"template is not a container"; | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 7 | 100 |  |  |  | 22 | if(my $fn = $attrs->{file}) | 
| 289 | 5 |  |  |  |  | 13 | {   my $output = $self->processFile($fn, $attrs); | 
| 290 | 5 | 100 | 66 |  |  | 28 | $output    = $self->processFile($attrs->{alt}, $attrs) | 
| 291 |  |  |  |  |  |  | if !defined $output && $attrs->{alt}; | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 5 | 50 |  |  |  | 11 | defined $output | 
| 294 |  |  |  |  |  |  | or error __x"cannot find template file {fn}", fn => $fn; | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 5 |  |  |  |  | 45 | return ($output); | 
| 297 |  |  |  |  |  |  | } | 
| 298 |  |  |  |  |  |  |  | 
| 299 | 2 | 50 |  |  |  | 8 | if(my $name = $attrs->{macro}) | 
| 300 | 2 | 50 |  |  |  | 7 | {    my $macro = $self->{macros}{$name} | 
| 301 |  |  |  |  |  |  | or error __x"cannot find macro {name}", name => $name; | 
| 302 |  |  |  |  |  |  |  | 
| 303 | 2 |  |  |  |  | 5 | return $macro->($tag, $attrs, $then, $else); | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 0 |  | 0 |  |  | 0 | error __x"file or macro attribute required for template in {source}" | 
| 307 |  |  |  |  |  |  | , source => $self->valueFor('source') || '??'; | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | sub loadFile($) | 
| 312 | 2 |  |  | 2 | 1 | 4 | {   my ($self, $relfn) = @_; | 
| 313 | 2 |  |  |  |  | 3 | my $absfn; | 
| 314 |  |  |  |  |  |  |  | 
| 315 | 2 | 50 |  |  |  | 105 | if(File::Spec->file_name_is_absolute($relfn)) | 
| 316 | 0 |  |  |  |  | 0 | {   my $fn = File::Spec->canonpath($relfn); | 
| 317 | 0 | 0 |  |  |  | 0 | $absfn = $fn if -f $fn; | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  |  | 
| 320 | 2 | 50 |  |  |  | 7 | unless($absfn) | 
| 321 | 2 |  |  |  |  | 8 | {   my @srcs = map { @$_ } $self->allValuesFor('search'); | 
|  | 2 |  |  |  |  | 8 |  | 
| 322 | 2 |  |  |  |  | 5 | foreach my $dir (@srcs) | 
| 323 | 3 |  |  |  |  | 143 | {   $absfn = File::Spec->rel2abs($relfn, $dir); | 
| 324 | 3 | 100 |  |  |  | 387 | last if -f $absfn; | 
| 325 | 2 |  |  |  |  | 7 | $absfn = undef; | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  | } | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 2 | 100 |  |  |  | 9 | defined $absfn | 
| 330 |  |  |  |  |  |  | or return undef; | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 1 |  |  |  |  | 11 | my $in = IO::File->new($absfn, 'r'); | 
| 333 | 1 | 50 |  |  |  | 133 | unless(defined $in) | 
| 334 | 0 |  | 0 |  |  | 0 | {   my $source = $self->valueFor('source') || '??'; | 
| 335 | 0 |  |  |  |  | 0 | fault __x"Cannot read from {fn} in {file}", fn => $absfn, file=>$source; | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  |  | 
| 338 | 1 |  |  |  |  | 39 | \(join '', $in->getlines);  # auto-close in | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | sub parse($@) | 
| 343 | 0 |  |  | 0 | 1 | 0 | {   my ($self, $template) = (shift, shift); | 
| 344 | 0 |  |  |  |  | 0 | $self->process(\$template, @_); | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | sub parseTemplate($) | 
| 349 | 106 |  |  | 106 | 1 | 348 | {   my ($self, $template) = @_; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 106 | 50 |  |  |  | 257 | defined $template | 
| 352 |  |  |  |  |  |  | or return undef; | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 106 |  |  |  |  | 260 | my $markers = $self->valueFor('markers'); | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | # Remove white-space escapes | 
| 357 | 106 |  |  |  |  | 1473 | $template =~ s! \\ (?: \s* (?: \\ \s*)? \n)+ | 
| 358 |  |  |  |  |  |  | (?: \s* (?= $markers->[0] | $markers->[3] ))? | 
| 359 |  |  |  |  |  |  | !!mgx; | 
| 360 |  |  |  |  |  |  |  | 
| 361 | 106 |  |  |  |  | 134 | my @frags; | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | # NOT_$tag supported for backwards compat | 
| 364 | 106 |  |  |  |  | 2502 | while( $template =~ s!^(.*?)        # text before container | 
| 365 |  |  |  |  |  |  | $markers->[0] \s* | 
| 366 |  |  |  |  |  |  | (?: IF \s* )? | 
| 367 |  |  |  |  |  |  | (NOT (?:_|\s+) )? | 
| 368 |  |  |  |  |  |  | ([\w.-]+) \s*    # tag | 
| 369 |  |  |  |  |  |  | (.*?) \s*    # attributes | 
| 370 |  |  |  |  |  |  | $markers->[1] | 
| 371 |  |  |  |  |  |  | !!xs | 
| 372 |  |  |  |  |  |  | ) | 
| 373 | 109 |  |  |  |  | 319 | {   push @frags, $1; | 
| 374 | 109 |  |  |  |  | 308 | my ($not, $tag, $attr) = ($2, $3, $4); | 
| 375 | 109 |  |  |  |  | 111 | my ($then, $else); | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 109 | 100 |  |  |  | 2494 | if($template =~ s! (.*?)           # contained | 
| 378 |  |  |  |  |  |  | ( $markers->[2] | 
| 379 |  |  |  |  |  |  | \s* \Q$tag\E \s*  # "our" tag | 
| 380 |  |  |  |  |  |  | $markers->[3] | 
| 381 |  |  |  |  |  |  | ) | 
| 382 |  |  |  |  |  |  | !!xs) | 
| 383 | 40 |  |  |  |  | 88 | {   $then       = $1; | 
| 384 | 40 |  |  |  |  | 80 | my $endline = $2; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 | 109 | 100 |  |  |  | 686 | if($not) { ($then, $else) = (undef, $then) } | 
|  | 5 | 100 |  |  |  | 13 |  | 
|  |  | 100 |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | elsif(!defined $then) { } | 
| 389 |  |  |  |  |  |  | elsif($then =~ s! $markers->[0] | 
| 390 |  |  |  |  |  |  | \s* ELSE (?:_|\s+) | 
| 391 |  |  |  |  |  |  | \Q$tag\E \s* | 
| 392 |  |  |  |  |  |  | $markers->[1] | 
| 393 |  |  |  |  |  |  | (.*) | 
| 394 |  |  |  |  |  |  | !!xs) | 
| 395 |  |  |  |  |  |  | {   # ELSE_$tag for backwards compat | 
| 396 | 5 |  |  |  |  | 11 | $else = $1; | 
| 397 |  |  |  |  |  |  | } | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 109 |  |  |  |  | 277 | push @frags, [$tag, $self->parseAttrs($attr), $then, $else]; | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 106 |  |  |  |  | 210 | push @frags, $template; | 
| 403 | 106 |  |  |  |  | 300 | \@frags; | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | sub parseAttrs($) | 
| 408 | 113 |  |  | 113 | 1 | 166 | {   my ($self, $string) = @_; | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 113 |  |  |  |  | 123 | my %attrs; | 
| 411 | 113 |  |  |  |  | 424 | while( $string =~ | 
| 412 |  |  |  |  |  |  | s!^\s* (?: '([^']+)'        # attribute name (might be quoted) | 
| 413 |  |  |  |  |  |  | |   "([^"]+)" | 
| 414 |  |  |  |  |  |  | |   (\w+) | 
| 415 |  |  |  |  |  |  | ) | 
| 416 |  |  |  |  |  |  | \s* (?: \= \>? \s*       # an optional value | 
| 417 |  |  |  |  |  |  | ( \"[^"]*\"          # dquoted value | 
| 418 |  |  |  |  |  |  | | \'[^']*\'          # squoted value | 
| 419 |  |  |  |  |  |  | | \$\{ [^}]+ \}      # complex variable | 
| 420 |  |  |  |  |  |  | | [^\s,]+            # unquoted value | 
| 421 |  |  |  |  |  |  | ) | 
| 422 |  |  |  |  |  |  | )? | 
| 423 |  |  |  |  |  |  | \s* \,?             # optionally separated by commas | 
| 424 |  |  |  |  |  |  | !!xs) | 
| 425 | 57 |  | 33 |  |  | 361 | {   my ($k, $v) = ($1||$2||$3, $4); | 
| 426 | 57 | 100 |  |  |  | 131 | unless(defined $v) | 
| 427 | 11 |  |  |  |  | 26 | {  $attrs{$k} = 1; | 
| 428 | 11 |  |  |  |  | 37 | next; | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 | 46 | 100 |  |  |  | 138 | if($v =~ m/^\'(.*)\'$/) | 
| 432 |  |  |  |  |  |  | {   # Single quoted parameter, no interpolation | 
| 433 | 11 |  |  |  |  | 31 | $attrs{$k} = $1; | 
| 434 | 11 |  |  |  |  | 42 | next; | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  |  | 
| 437 | 35 |  |  |  |  | 89 | $v =~ s/^\"(.*)\"$/$1/; | 
| 438 | 35 |  |  |  |  | 175 | my @v = split /( \$\{[^\}]+\} | \$\w+ )/x, $v; | 
| 439 |  |  |  |  |  |  |  | 
| 440 | 35 | 100 | 66 |  |  | 187 | if(@v==1 && $v[0] !~ m/^\$/) | 
| 441 | 23 |  |  |  |  | 60 | {   $attrs{$k} = $v[0]; | 
| 442 | 23 |  |  |  |  | 120 | next; | 
| 443 |  |  |  |  |  |  | } | 
| 444 |  |  |  |  |  |  |  | 
| 445 | 12 |  |  |  |  | 17 | my @steps; | 
| 446 | 12 |  |  |  |  | 27 | foreach (@v) | 
| 447 | 36 | 100 |  |  |  | 133 | {   if( m/^ (?: \$(\w+) | \$\{ (\w+) \s* \} ) $/x ) | 
|  |  | 100 |  |  |  |  |  | 
| 448 | 12 |  |  |  |  | 50 | {   push @steps, [ $+ ]; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  | elsif( m/^ \$\{ (\w+) \s* ([^\}]+? \s* ) \} $/x ) | 
| 451 | 4 |  |  |  |  | 15 | {   push @steps, [ $1, $self->parseAttrs($2) ]; | 
| 452 |  |  |  |  |  |  | } | 
| 453 |  |  |  |  |  |  | else | 
| 454 | 20 | 100 |  |  |  | 61 | {   push @steps, $_ if length $_; | 
| 455 |  |  |  |  |  |  | } | 
| 456 |  |  |  |  |  |  | } | 
| 457 |  |  |  |  |  |  |  | 
| 458 | 12 |  |  |  |  | 75 | $attrs{$k} = \@steps; | 
| 459 |  |  |  |  |  |  | } | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 113 | 50 |  |  |  | 284 | error __x"attribute error in {tag}'", tag => $_[1] | 
| 462 |  |  |  |  |  |  | if length $string; | 
| 463 |  |  |  |  |  |  |  | 
| 464 | 113 |  |  |  |  | 1146 | \%attrs; | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | 1; |