| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 2 |  |  | 2 |  | 1508 | use strict; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 59 |  | 
| 2 | 2 |  |  | 2 |  | 11 | use warnings; | 
|  | 2 |  |  |  |  | 2 |  | 
|  | 2 |  |  |  |  | 92 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | package XML::BindData; | 
| 5 |  |  |  |  |  |  | $XML::BindData::VERSION = '0.3.2'; | 
| 6 | 2 |  |  | 2 |  | 1414 | use XML::LibXML; | 
|  | 2 |  |  |  |  | 113501 |  | 
|  | 2 |  |  |  |  | 12 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | sub bind { | 
| 9 | 26 |  |  | 26 | 1 | 15804 | my ($class, $xml_string, $data) = @_; | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 26 |  |  |  |  | 87 | my $xml = XML::LibXML->load_xml(string => $xml_string); | 
| 12 | 26 |  |  |  |  | 6265 | parse_node($xml->documentElement, $data); | 
| 13 |  |  |  |  |  |  |  | 
| 14 | 26 |  |  |  |  | 321 | return $xml->toStringC14N(1); | 
| 15 |  |  |  |  |  |  | } | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | sub parse_node { | 
| 18 | 67 |  |  | 67 | 0 | 133 | my ($node, $context) = @_; | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 67 | 100 |  |  |  | 119 | if (my $if_key = _strip_attr($node, 'tmpl-if')) { | 
| 21 | 8 |  |  |  |  | 33 | my $unless = $if_key =~ s/^!//; | 
| 22 | 8 |  |  |  |  | 18 | my $val    = _get($context, $if_key); | 
| 23 | 8 | 100 | 100 |  |  | 43 | if ( (!$unless && ! defined $val) | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 24 |  |  |  |  |  |  | || ( $unless &&  defined $val)) { | 
| 25 | 3 |  |  |  |  | 12 | $node->unbindNode; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  | } | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 67 | 100 |  |  |  | 809 | if (my $each_key = _strip_attr($node, 'tmpl-each')) { | 
| 30 | 10 |  |  |  |  | 38 | my $parent = $node->parentNode; | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 10 |  |  |  |  | 24 | my $to_add = _get($context, $each_key); | 
| 33 | 10 | 50 | 33 |  |  | 53 | if (!$to_add || ref $to_add ne 'ARRAY') { | 
| 34 | 0 |  |  |  |  | 0 | $to_add = []; | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 10 |  |  |  |  | 22 | foreach my $subcontext ( reverse @$to_add ) { | 
| 38 | 21 |  |  |  |  | 316 | my $new = $node->cloneNode(1); # deep clone | 
| 39 | 21 |  |  |  |  | 58 | parse_node($new, $subcontext); | 
| 40 | 21 |  |  |  |  | 165 | $parent->insertAfter( $new, $node ); | 
| 41 |  |  |  |  |  |  | } | 
| 42 | 10 |  |  |  |  | 207 | $node->unbindNode; | 
| 43 | 10 |  |  |  |  | 28 | return; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 57 | 100 |  |  |  | 659 | if (my $binding = _strip_attr($node, 'tmpl-bind')) { | 
| 47 | 24 |  |  |  |  | 52 | my $val = _get($context, $binding); | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 24 |  |  |  |  | 46 | my $default = _strip_attr($node, 'tmpl-default'); | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 24 | 100 |  |  |  | 277 | unless (defined $val) { | 
| 52 | 2 | 100 |  |  |  | 5 | $val = defined $default ? $default : ''; | 
| 53 |  |  |  |  |  |  | } | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 24 |  |  |  |  | 89 | $node->appendTextNode($val); | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 57 | 100 |  |  |  | 404 | if (my $attr_map = _strip_attr($node, 'tmpl-attr-map')) { | 
| 59 | 7 |  |  |  |  | 62 | my @attributes = map { [ split qr/:/ ] } split qr/,/, $attr_map; | 
|  | 17 |  |  |  |  | 104 |  | 
| 60 |  |  |  |  |  |  |  | 
| 61 | 7 |  |  |  |  | 19 | foreach (@attributes) { | 
| 62 | 17 |  |  |  |  | 99 | my $value = _get( $context, $_->[1] ); | 
| 63 | 17 | 100 |  |  |  | 59 | $node->setAttribute( $_->[0], $value ) if defined $value; | 
| 64 |  |  |  |  |  |  | } | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 57 | 100 |  |  |  | 572 | if ( my $attr_defaults = _strip_attr( $node, 'tmpl-attr-defaults' ) ) { | 
| 68 |  |  |  |  |  |  | my @attributes | 
| 69 |  |  |  |  |  |  | = map { | 
| 70 | 4 |  |  |  |  | 39 | [   map { s/\\([:,])/$1/g; $_ } # remove backslash escaping | 
|  | 10 |  |  |  |  | 54 |  | 
|  | 20 |  |  |  |  | 37 |  | 
|  | 20 |  |  |  |  | 53 |  | 
| 71 |  |  |  |  |  |  | split qr/(? | 
| 72 |  |  |  |  |  |  | ] | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  | split qr/(? | 
| 75 |  |  |  |  |  |  | $attr_defaults; | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 4 |  |  |  |  | 16 | foreach (@attributes) { | 
| 78 | 10 |  |  |  |  | 42 | my $value = $node->getAttribute( $_->[0] ); | 
| 79 | 10 | 100 | 66 |  |  | 112 | $node->setAttribute( $_->[0], $_->[1] ) | 
| 80 |  |  |  |  |  |  | if ! defined $value || $value eq ''; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | my @children = grep { | 
| 85 | 57 |  |  |  |  | 623 | $_->nodeType eq XML_ELEMENT_NODE | 
|  | 49 |  |  |  |  | 483 |  | 
| 86 |  |  |  |  |  |  | } $node->childNodes; | 
| 87 | 57 |  |  |  |  | 264 | parse_node($_, $context) foreach @children; | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | sub _get { | 
| 91 | 58 |  |  | 58 |  | 105 | my ($context, $key) = @_; | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 58 | 50 |  |  |  | 125 | return '' if !defined $key; | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 58 | 100 |  |  |  | 133 | return $context if $key eq 'this'; | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 42 |  |  |  |  | 253 | my @parts = split qr/\./, $key; | 
| 98 | 42 |  |  |  |  | 108 | foreach (@parts) { | 
| 99 | 44 |  |  |  |  | 144 | $context = $context->{$_}; | 
| 100 |  |  |  |  |  |  | } | 
| 101 | 42 |  |  |  |  | 85 | return $context; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | sub _strip_attr { | 
| 105 | 329 |  |  | 329 |  | 551 | my ($node, $attr_name) = @_; | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 329 | 50 |  |  |  | 662 | if (my $attributes = $node->attributes) { | 
| 108 | 329 | 100 |  |  |  | 5309 | if (my $attr = $attributes->removeNamedItem($attr_name)) { | 
| 109 | 55 |  |  |  |  | 1717 | return $attr->nodeValue; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | 1; | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | __END__ |