factor out application of a match+filter to a stream into a Transform object
[catagits/HTML-Zoom.git] / lib / HTML / Zoom / FilterStream.pm
CommitLineData
456a815d 1package HTML::Zoom::FilterStream;
2
3use strict;
4use warnings FATAL => 'all';
5f74b883 5use base qw(HTML::Zoom::StreamBase);
456a815d 6
7sub new {
8 my ($class, $args) = @_;
655965b3 9 if ($args->{filters}) {
10 die "Single filter please (XXX FIXME)"
11 unless @{$args->{filters}} == 1;
12 $args->{filter} = $args->{filters}[0];
13 }
456a815d 14 bless(
15 {
16 _stream => $args->{stream},
17 _match => $args->{match},
18 _filter => $args->{filter},
d80786d0 19 _zconfig => $args->{zconfig},
456a815d 20 },
21 $class
22 );
23}
24
b5a48c47 25sub _next {
456a815d 26 my ($self) = @_;
27
28 # if our main stream is already gone then we can short-circuit
29 # straight out - there's no way for an alternate stream to be there
30
31 return unless $self->{_stream};
32
33 # if we have an alternate stream (provided by a filter call resulting
34 # from a match on the main stream) then we want to read from that until
35 # it's gone - we're still effectively "in the match" but this is the
36 # point at which that fact is abstracted away from downstream consumers
37
38 if (my $alt = $self->{_alt_stream}) {
39
40 if (my ($evt) = $alt->next) {
41 return $evt;
42 }
43
44 # once the alternate stream is exhausted we can throw it away so future
45 # requests fall straight through to the main stream
46
47 delete $self->{_alt_stream};
48 }
49
50 # if there's no alternate stream currently, process the main stream
51
52 while (my ($evt) = $self->{_stream}->next) {
53
54 # don't match this event? return it immediately
55
56 return $evt unless $evt->{type} eq 'OPEN' and $self->{_match}->($evt);
57
58 # run our filter routine against the current event
59
60 my ($res) = $self->{_filter}->($evt, $self->{_stream});
61
62 # if the result is just an event, we can return that now
63
64 return $res if ref($res) eq 'HASH';
65
66 # if no result at all, jump back to the top of the loop to get the
67 # next event and try again - the filter has eaten this one
68
69 next unless defined $res;
70
71 # ARRAY means a pair of [ $evt, $new_stream ]
72
73 if (ref($res) eq 'ARRAY') {
74 $self->{_alt_stream} = $res->[1];
75 return $res->[0];
76 }
77
78 # the filter returned a stream - if it contains something return the
79 # first entry and stash it as the new alternate stream
80
81 if (my ($new_evt) = $res->next) {
82 $self->{_alt_stream} = $res;
83 return $new_evt;
84 }
85
86 # we got a new alternate stream but it turned out to be empty
87 # - this will happens for e.g. with an in place close (<foo />) that's
88 # being removed. In that case, we fall off to loop back round and try
89 # the next event from our main stream
90 }
91
92 # main stream exhausted so throw it away so we hit the short circuit
93 # at the top and return nothing to indicate to our caller we're done
94
95 delete $self->{_stream};
96 return;
97}
98
991;