--- /dev/null
+package PPI::Find;
+
+=pod
+
+=head1 NAME
+
+PPI::Find - Object version of the Element->find method
+
+=head1 SYNOPSIS
+
+ # Create the Find object
+ my $Find = PPI::Find->new( \&wanted );
+
+ # Return all matching Elements as a list
+ my @found = $Find->in( $Document );
+
+ # Can we find any matching Elements
+ if ( $Find->any_matches($Document) ) {
+ print "Found at least one matching Element";
+ }
+
+ # Use the object as an iterator
+ $Find->start($Document) or die "Failed to execute search";
+ while ( my $token = $Find->match ) {
+ ...
+ }
+
+=head1 DESCRIPTION
+
+PPI::Find is the primary PDOM searching class in the core PPI package.
+
+=head2 History
+
+It became quite obvious during the development of PPI that many of the
+modules that would be built on top of it were going to need large numbers
+of saved, storable or easily creatable search objects that could be
+reused a number of times.
+
+Although the internal ->find method provides a basic ability to search,
+it is by no means thorough. PPI::Find attempts to resolve this problem.
+
+=head2 Structure and Style
+
+PPI::Find provides a similar API to the popular L<File::Find::Rule>
+module for file searching, but without the ability to assemble queries.
+
+The implementation of a separate PPI::Find::Rule sub-class that does
+provide this ability is left as an exercise for the reader.
+
+=head2 The &wanted function
+
+At the core of each PPI::Find object is a "wanted" function that is
+passed a number of arguments and returns a value which controls the
+flow of the search.
+
+As the search executes, each Element will be passed to the wanted function
+in depth-first order.
+
+It will be provided with two arguments. The current Element to test as $_[0],
+and the top-level Element of the search as $_[1].
+
+The &wanted function is expected to return 1 (positive) if the Element
+matches the condition, 0 (false) if it does not, and undef (undefined) if
+the condition does not match, and the Find search should not descend to
+any of the current Element's children.
+
+Errors should be reported from the &wanted function via die, which will be
+caught by the Find object and returned as an error.
+
+=head1 METHODS
+
+=cut
+
+use strict;
+use Params::Util qw{_INSTANCE};
+
+use vars qw{$VERSION};
+BEGIN {
+ $VERSION = '1.206';
+}
+
+
+
+
+
+#####################################################################
+# Constructor
+
+=pod
+
+=head2 new &wanted
+
+The C<new> constructor takes a single argument of the &wanted function,
+as described above and creates a new search.
+
+Returns a new PPI::Find object, or C<undef> if not passed a CODE reference.
+
+=cut
+
+sub new {
+ my $class = ref $_[0] ? ref shift : shift;
+ my $wanted = ref $_[0] eq 'CODE' ? shift : return undef;
+
+ # Create the object
+ my $self = bless {
+ wanted => $wanted,
+ }, $class;
+
+ $self;
+}
+
+=pod
+
+=head2 clone
+
+The C<clone> method creates another instance of the same Find object.
+
+The cloning is done safely, so if your existing Find object is in the
+middle of an iteration, the cloned Find object will not also be in the
+iteration and can be safely used independently.
+
+Returns a duplicate PPI::Find object.
+
+=cut
+
+sub clone {
+ my $self = ref $_[0] ? shift
+ : die "->clone can only be called as an object method";
+ my $class = ref $self;
+
+ # Create the object
+ my $clone = bless {
+ wanted => $self->{wanted},
+ }, $class;
+
+ $clone;
+}
+
+
+
+
+
+####################################################################
+# Search Execution Methods
+
+=pod
+
+=head2 in $Document [, array_ref => 1 ]
+
+The C<in> method starts and completes a full run of the search.
+
+It takes as argument a single L<PPI::Element> object which will
+serve as the top of the search process.
+
+Returns a list of PPI::Element objects that match the condition
+described by the &wanted function, or the null list on error.
+
+You should check the ->errstr method for any errors if you are
+returned the null list, which may also mean simply that no Elements
+were found that matched the condition.
+
+Because of this need to explicitly check for errors, an alternative
+return value mechanism is provide. If you pass the C<array_ref => 1>
+parameter to the method, it will return the list of matched Elements
+as a reference to an ARRAY. The method will return false if no elements
+were matched, or C<undef> on error.
+
+The ->errstr method can still be used to get the error message as normal.
+
+=cut
+
+sub in {
+ my $self = shift;
+ my $Element = shift;
+ my %params = @_;
+ delete $self->{errstr};
+
+ # Are we already acting as an iterator
+ if ( $self->{in} ) {
+ return $self->_error('->in called while another search is in progress', %params);
+ }
+
+ # Get the root element for the search
+ unless ( _INSTANCE($Element, 'PPI::Element') ) {
+ return $self->_error('->in was not passed a PPI::Element object', %params);
+ }
+
+ # Prepare the search
+ $self->{in} = $Element;
+ $self->{matches} = [];
+
+ # Execute the search
+ eval {
+ $self->_execute;
+ };
+ if ( $@ ) {
+ my $errstr = $@;
+ $errstr =~ s/\s+at\s+line\s+.+$//;
+ return $self->_error("Error while searching: $errstr", %params);
+ }
+
+ # Clean up and return
+ delete $self->{in};
+ if ( $params{array_ref} ) {
+ if ( @{$self->{matches}} ) {
+ return delete $self->{matches};
+ }
+ delete $self->{matches};
+ return '';
+ }
+
+ # Return as a list
+ my $matches = delete $self->{matches};
+ @$matches;
+}
+
+=pod
+
+=head2 start $Element
+
+The C<start> method lets the Find object act as an iterator. The method
+is passed the parent PPI::Element object as for the C<in> method, but does
+not accept any parameters.
+
+To simplify error handling, the entire search is done at once, with the
+results cached and provided as-requested.
+
+Returns true if the search completes, and false on error.
+
+=cut
+
+sub start {
+ my $self = shift;
+ my $Element = shift;
+ delete $self->{errstr};
+
+ # Are we already acting as an iterator
+ if ( $self->{in} ) {
+ return $self->_error('->in called while another search is in progress');
+ }
+
+ # Get the root element for the search
+ unless ( _INSTANCE($Element, 'PPI::Element') ) {
+ return $self->_error('->in was not passed a PPI::Element object');
+ }
+
+ # Prepare the search
+ $self->{in} = $Element;
+ $self->{matches} = [];
+
+ # Execute the search
+ eval {
+ $self->_execute;
+ };
+ if ( $@ ) {
+ my $errstr = $@;
+ $errstr =~ s/\s+at\s+line\s+.+$//;
+ $self->_error("Error while searching: $errstr");
+ return undef;
+ }
+
+ 1;
+}
+
+=pod
+
+=head2 match
+
+The C<match> method returns the next matching Element in the iteration.
+
+Returns a PPI::Element object, or C<undef> if there are no remaining
+Elements to be returned.
+
+=cut
+
+sub match {
+ my $self = shift;
+ return undef unless $self->{matches};
+
+ # Fetch and return the next match
+ my $match = shift @{$self->{matches}};
+ return $match if $match;
+
+ $self->finish;
+ undef;
+}
+
+=pod
+
+=head2 finish
+
+The C<finish> method provides a mechanism to end iteration if you wish to
+stop the iteration prematurely. It resets the Find object and allows it to
+be safely reused.
+
+A Find object will be automatically finished when C<match> returns false.
+This means you should only need to call C<finnish> when you stop
+iterating early.
+
+You may safely call this method even when not iterating and it will return
+without failure.
+
+Always returns true
+
+=cut
+
+sub finish {
+ my $self = shift;
+ delete $self->{in};
+ delete $self->{matches};
+ delete $self->{errstr};
+ 1;
+}
+
+
+
+
+
+#####################################################################
+# Support Methods and Error Handling
+
+sub _execute {
+ my $self = shift;
+ my $wanted = $self->{wanted};
+ my @queue = ( $self->{in} );
+
+ # Pull entries off the queue and hand them off to the wanted function
+ while ( my $Element = shift @queue ) {
+ my $rv = &$wanted( $Element, $self->{in} );
+
+ # Add to the matches if returns true
+ push @{$self->{matches}}, $Element if $rv;
+
+ # Continue and don't descend if it returned undef
+ # or if it doesn't have children
+ next unless defined $rv;
+ next unless $Element->isa('PPI::Node');
+
+ # Add the children to the head of the queue
+ if ( $Element->isa('PPI::Structure') ) {
+ unshift @queue, $Element->finish if $Element->finish;
+ unshift @queue, $Element->children;
+ unshift @queue, $Element->start if $Element->start;
+ } else {
+ unshift @queue, $Element->children;
+ }
+ }
+
+ 1;
+}
+
+=pod
+
+=head2 errstr
+
+The C<errstr> method returns the error messages when a given PPI::Find
+object fails any action.
+
+Returns a string, or C<undef> if there is no error.
+
+=cut
+
+sub errstr {
+ shift->{errstr};
+}
+
+sub _error {
+ my $self = shift;
+ $self->{errstr} = shift;
+ my %params = @_;
+ $params{array_ref} ? undef : ();
+}
+
+1;
+
+=pod
+
+=head1 TO DO
+
+- Implement the L<PPI::Find::Rule> class
+
+=head1 SUPPORT
+
+See the L<support section|PPI/SUPPORT> in the main module.
+
+=head1 AUTHOR
+
+Adam Kennedy E<lt>adamk@cpan.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2001 - 2009 Adam Kennedy.
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=cut