[Bioperl-l] interface checking code
Matthew Pocock
matthew_pocock@yahoo.co.uk
Thu, 26 Sep 2002 23:07:05 +0100
This is a multi-part message in MIME format.
--------------000807060907020308080201
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Hi,
I've knocked up a proof-of-concept module for checking if interfaces are
implemented fully by packages. The skeleton code can be munged arround
to check all sorts of usefull properties, and could be augmented with
abstract method lists, warnings when methods are defined by more than
one implementation class and so on.
It uses the *{$packageName . ::} tricks to get package symbol tables
back, and $glob{TERM} magic (e.g. $glob{CODE} to access the code slot)
to actualy check for things. It's un-podded, but there's demo script
with some comments.
I would envisage this being used in the .t files to make sure that a
concrete class is just that e.g. test 1 would be a call to:
Interface->is_not_abstract($package_to_test)
and if this returns true, fail (fataly probably). It's probably not
worth putting this test in modules directly (e.g. in an INIT block)
untill/unless modules are stable, or some debug flag is available to
control this.
If you think we should be able to check a different sort of inheritance
or implementation property, I'm all ears, and can probably come up with
some code that does something close.
Have fun with this - it's realy not that magical.
Matthew
--
BioJava Consulting LTD - Support and training for BioJava
http://www.biojava.co.uk
--------------000807060907020308080201
Content-Type: text/plain;
name="Interface.t"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="Interface.t"
package FooI;
sub meth_1 {};
sub meth_2 {};
package Bar;
@ISA = ("FooI");
sub meth_1 {};
sub meth_2 {}
package Baz;
@ISA = ("FooI");
sub meth_1{};
package main;
use Interface;
use LWP;
use CGI;
print "Checking for exact interface implementation\n";
Interface->implements_exactly("Bar", "FooI");
Interface->implements_exactly("Baz", "FooI");
print "Checking for over-ride exact interface implementation\n";
Interface->over_rides_exactly("Bar", "FooI");
Interface->over_rides_exactly("Baz", "FooI");
print "Checking for abstract methods\n";
Interface->is_not_abstract("Bar");
Interface->is_not_abstract("Baz");
--------------000807060907020308080201
Content-Type: text/plain;
name="Interface.pm"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="Interface.pm"
package Interface;
use strict;
sub implements_exactly {
my ($self, $package, $interface) = @_;
my %abstract = $self->_find_methods($interface);
my %concrete = $self->_find_methods($package);
my @missing_methods = $self->_find_all_missing(\%abstract, \%concrete);
if(@missing_methods) {
print "Methods missing from $package defined in $interface:\n";
foreach my $meth (@missing_methods) {
print "\t$meth\n";
}
return 0;
}
return 1;
}
sub over_rides_exactly {
my ($self, $package, $interface) = @_;
my %abstract = $self->_find_methods($interface);
my %concrete = $self->_find_methods($package);
my @missing_methods = $self->_find_all_not_implemented(\%abstract, \%concrete);
if(@missing_methods) {
print "Methods missing from $package defined in $interface:\n";
foreach my $meth (@missing_methods) {
print "\t$meth\n";
}
return 0;
}
return 1;
}
sub is_not_abstract {
my ($self, $package) = @_;
my %abstract = $self->_find_all_methods($package, "I\$");
my %concrete = $self->_find_all_methods($package);
my @missing_methods = $self->_find_all_not_implemented(\%abstract, \%concrete);
if(@missing_methods) {
print "Methods not implemented:\n";
foreach my $meth (@missing_methods) {
print "\t$meth\n";
}
return 0;
}
return 1;
}
sub _find_methods {
my ($self, $interface) = @_;
my %methods = ();
my %symTab;
{
no strict "refs";
%symTab = %{$interface . "::"};
}
foreach my $sym (keys(%symTab)) {
my $val = $symTab{$sym};
my $code = *{$val}{CODE};
if(defined $code) {
$methods{$sym} = $val;
}
}
return %methods
}
sub _find_all_methods {
my ($self, $package, $pattern) = @_;
my %methods;
if(!defined($pattern) || $package =~ m/$pattern/) {
%methods = $self->_find_methods($package);
} else {
%methods = ();
}
my $isa = ();
{
no strict "refs";
my $sym_tab = *{$package . "::"};
my $sym = $sym_tab->{ISA};
if(defined $sym) {
$isa = *{$sym}{ARRAY};
}
}
foreach my $sup (@$isa) {
my %sup_meth = $self->_find_all_methods($sup, $pattern);
my ($meth, $sub);
while(($meth, $sub) = each %sup_meth) {
if(!exists($methods{$meth})) {
$methods{$meth} = $sub;
}
}
}
return %methods;
}
sub _find_all_missing {
my ($self, $interface, $concrete) = @_;
my @missing = grep { !$concrete->{$_} } keys(%$interface);
return @missing
}
sub _find_all_not_implemented {
my ($self, $interface, $concrete) = @_;
my @missing = grep { !(exists($concrete->{$_})
&& exists($interface->{$_})
&& ($concrete->{$_} ne $interface->{$_}) )}
keys(%$interface);
return @missing
}
1;
--------------000807060907020308080201--