File Coverage

blib/lib/Parse/Matroska/Element.pm
Criterion Covered Total %
statement 77 95 81.0
branch 31 50 62.0
condition 6 9 66.6
subroutine 11 13 84.6
pod 8 8 100.0
total 133 175 76.0


line stmt bran cond sub pod time code
1 1     1   20 use 5.008;
  1         3  
  1         38  
2 1     1   4 use strict;
  1         2  
  1         34  
3 1     1   18 use warnings;
  1         2  
  1         46  
4              
5             # ABSTRACT: a mid-level representation of an EBML element
6             package Parse::Matroska::Element;
7             {
8             $Parse::Matroska::Element::VERSION = '0.003';
9             }
10              
11 1     1   4 use Carp;
  1         1  
  1         56  
12 1     1   5 use List::Util qw{first};
  1         1  
  1         910  
13              
14             sub new {
15 25     25 1 29 my $class = shift;
16 25         27 my $self = {};
17 25         46 bless $self, $class;
18              
19 25         53 $self->initialize(@_);
20 25         50 return $self;
21             }
22              
23             sub initialize {
24 25     25 1 148 my ($self, %args) = @_;
25 25         75 for (keys %args) {
26 225         336 $self->{$_} = $args{$_};
27             }
28 25 50       105 $self->{depth} = 0 unless $self->{depth};
29             }
30              
31             sub skip {
32 0     0 1 0 my ($self) = @_;
33 0         0 my $reader = $self->{reader};
34 0 0       0 return unless $reader; # we don't have to skip if there's no reader
35 0         0 my $pos = $reader->getpos;
36 0 0       0 croak "Too late to skip, reads were already done"
37             if $pos ne $self->{data_pos};
38 0         0 $reader->skip($self->{content_len});
39             }
40              
41             sub get_value {
42 4     4 1 7 my ($self, $keep_bin) = @_;
43              
44 4 50       12 return undef if $self->{type} eq 'skip';
45 4 100       16 return $self->{value} if $self->{value};
46              
47 2 50       24 my $reader = $self->{reader} or
48             croak "The associated Reader has been deleted";
49              
50             # delay-loaded 'binary'
51 2 100       13 if ($self->{type} eq 'binary') {
52 1 50       4 croak "Cannot seek in the current Reader" unless $self->{data_pos};
53             # seek to the data position...
54 1         4 $reader->setpos($self->{data_pos});
55             # read the data, keeping it in value if requested
56 1 50       3 if ($keep_bin) {
57 0         0 $self->{value} = $reader->readlen($self->{content_len});
58 0         0 return $self->{value};
59             } else {
60 1         4 return $reader->readlen($self->{content_len});
61             }
62             }
63             }
64              
65             sub next_child {
66 28     28 1 37 my ($self, $read_bin) = @_;
67 28 50       57 return unless $self->{type} eq 'sub';
68              
69 28 100       56 if ($self->{_all_children_read}) {
70 1   50     6 my $idx = $self->{_last_child} ||= 0;
71 1 50       2 if ($idx == @{$self->{value}}) {
  1         3  
72             # reset the iterator, returning undef once
73 0         0 $self->{_last_child} = 0;
74 0         0 return;
75             }
76 1         2 my $ret = $self->{value}->[$idx];
77              
78 1         9 ++$idx;
79 1         2 $self->{_last_child} = $idx;
80 1         8 return $ret;
81             }
82              
83 27 100       51 my $len = defined $self->{remaining_len}
84             ? $self->{remaining_len}
85             : $self->{content_len};
86              
87 27 100       51 if ($len == 0) {
88             # we've read all children; switch into $self->{value} iteration mode
89 5         96 $self->{_all_children_read} = 1;
90             # return undef since the iterator will reset
91 5         12 return;
92             }
93              
94 22   100     455 $self->{pos_offset} ||= 0;
95 22         29 my $pos = $self->{data_pos};
96 22 50       40 my $reader = $self->{reader} or croak "The associated reader has been deleted";
97 22         59 $reader->setpos($pos);
98 22 50       82 $reader->{fh}->seek($self->{pos_offset}, 1) if $pos;
99              
100 22         177 my $chld = $reader->read_element($read_bin);
101 22 50       44 return undef unless defined $chld;
102 22         33 $self->{pos_offset} += $chld->{full_len};
103              
104 22         43 $self->{remaining_len} = $len - $chld->{full_len};
105              
106 22 50       619 if ($self->{remaining_len} < 0) {
107 0         0 croak "Child elements consumed $self->{remaining_len} more bytes than parent $self->{name} contained";
108             }
109              
110 22         413 $chld->{depth} = $self->{depth} + 1;
111 22   50     39 $self->{value} ||= [];
112              
113 22         21 push @{$self->{value}}, $chld;
  22         37  
114              
115 22         59 return $chld;
116             }
117              
118             sub all_children {
119 0     0 1 0 my ($self, $recurse, $read_bin) = @_;
120 0         0 $self->populate_children($recurse, $read_bin);
121 0         0 return $self->{value};
122             }
123              
124             sub children_by_name {
125 9     9 1 883 my ($self, $name) = @_;
126 9 50       19 return unless defined wantarray; # don't do work if work isn't wanted
127 9 50       21 croak "Element can't have children" unless $self->{type} eq 'sub';
128              
129 9         10 my @found = grep { $_->{name} eq $name } @{$self->{value}};
  35         67  
  9         17  
130 9 100       20 return @found if wantarray; # list
131 8 50       40 return shift @found if defined wantarray; # scalar
132             }
133              
134             sub populate_children {
135 9     9 1 14 my ($self, $recurse, $read_bin) = @_;
136              
137 9 100       31 return unless $self->{type} eq 'sub';
138              
139 5 50 66     6 if (@{$self->{value}} && $recurse) {
  5         32  
140             # only recurse
141 0         0 foreach (@{$self->{value}}) {
  0         0  
142 0         0 $_->populate_children($recurse, $read_bin);
143             }
144 0         0 return $self;
145             }
146              
147 5         11 while (my $chld = $self->next_child($read_bin)) {
148 21 100       56 $chld->populate_children($recurse, $read_bin) if $recurse;
149             }
150              
151 5         9 return $self;
152             }
153              
154             1;
155              
156             __END__