1 2package Tree::Simple::Visitor::FindByPath; 3 4use strict; 5use warnings; 6 7our $VERSION = '0.03'; 8 9use Scalar::Util qw(blessed); 10 11use base qw(Tree::Simple::Visitor); 12 13sub new { 14 my ($_class) = @_; 15 my $class = ref($_class) || $_class; 16 my $visitor = {}; 17 bless($visitor, $class); 18 $visitor->_init(); 19 return $visitor; 20} 21 22sub _init { 23 my ($self) = @_; 24 $self->{search_path} = undef; 25 $self->{success} = 0; 26 $self->SUPER::_init(); 27} 28 29sub setSearchPath { 30 my ($self, @path) = @_; 31 (@path) || die "Insufficient Arguments : You must specify a path"; 32 $self->{search_path} = \@path; 33} 34 35sub visit { 36 my ($self, $tree) = @_; 37 (blessed($tree) && $tree->isa("Tree::Simple")) 38 || die "Insufficient Arguments : You must supply a valid Tree::Simple object"; 39 40 # reset our success flag 41 $self->{success} = 0; 42 43 # get our filter function 44 my $func; 45 if ($self->{_filter_function}) { 46 $func = sub { 47 my ($tree, $test) = @_; 48 return (($self->{_filter_function}->($tree) . "") eq $test); 49 }; 50 } 51 else { 52 $func = sub { 53 my ($tree, $test) = @_; 54 return (($tree->getNodeValue() . "") eq $test); 55 }; 56 } 57 58 # get ready with our results 59 my @results; 60 61 # get our path 62 my @path = @{$self->{search_path}}; 63 64 # get our variables ready 65 my $current_path; 66 my $current_tree = $tree; 67 68 # check to see if we have been 69 # asked to include the trunk 70 if ($self->includeTrunk()) { 71 # if we dont match the root of the path 72 # then we have failed already and so return 73 $self->setResults(()) && return 74 unless $func->($current_tree, $path[0]); 75 # if we do match, then remove it off the path 76 shift @path; 77 } 78 79 TOP: { 80 # if we have no more @path we have found it 81 unless (@path) { 82 # store the current tree as 83 # our last result 84 $self->setResults(@results, $current_tree); 85 # and set the sucess flag 86 $self->{success} = 1; 87 return; 88 } 89 # otherwise we need to keep looking ... 90 # get the next element in the path 91 $current_path = shift @path; 92 # now check all the current tree's children 93 # for a match 94 foreach my $child ($current_tree->getAllChildren()) { 95 if ($func->($child, $current_path)) { 96 # if we find a match, then 97 # we store the current tree 98 # in our results, and 99 push @results => $current_tree; 100 # we change our current tree 101 $current_tree = $child; 102 # and go back to the TOP 103 goto TOP; 104 } 105 } 106 107 # if we do not find a match, then we can fall off 108 # this block and the whole subroutine for that matter 109 # since we know the match has failed. 110 push @results => $current_tree 111 if (@path || $self->{success} == 0) && $current_tree != $tree; 112 } 113 # we do however, store the 114 # results as far as we got, 115 # so that the user can maybe 116 # do something else to recover 117 $self->setResults(@results); 118} 119 120sub getResult { 121 my ($self) = @_; 122 # if we did not succeed, then 123 # we return undef, ... 124 return undef unless $self->{success}; 125 # otherwise we return the 126 # last in the results 127 return $self->getResults()->[-1]; 128} 129 1301; 131 132__END__ 133 134=head1 NAME 135 136Tree::Simple::Visitor::FindByPath - A Visitor for finding an element in a Tree::Simple hierarchy with a path 137 138=head1 SYNOPSIS 139 140 use Tree::Simple::Visitor::FindByPath; 141 142 # create a visitor object 143 my $visitor = Tree::Simple::Visitor::FindByPath->new(); 144 145 # set the search path for our tree 146 $visitor->setSearchPath(qw(1 1.2 1.2.2)); 147 148 # pass the visitor to a tree 149 $tree->accept($visitor); 150 151 # fetch the result, which will 152 # be the Tree::Simple object that 153 # we have found, or undefined 154 my $result = $visitor->getResult() || die "No Tree found"; 155 156 # our result's node value should match 157 # the last element in our path 158 print $result->getNodeValue(); # this should print 1.2.2 159 160=head1 DESCRIPTION 161 162Given a path and Tree::Simple hierarchy, this Visitor will attempt to find the node specified by the path. 163 164=head1 METHODS 165 166=over 4 167 168=item B<new> 169 170There are no arguments to the constructor the object will be in its default state. You can use the C<setSearchPath> and C<setNodeFilter> methods to customize its behavior. 171 172=item B<includeTrunk ($boolean)> 173 174Based upon the value of C<$boolean>, this will tell the visitor to include the trunk of the tree in the search as well. 175 176=item B<setSearchPath (@path)> 177 178This is the path we will attempt to follow down the tree. We will do a stringified comparison of each element of the path and the current tree's node (or the value returned by the node filter if it is set). 179 180=item B<setNodeFilter ($filter_function)> 181 182This method accepts a CODE reference as its C<$filter_function> argument and throws an exception if it is not a code reference. This code reference is used to filter the tree nodes as they are collected. This can be used to customize output, or to gather specific information from a more complex tree node. The filter function should accept a single argument, which is the current Tree::Simple object. 183 184=item B<visit ($tree)> 185 186This is the method that is used by Tree::Simple's C<accept> method. It can also be used on its own, it requires the C<$tree> argument to be a Tree::Simple object (or derived from a Tree::Simple object), and will throw and exception otherwise. 187 188=item B<getResult> 189 190This method will return the tree found at the specified path (set by the C<setSearchPath> method) or C<undef> if no tree is found. 191 192=item B<getResults> 193 194This method will return the tree's that make up the path specified in C<setSearchPath>. In the case of a failed search, this can be used to find the elements which did successfully match along the way. 195 196=back 197 198=head1 BUGS 199 200None that I am aware of. Of course, if you find a bug, let me know, and I will be sure to fix it. 201 202=head1 CODE COVERAGE 203 204See the B<CODE COVERAGE> section in L<Tree::Simple::VisitorFactory> for more inforamtion. 205 206=head1 SEE ALSO 207 208These Visitor classes are all subclasses of B<Tree::Simple::Visitor>, which can be found in the B<Tree::Simple> module, you should refer to that module for more information. 209 210=head1 AUTHOR 211 212stevan little, E<lt>stevan@iinteractive.comE<gt> 213 214=head1 COPYRIGHT AND LICENSE 215 216Copyright 2004, 2005 by Infinity Interactive, Inc. 217 218L<http://www.iinteractive.com> 219 220This library is free software; you can redistribute it and/or modify 221it under the same terms as Perl itself. 222 223=cut 224 225