######### # Author: rmp # Maintainer: rmp # Created: 2003-05-20 # Last Modified: 2005-07-29 # Generic SourceAdaptor. Generates XML and manages callouts for DAS functions # package Bio::Das::ProServer::SourceAdaptor; =head1 AUTHOR Roger Pettett . Based on AGPServer by Tony Cox . Copyright (c) 2003 The Sanger Institute This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See DISCLAIMER.txt for disclaimers of warranty. =cut use strict; use Data::Dumper; sub new { my ($class, $defs) = @_; my $self = { 'dsn' => $defs->{'dsn'}, 'port' => $defs->{'port'}, 'hostname' => $defs->{'hostname'}, 'config' => $defs->{'config'}, '_data' => {}, '_sequence' => {}, '_features' => {}, 'capabilities' => { 'dsn' => "1.0", }, }; bless $self, $class; $self->init($defs); if(!exists($self->{'capabilities'}->{'stylesheet'}) && ($self->{'config'}->{'stylesheet'} || $self->{'config'}->{'stylesheetfile'})) { $self->{'capabilities'}->{'stylesheet'} = "1.0"; } return $self; } ######### # Called at adaptor initialisation # sub init {}; sub length { 0; } ######### # mapmaster for this source. overrides configuration 'mapmaster' setting # sub mapmaster {} ######### # description for this source. overrides configuration 'description' setting # sub description {} ######### # hook for optimising results to be returned. # default - do nothing # Not necessary for most circumstances, but useful for deciding on what sort # of coordinate system you return the results if more than one type is available. # sub init_segments {} ######### # returns a list of valid segments that this adaptor knows about # sub known_segments {} ######### # returns a list of valid segments that this adaptor knows about # sub segment_version {} sub dsn { my $self = shift; return $self->{'dsn'} || "unknown"; }; sub dsnversion { my $self = shift; return $self->{'dsnversion'} || "1.0"; }; sub start { 1; } sub end { my $self = shift; return $self->length(@_); } ######### # build the relevant transport configured for this adaptor # sub transport { my $self = shift; if(!exists $self->{'_transport'}) { my $transport = "Bio::Das::ProServer::SourceAdaptor::Transport::".$self->config->{'transport'}; eval "require $transport"; warn $@ if($@); $self->{'_transport'} = $transport->new({ 'dsn' => $self->{'dsn'}, # for debug purposes 'config' => $self->config(), }); } return $self->{'_transport'}; } ######### # config settings for this adaptor # sub config { my ($self, $config) = @_; $self->{'config'} = $config if($config); return $self->{'config'}; } ######### # helper use to determine if an adaptor implements a request # sub implements { my ($self, $method) = @_; return $method?(exists $self->{'capabilities'}->{$method}):undef; } ######### # capabilities header support # sub das_capabilities { my $self = shift; return join('; ', map { "$_/$self->{'capabilities'}->{$_}" } grep { defined $self->{'capabilities'}->{$_} } keys %{$self->{'capabilities'}}); } ######### # dsn response # sub das_dsn { my $self = shift; my $port = $self->{'port'}; my $host = $self->{'hostname'}; my $content = $self->open_dasdsn(); for my $adaptor ($self->config->adaptors()) { my $dsn = $adaptor->dsn(); my $dsnversion = $adaptor->dsnversion(); my $mapmaster = $adaptor->mapmaster() || $adaptor->config->{'mapmaster'} || "http://$host:$port/das/$dsn/"; my $description = $adaptor->description() || $adaptor->config->{'description'} || $dsn; $content .= qq( $dsn $mapmaster $description \n); } $content .= $self->close_dasdsn(); return ($content); } ######### # open dsn response # sub open_dasdsn { qq( \n); } ######### # close dsn response # sub close_dasdsn { qq(\n); } ######### # open features response # sub open_dasgff { my ($self) = @_; my $host = $self->{'hostname'}; my $port = $self->{'port'}; my $dsn = $self->dsn(); return qq( \n); } ######### # close features response # sub close_dasgff { qq( \n); } ######### # unknown segment error response # sub unknown_segment { my ($self, $seg) = @_; my $error = ""; $error .= qq( \n); return($error); } ######### # code refactoring function to generate the link parts of the DAS response # sub gen_link_das_response ($$$) { my ($link, $linktxt, $spacing) = @_; my $response = ""; #if $link is a reference to and array or hash use their contents as multiple links if(ref($link) eq "ARRAY") { while(my $k = shift @{$link}) { my $v; $v = shift @{$linktxt} if(ref($linktxt) eq "ARRAY"); $v ||= $linktxt; $response .= qq($spacing$v\n); } } elsif(ref($link) eq "HASH") { while(my ($k, $v) = each %$link) { $response .= qq($spacing$v\n); } } elsif($link) { $response .= qq($spacing$linktxt\n) if($link ne ""); } return $response; } ######### # code refactoring function to generate the feature parts of the DAS response # sub gen_feature_das_response ($$) { my ($feature, $spacing) = @_; my $response = ""; my $start = $feature->{'start'} || "0"; my $end = $feature->{'end'} || "0"; my $note = $feature->{'note'} || ""; my $id = $feature->{'id'} || ""; my $label = $feature->{'label'} || $id; my $type = $feature->{'type'} || ""; my $method = $feature->{'method'} || ""; my $group = $feature->{'group'} || ""; my $glabel = $feature->{'grouplabel'} || ""; my $gtype = $feature->{'grouptype'} || ""; my $gnote = $feature->{'groupnote'} || ""; my $glink = $feature->{'grouplink'} || ""; my $glinktxt = $feature->{'grouplinktxt'} || ""; my $score = $feature->{'score'} || ""; my $ori = $feature->{'ori'} || "0"; my $phase = $feature->{'phase'} || ""; my $link = $feature->{'link'} || ""; my $linktxt = $feature->{'linktxt'} || $link; my $tst = $feature->{'target_start'} || ""; my $tend = $feature->{'target_stop'} || ""; my $cat = (defined $feature->{'typecategory'})?qq(category="$feature->{'typecategory'}"):""; my $subparts = $feature->{'typesubparts'} || "no"; my $supparts = $feature->{'typessuperparts'} || "no"; my $ref = $feature->{'typesreference'} || "no"; $response .= qq($spacing\n); $response .= qq($spacing $type\n); $response .= qq($spacing $method\n) if($method ne ""); $response .= qq($spacing $start\n); $response .= qq($spacing $end\n); $response .= qq($spacing $score\n) if($score ne ""); $response .= qq($spacing $ori\n) if($ori ne ""); $response .= qq($spacing $phase\n) if($phase ne ""); $response .= qq($spacing $note\n) if($note ne ""); $response .= gen_link_das_response($link,$linktxt,"$spacing "); $response .= qq($spacing \n) if($tst ne "" && $tend ne ""); #if $group is a hash reference treat its keys as the multiple groups to be reported for this feature my %groups = (ref $group?%{$group}:($group=>{'grouplabel'=>$glabel,'grouptype'=>$gtype,'groupnote'=>$gnote, 'grouplink'=>$glink,'grouplinktxt'=>$glinktxt})); foreach my $groupi (keys %groups){ if($groupi ne ""){ my $groupinfo= $groups{$groupi}; my $glabeli= $groupinfo->{'grouplabel'}?qq(label="$groupinfo->{'grouplabel'}"):""; my $gtypei = $groupinfo->{'grouptype'}?qq(type="$groupinfo->{'grouptype'}"):""; my $gnotei = $groupinfo->{'groupnote'} || ""; my $glinki = $groupinfo->{'grouplink'} || ""; $response.= qq($spacing \n); }else{ my $glinktxti = $groupinfo->{'grouplinktxt'} || $glinki; $response.=qq(>\n); $response.=qq($spacing $gnotei\n) if($gnotei ne ""); $response.= gen_link_das_response($glinki,$glinktxti,"$spacing "); $response.=qq($spacing \n); } } } $response .= qq($spacing\n); return $response; } ######### # features response # sub das_features { my ($self, $opts) = @_; my $response = ""; $self->init_segments($opts->{'segments'}); ######### # features on segments # for my $seg (@{$opts->{'segments'}}) { my ($seg, $coords) = split(':', $seg); my ($start, $end) = split(',', $coords||""); my $segstart = $start || $self->start($seg) || ""; my $segend = $end || $self->end($seg) || ""; if ( $self->known_segments() ){ unless (grep /$seg/ , $self->known_segments() ){ $response .= $self->unknown_segment($seg); next; } } my $segment_version = $self->segment_version($seg) || "1.0"; $response .= qq( \n); for my $feature ($self->build_features({ 'segment' => $seg, 'start' => $start, 'end' => $end, })) { $response .= gen_feature_das_response($feature," "); } $response .= qq( \n); } ######### # escape ampersands # $response =~ s/&/&/smg; ######### # features by specific id # my $error_feature = 1; for my $fid (@{$opts->{'features'}}) { my @f = $self->build_features({ 'feature_id' => $fid, }); unless (@f) { $response .= $self->error_feature($fid); next; } for my $feature (@f) { my $seg = $feature->{'segment'} || ""; my $segstart = $feature->{'segment_start'} || $feature->{'start'} || ""; my $segend = $feature->{'segment_end'} || $feature->{'end'} || ""; my $segver = $feature->{'segment_version'} || "1.0"; $response .= qq( \n); $response .= gen_feature_das_response($feature," "); $response .= qq( \n); } } ######### # escape ampersands # $response =~ s/&/&/smg; return $response; } ######### # unknown feature error response # sub error_feature { my ($self, $f) = @_; qq( \n); } ######### # open dna/sequence response # sub open_dassequence { qq( \n); } ######### # dna/sequence response # sub das_dna { my ($self, $segref) = @_; my $response = ""; for my $seg (@$segref) { my ($seg, $coords) = split(':', $seg); my ($start, $end) = split(',', $coords||""); my $segstart = $start || $self->start($seg) || ""; my $segend = $end || $self->end($seg) || ""; my $sequence = $self->sequence({ 'segment' => $seg, 'start' => $start, 'end' => $end, }); my $seq = $sequence->{'seq'}; my $moltype = $sequence->{'moltype'}; my $len = CORE::length($seq); $response .= qq( \n); $response .= qq( \n $seq\n); $response .= qq( \n); $response .= qq( \n); } return $response; } sub open_dastypes { my $self = shift; my $host = $self->{'hostname'}; my $port = $self->{'port'}; my $dsn = $self->dsn(); qq( \n); } sub close_dastypes { qq( \n); } ######### # types response # sub das_types { my ($self, $opts) = @_; my $response = ""; my @types = (); my $data = {}; unless (@{$opts->{'segments'}}){ $data->{'anon'} = []; push (@{$data->{'anon'}},$self->build_types()); } else { for my $seg (@{$opts->{'segments'}}) { my ($seg, $coords) = split(':', $seg); my ($start, $end) = split(',', $coords||""); my $segstart = $start || $self->start($seg) || ""; my $segend = $end || $self->end($seg) || ""; $data->{$seg} = []; @types = $self->build_types({ 'segment' => $seg, 'start' => $start, 'end' => $end, }); push (@{$data->{$seg}}, @types); } } for my $seg (keys %{$data}) { my ($seg, $coords) = split(':', $seg); my ($start, $end) = split(',', $coords || ""); my $segstart = $start || $self->start($seg) || ""; my $segend = $end || $self->end($seg) || ""; if ($seg ne "anon") { $response .= qq( \n); } else { $response .= qq( \n); } for my $type (@{$data->{$seg}}) { $type->{'count'} ||= ""; my $method = qq(method="$type->{'method'}") if(defined $type->{'method'}); my $category = qq(category="$type->{'category'}") if(defined $type->{'category'}); $response .= qq( $type->{'count'}\n); } $response .= qq( \n); } return $response; } ######### # close dna/sequence response # sub close_dassequence { qq(\n); } ######### # open entrypoints response # sub open_dasep { my $self = shift; my $dsn = $self->dsn(); my $host = $self->{'hostname'}; my $port = $self->{'port'}; return qq( \n); } ######### # close entrypoints response # sub close_dasep { qq( \n); } sub das_entry_points { my $self = shift; my $content = ""; for my $ep ($self->build_entry_points()) { my $subparts = $ep->{'subparts'} || "yes"; # default to yes here as we're giving entrypoints $content .= qq( {'length'}" ); $content .= qq(subparts="$subparts" />\n); } return $content; } ######### # default stylesheet response # sub das_stylesheet { my $self = shift; if($self->config->{'stylesheet'}) { ######### # Inline stylesheet # return $self->config->{'stylesheet'}; } elsif($self->config->{'stylesheetfile'}) { ######### # import stylesheet file # my $ssf = $self->{'stylesheetfile'}; unless($ssf) { my ($fn) = $self->config->{'stylesheetfile'} =~ m|([a-z0-9_\./\-]+)|i; eval { my $fh; open($fh, $fn) or die "opening stylesheet '$fn': $!"; local $/ = undef; $ssf = <$fh>; close($fh); }; warn $@ if($@); } if(($self->config->{'cachestylesheetfile'}||"yes") eq "yes") { $self->{'stylesheetfile'} ||= $ssf; } $ssf and return $ssf; } return qq( black sanserif 0 black \n); } ######### # default homepage (non-standard) response # sub das_homepage { my $self = shift; if($self->config->{'homepage'}) { ######### # Inline homepage # return $self->config->{'homepage'}; } elsif($self->config->{'homepagefile'}) { ######### # import homepage file # my $hpf = $self->{'homepagefile'}; unless($hpf) { my ($fn) = $self->config->{'homepagefile'} =~ m|([a-z0-9_\./\-]+)|i; eval { my $fh; open($fh, $fn) or die "opening homepage '$fn': $!"; local $/ = undef; $hpf = <$fh>; close($fh); }; warn $@ if($@); } if(($self->config->{'cachehomepagefile'}||"yes") eq "yes") { $self->{'homepagefile'} ||= $hpf; } $hpf and return $hpf; } my $port = $self->{'port'}; my $host = $self->{'hostname'}; my $dsn = $self->dsn(); return qq( Source Information for $dsn

Source Information for $dsn

DSN
$dsn
Description
@{[$self->description() || $self->config->{'description'} || "none configured"]}
Mapmaster
@{[$self->mapmaster() || $self->config->{'mapmaster'} || "none configured"]}
\n); } 1;