X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FHTML%2FZoom%2FFilterBuilder.pm;h=316d56c6a1214ecdd04b00e12c328cbfde8c3e1a;hb=a5439c3228d3d0d252003910b4d8c9e3eb1818e0;hp=64b85b2c75a037dd0c0439865fcf343853800b41;hpb=b4d044eb69c61bc8265e79ab5190b9d9795937e1;p=catagits%2FHTML-Zoom.git diff --git a/lib/HTML/Zoom/FilterBuilder.pm b/lib/HTML/Zoom/FilterBuilder.pm index 64b85b2..316d56c 100644 --- a/lib/HTML/Zoom/FilterBuilder.pm +++ b/lib/HTML/Zoom/FilterBuilder.pm @@ -1,36 +1,28 @@ package HTML::Zoom::FilterBuilder; -use Devel::Dwarn; - use strict; use warnings FATAL => 'all'; +use base qw(HTML::Zoom::SubObject); use HTML::Zoom::CodeStream; -sub new { bless({}, shift) } - sub _stream_from_code { - HTML::Zoom::CodeStream->new({ code => $_[1] }) + shift->_zconfig->stream_utils->stream_from_code(@_) } sub _stream_from_array { - shift; # lose $self - HTML::Zoom::CodeStream->from_array(@_) + shift->_zconfig->stream_utils->stream_from_array(@_) +} + +sub _stream_from_proto { + shift->_zconfig->stream_utils->stream_from_proto(@_) } sub _stream_concat { - shift; # lose $self - my @streams = @_; - my $cur_stream = shift(@streams) or die "No streams passed"; - HTML::Zoom::CodeStream->new({ - code => sub { - return unless $cur_stream; - my $evt; - until (($evt) = $cur_stream->next) { - return unless $cur_stream = shift(@streams); - } - return $evt; - } - }); + shift->_zconfig->stream_utils->stream_concat(@_) +} + +sub _flatten_stream_of_streams { + shift->_zconfig->stream_utils->flatten_stream_of_streams(@_) } sub set_attribute { @@ -82,7 +74,8 @@ sub remove_attribute { sub collect { my ($self, $options) = @_; - my ($into, $passthrough, $inside) = @{$options}{qw(into passthrough inside)}; + my ($into, $passthrough, $content, $filter) = + @{$options}{qw(into passthrough content filter)}; sub { my ($evt, $stream) = @_; # We wipe the contents of @$into here so that other actions depending @@ -90,15 +83,16 @@ sub collect { # I -suspect- it's better for that state reset to be managed here; if it # ever becomes painful the decision should be revisited if ($into) { - @$into = $inside ? () : ($evt); + @$into = $content ? () : ($evt); } if ($evt->{is_in_place_close}) { - return $evt if $passthrough || $inside; + return $evt if $passthrough || $content; return; } my $name = $evt->{name}; my $depth = 1; - my $_next = $inside ? 'peek' : 'next'; + my $_next = $content ? 'peek' : 'next'; + $stream = do { local $_ = $stream; $filter->($stream) } if $filter; my $collector = $self->_stream_from_code(sub { return unless $stream; while (my ($evt) = $stream->$_next) { @@ -106,21 +100,26 @@ sub collect { $depth-- if ($evt->{type} eq 'CLOSE'); unless ($depth) { undef $stream; - return if $inside; + return if $content; push(@$into, $evt) if $into; return $evt if $passthrough; return; } push(@$into, $evt) if $into; - $stream->next if $inside; + $stream->next if $content; return $evt if $passthrough; } die "Never saw closing before end of source"; }); - return ($passthrough||$inside) ? [ $evt, $collector ] : $collector; + return ($passthrough||$content) ? [ $evt, $collector ] : $collector; }; } +sub collect_content { + my ($self, $options) = @_; + $self->collect({ %{$options||{}}, content => 1 }) +} + sub add_before { my ($self, $events) = @_; sub { return $self->_stream_from_array(@$events, $_[0]) }; @@ -128,17 +127,18 @@ sub add_before { sub add_after { my ($self, $events) = @_; + my $coll_proto = $self->collect({ passthrough => 1 }); sub { my ($evt) = @_; my $emit = $self->_stream_from_array(@$events); - my $coll = $self->collect({ passthrough => 1 })->(@_); + my $coll = &$coll_proto; return ref($coll) eq 'HASH' # single event, no collect ? [ $coll, $emit ] : [ $coll->[0], $self->_stream_concat($coll->[1], $emit) ]; }; } -sub prepend_inside { +sub prepend_content { my ($self, $events) = @_; sub { my ($evt) = @_; @@ -152,8 +152,9 @@ sub prepend_inside { }; } -sub append_inside { +sub append_content { my ($self, $events) = @_; + my $coll_proto = $self->collect({ passthrough => 1, content => 1 }); sub { my ($evt) = @_; if ($evt->{is_in_place_close}) { @@ -162,18 +163,19 @@ sub append_inside { @$events, { type => 'CLOSE', name => $evt->{name} } ) ]; } - my $coll = $self->collect({ passthrough => 1, inside => 1 })->(@_); + my $coll = &$coll_proto; my $emit = $self->_stream_from_array(@$events); return [ $coll->[0], $self->_stream_concat($coll->[1], $emit) ]; }; } sub replace { - my ($self, $events, $options) = @_; + my ($self, $replace_with, $options) = @_; + my $coll_proto = $self->collect($options); sub { my ($evt, $stream) = @_; - my $emit = $self->_stream_from_array(@$events); - my $coll = $self->collect($options)->(@_); + my $emit = $self->_stream_from_proto($replace_with); + my $coll = &$coll_proto; # For a straightforward replace operation we can, in fact, do the emit # -before- the collect, and my first cut did so. However in order to # use the captured content in generating the new content, we need @@ -191,4 +193,52 @@ sub replace { }; } +sub replace_content { + my ($self, $replace_with, $options) = @_; + $self->replace($replace_with, { %{$options||{}}, content => 1 }) +} + +sub repeat { + my ($self, $repeat_for, $options) = @_; + $options->{into} = \my @into; + my @between; + my $repeat_between = delete $options->{repeat_between}; + if ($repeat_between) { + $options->{filter} = sub { + $_->select($repeat_between)->collect({ into => \@between }) + }; + } + my $repeater = sub { + my $s = $self->_stream_from_proto($repeat_for); + # We have to test $repeat_between not @between here because + # at the point we're constructing our return stream @between + # hasn't been populated yet - but we can test @between in the + # map routine because it has been by then and that saves us doing + # the extra stream construction if we don't need it. + $self->_flatten_stream_of_streams(do { + if ($repeat_between) { + $s->map(sub { + local $_ = $self->_stream_from_array(@into); + (@between && $s->peek) + ? $self->_stream_concat( + $_[0]->($_), $self->_stream_from_array(@between) + ) + : $_[0]->($_) + }) + } else { + $s->map(sub { + local $_ = $self->_stream_from_array(@into); + $_[0]->($_) + }) + } + }) + }; + $self->replace($repeater, $options); +} + +sub repeat_content { + my ($self, $repeat_for, $options) = @_; + $self->repeat($repeat_for, { %{$options||{}}, content => 1 }) +} + 1;