package Data::ObjSearch; use strict; use Carp; sub new { my ($class, $sub) = @_; my $self = bless({}, $class); if (ref($sub) eq 'CODE') { $self->{sub} = $sub; } elsif ($sub and !ref($sub)) { $self->{sub} = sub { return UNIVERSAL::isa($_[0], $sub); }; } else { croak "Pass a subroutine reference or package name.\n"; } $self->{records} = []; $self->{seen} = {}; return $self; } sub search { my ($class, $ref, $sub) = @_; my $self = ref($class) ? $class : $class->new($sub); $self->analyze($ref); return @{$self->{records}}; } sub analyze { my ($self, $data) = @_; my $ref = ref($data); return if (!$ref); my ($pack, $type, $id) = $data =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/; return if ($id && $self->{seen}->{$id}); $pack && ($self->check_and_push($data), $ref = $type); $id && ($self->{seen}->{$id} = 1); if ($ref eq 'HASH') { while (my ($key, $val) = each %{$data}) { $self->analyze($val); } } elsif ($ref eq 'ARRAY') { map { $self->analyze($_) } @$data; } elsif ($ref eq 'SCALAR') { map { $self->analyze($_) } $$data; } } sub check_and_push { my ($self, $data) = @_; push(@{$self->{records}}, $data) if $self->{sub}->($data); } 1;