#!/usr/bin/perl
package dictyBaseObject_base;
use strict;

use DBI;
use CGI qw/:all :html3/;
use CGI::Carp qw(fatalsToBrowser);

use lib "/usr/local/dicty/www_dictybase/db/lib/dictyBase/Objects";
use ConfigURLdictyBase;
use Expression_connection;
use Locus;
use Feature;
use dictyBaseid;
use Go;
use Reflink;
use Pdb_sequence;
use Motif;
use Display_seq;

use lib "/usr/local/dicty/www_dictybase/db/lib/common";
use Login qw (ConnectToDatabase);
use TextUtil qw (DeleteSpaceFromBegEnd);

{

####################################################################
# Class Globals
####################################################################

our %arab2Rom = (

		"1"=>"I",
		"2"=>"II",
		"3"=>"III",
		"4"=>"IV",
		"5"=>"V",
		"6"=>"VI",
		"7"=>"VII",
		"8"=>"VIII",
		"9"=>"IX",
		"10"=>"X",
		"11"=>"XI",
		"12"=>"XII",
		"13"=>"XIII",
		"14"=>"XIV",
		"15"=>"XV",
		"16"=>"XVI",
		"17"=>"Q"

		);

our $dbh;
our $database;
our $configUrl = ConfigURLdictyBase->new;
our $bread  = $configUrl->breadServerRoot."cgi-bin/";
our $Dictyostelium = $configUrl->DictyosteliumServerRoot."cgi-bin/";
our $wine  = $configUrl->dictyBaseCGIRoot;
our $fungiRoot; 

####################################################################
#
# This class, dictyBaseObject, is envisaged as a means of abstracting away
# several commonly used methods behind a simple interface.

####################################################################
sub new{
####################################################################
# Ideally the constructor should be able to be called with a named 
# argument, such as -orf, -gene, -alias, and should fill out an object
# with any other attributes that are needed.  In addition it should be 
# able to be called with an as yet unresolved name, and the object will 
# resolve it, and flesh out all the correct properties.  It is also envisaged
# that the object will store various URLs that pertain to the locus to
# which the object corresponds, such as various gene sequence resources
# links, pubmed links etc.

    my $self = {};

    bless $self, shift;

    my (%args) = @_;

    if (defined $args{'dbh'}) {
	$dbh = $args{'dbh'};
    }

    if (!$dbh){ # we need a database connection

       $dbh = &ConnectToDatabase($args{'database'}); # only one database connection at any one time
       $database = $args{'database'};

    }else{ # there's already a connection - check it's to the same database

	if ($args{'database'} && $database ne $args{'database'}){

	    die "Can't have dictyBaseObjects using different databases in a single program.\n";

	}

    }

    $self->{'_database'} = $args{'database'};
    if ($args{'database'} eq "dictyBase"){
	$self->{'_linker'} = "dictyBase";
	$fungiRoot = $wine."FUNGI/";
    }else{
	$self->{'_linker'} = "dictyBaseDEV";
	$fungiRoot = $wine."FUNDEV/";
    }

    $self->_init(@_);

    return $self;

}

####################################################################
sub DESTROY{
####################################################################
# nothing needs to be done 
# the connection is taken care of by an END method

}

####################################################################
sub _init{
####################################################################
# This method performs the initialization of the object

    my ($self, %args) = @_; # have to use named arguments

    $self->{'help'} = $args{'help'};

    if ($args{'query'}){ # a general query

	$self->{'_query'} = $args{'query'};

	$self->_resolveQuery; # grab all info we can

    }elsif ($args{'dictyBaseid'}){ # they know it's an dictyBaseid

	$self->{'_query'} = $args{'dictyBaseid'};

	my $isdictyBaseID = $self->_isdictyBaseID;

	if ($isdictyBaseID){ $self->_populateBydictyBaseID }else{ return };
	
    }elsif ($args{'locusName'}){ # they know it's a locus name

	$self->{'_query'} = $args{'locusName'};

	my $isLocusName =  $self->isLocusName;

	if ($isLocusName){ $self->_populateByLocus }else{ return };

    }elsif ($args{'featureName'}){ # they know it's a feature name

	$self->{'_query'} = $args{'featureName'};

	my $isFeatureName =  $self->isFeatureName;

	if ($isFeatureName){ $self->_populateByFeature }else{ return };

    }elsif ($args{'aliasName'}){ # they know it's an alias name

	$self->{'_query'} = $args{'aliasName'};

	my $isFeatureName =  $self->isAliasName;

	if ($isFeatureName){ $self->_populateByAlias }else{ return };

    }elsif ($args{'locusNo'}){ # they know the locus no

	$self->{'_locusNo'} = $args{'locusNo'};
	$self->_populateByLocus;

    }elsif ($args{'featureNo'}){ # they know the featureNo

	$self->{'_featureNo'} = $args{'featureNo'};
	$self->_populateByFeature;

    }elsif ($args{'aliasNo'}){ # they know the alias no

	$self->{'_aliasNo'} = $args{'aliasNo'};
	$self->_populateByAlias;

    }else{

	die "Unknown argument passed to dictyBaseObject constructor\n";

    }

    return if ($self->{'_isMultiple'} == 1); # if alias mapped to many loci

    $self->{'_standard name'} = $self->{'_locusName'}|| $self->{'_featureName'};

    # maybe want to move these out of the object intialization

    $self->_initORFMapURLs; # will need these for locus page, and for wild card search
    $self->_initCommonURLs;

    if ($self->{'_featureName'}){
	&GetProcessFunction; #### init process, function and component
    }

}

####################################################################
sub _resolveQuery{
####################################################################
# This subroutine resolves a query to the correct orf, gene and
# aliases

    my ($self) = shift;

    # first we simply check what we've got

    my ($isFeatureName, $isLocusName, $isAliasName);

    my $isdictyBaseID = $self->_isdictyBaseID;

    if (!$isdictyBaseID){ # only check the others if it's not an dictyBaseID

	$isFeatureName = $self->isFeatureName;
	$isLocusName =  $self->isLocusName;
	$isAliasName =  $self->isAliasName;

    }

    my $total = $isdictyBaseID + $isFeatureName + $isLocusName + $isAliasName;

    if ($total == 0){

	return; # can't fill out any object parameters, as it doesn't resolve

    }elsif ($total > 1){

	# need to cope here with the fact that something may resolve
	# as both a locus_name and a feature_name, simply because for 
	# some features they have the identical name (bar casing) for
	# the locus, eg snR48

	# we don't want to treat these as multiples, but instead
	# treat them as resolvable to a single enntity

	if ($isFeatureName && $isLocusName == 1){

	    my $sth = $dbh->prepare("SELECT LOCUS_NO
                                            FROM CGM_DDB.FEATURE
                                            WHERE FEATURE_NO = ?");

	    $sth->execute($self->{'_featureNo'});
	    
	    my $check = $sth->fetchrow;

	    $sth->finish;

	    if ($check != $self->{'_locusNo'}){
		$self->{'_isMultiple'} = 1; # it had more than one hit
		return; # can't correctly resolve
	    }

	}else{

	    $self->{'_isMultiple'} = 1; # it had more than one hit
	    return; # can't correctly resolve

	}
	
    }
    
    # if we get here, it must resolve to a single type, so we can fill out
    # the rest of the object

    if ($isdictyBaseID){

	$self->_populateBydictyBaseID;

    }elsif ($isFeatureName){ # requires we have the feature number

	$self->_populateByFeature;

    }elsif ($isLocusName){ # requires we have the locus no

	$self->_populateByLocus;

    }elsif ($isAliasName){

	$self->_populateByAlias;

    }
    
}

#############################################################################################
sub _getAllAliases{
#############################################################################################
# This method retrieves all aliases for the object

# For an object there may be aliases linked through the feature_no
# or through the locus_no, so need to check both

    my ($self) = shift;

    my ($sth, @aliases, $alias);

    if ($self->{'_featureNo'}){ # if it's not a locus

	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a
                                     WHERE FEATURE_NO = ?");

	$sth->execute($self->{'_featureNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    if ($self->{'_locusNo'}){
       
	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a, CGM_DDB.locus_alias la, CGM_DDB.locus l
                                     WHERE a.ALIAS_NO = la.ALIAS_NO
                                     AND la.LOCUS_NO = l.LOCUS_NO
                                     AND l.LOCUS_NO = ?");
	
	$sth->execute($self->{'_locusNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    $self->{'_alias'} = join (", ", @aliases);

}

#############################################################################################
sub getUniformAliases{
#############################################################################################
# This method retrieves aliases of the type 'Uniform' for the object
# For an object there may be aliases linked through the feature_no
# or through the locus_no, so need to check both

    my ($self) = shift;

    my ($sth, @aliases, $alias);

    if ($self->{'_featureNo'}){ # if it's not a locus

	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a
                                     WHERE FEATURE_NO = ? 
                                     AND a.ALIAS_TYPE = 'Uniform'");

	$sth->execute($self->{'_featureNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    if ($self->{'_locusNo'}){
       
	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a, CGM_DDB.locus_alias la, CGM_DDB.locus l
                                     WHERE a.ALIAS_NO = la.ALIAS_NO
                                     AND la.LOCUS_NO = l.LOCUS_NO
                                     AND l.LOCUS_NO = ? 
                                     AND a.ALIAS_TYPE = 'Uniform'");
	
	$sth->execute($self->{'_locusNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    $self->{'_uniformAlias'} = join (", ", @aliases);
    
    return $self->{'_uniformAlias'};

}


#############################################################################################
sub getNonUniformProteinNameAliases{
#############################################################################################
# This method retrieves aliases of the type 'Non-uniform' and 'Protein name' for the object
# For an object there may be aliases linked through the feature_no
# or through the locus_no, so need to check both

    my ($self) = shift;

    my ($sth, @aliases, $alias);

    if ($self->{'_featureNo'}){ # if it's not a locus

	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a
                                     WHERE FEATURE_NO = ? 
                                     AND a.ALIAS_TYPE in ('Non-uniform', 'Protein name')");

	$sth->execute($self->{'_featureNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    if ($self->{'_locusNo'}){
       
	$sth = $dbh->prepare("SELECT ALIAS_NAME
                                     FROM CGM_DDB.ALIAS a, CGM_DDB.locus_alias la, CGM_DDB.locus l
                                     WHERE a.ALIAS_NO = la.ALIAS_NO
                                     AND la.LOCUS_NO = l.LOCUS_NO
                                     AND l.LOCUS_NO = ? 
                                     AND a.ALIAS_TYPE in ('Non-uniform', 'Protein name')");
	
	$sth->execute($self->{'_locusNo'});

	while ($alias = $sth->fetchrow){
	    
	    push (@aliases, $alias);
	    
	}
	
	$sth->finish;
	
    }

    $self->{'_NonUniformProteinNameAlias'} = join (", ", @aliases);
    
    return $self->{'_NonUniformProteinNameAlias'};

}

#############################################################################
sub _isdictyBaseID{
#############################################################################
# This method tries to resolve an dictyBaseID to either a locus and/or a feature, or
# a deleted dictyBaseid, then returns a boolean to indicate successful resolution

# it will set any object attributes that it was able to retrieve

    my ($self) = @_;

    my $query = uc($self->{'_query'});

    return (0) if ($query!~/^\DDB\d{7}$/i); # it's not an dictyBaseID


    my ($dictyBaseid, $type);

    my $dictyBaseidObj = dictyBaseid->new(dbh=>$dbh,
	                      dictyBaseid=>$query);
    if (!$dictyBaseidObj) {
	return 0;
    }

    $self->{'_isdictyBaseID'} = 1;
    if ($dictyBaseidObj->dictyBaseid_type eq 'Primary') {
	$self->{'_dictyBaseidTable'} = $dictyBaseidObj->tab_name;
    }
    else {
	$self->{'_dictyBaseidTable'} = 'dictyBaseID_OTHER';
	$self->{'_dictyBaseidType'} = $dictyBaseidObj->dictyBaseid_type;
	if ($dictyBaseidObj->dictyBaseid_type eq "Deleted") {
		$self->{'_dictyBaseid'} = $dictyBaseidObj->dictyBaseid;
	}
    }
    return 1;
}

#############################################################################
sub isFeatureName{
#############################################################################
# this method determines if the passed in query is a feature_name

    my ($self) = @_;

    my $sth = $dbh->prepare("SELECT FEATURE_NO
                                    FROM CGM_DDB.feature
                                    WHERE UPPER(FEATURE_NAME) = ?");

    $sth->execute(uc($self->{'_query'}));
    
    $self->{'_featureNo'} = $sth->fetchrow;

    $sth->finish;
        
    return (1) if $self->{'_featureNo'}; # note we return here if we found something

    return (0);

}

#############################################################################
sub isLocusName{
#############################################################################
# this method determines if the passed in query is a locus_name
# however need to remember that a locus_name may correspond to multiple
# features - need to find how many

    my ($self) = @_;

    my $count;

    my $sth = $dbh->prepare("SELECT COUNT (*), l.LOCUS_NO
                                    FROM CGM_DDB.feature f, CGM_DDB.locus l
                                    WHERE UPPER(LOCUS_NAME) = ?
                                    AND l.LOCUS_NO = f.LOCUS_NO (+)
                                    GROUP BY l.LOCUS_NO");

    $sth->execute(uc($self->{'_query'}));
    
    ($count, $self->{'_locusNo'}) = $sth->fetchrow_array;
    
    $sth->finish;
    
    return ($count);

}

#############################################################################
sub isAliasName{
#############################################################################
# This method determines whethher the passed in query is an alias_name

    my ($self) = @_;

    my $sth = $dbh->prepare("SELECT ALIAS_NO
                                    FROM CGM_DDB.ALIAS a
                                    WHERE UPPER(ALIAS_NAME) = ?");

    $sth->execute(uc($self->{'_query'}));
    
    $self->{'_aliasNo'} = $sth->fetchrow;
    
    $sth->finish;

    return (1) if $self->{'_aliasNo'}; # note we return here if we found nothing
    
    return (0);

}

#############################################################################
sub _populateBydictyBaseID{
#############################################################################
# This method fills out all the attributes of an object, given that the
# query was an dictyBaseID.  Simply get either the associated locus_no or feature_no
# then use those to populate the object by a further private method call

    my ($self) = shift;

    my ($sth, $join);

    my $table = $self->{'_dictyBaseidTable'};
    my $dictyBaseid = uc($self->{'_query'});

    my $dictyBaseidObj = dictyBaseid->new(dbh=>$dbh,
	                      dictyBaseid=>$dictyBaseid);
    if (!$dictyBaseidObj) { return; }
    
    $self->{'_featureNo'} = $dictyBaseidObj->feature_no;
    $self->{'_locusNo'} = $dictyBaseidObj->locus_no;

    if ($self->{'_featureNo'}){ # use the feature no to get info	
	$self->_populateByFeature;
	
    }elsif ($self->{'_locusNo'}){
	
	$self->_populateByLocus;
	
    }

}

#############################################################################
sub _populateByFeature{
#############################################################################
# This subroutine fleshes out the object.  It absolutely requires that the
# $self->{'_featureNo'} has been set

    my ($self) = shift;

    if (!$self->{'_featureNo'}){

	die '_populateByFeature called without having previously set $self->{\'_featureNo\'}\n';

    }

    $self->_populateByNumber("f.FEATURE_NO", "f.LOCUS_NO = l.LOCUS_NO", $self->{'_featureNo'});

}

#############################################################################
sub _populateByLocus{
#############################################################################
# This subroutine fleshes out the object.  It absolutely requires that the
# $self->{'_locusNo'} has been set

    my ($self) = shift;

    if (!$self->{'_locusNo'}){

	die '_populateByLocus called without having previously set $self->{\'_featureLocus\'}\n';

    }

    $self->_populateByNumber("l.LOCUS_NO", "l.LOCUS_NO = f.LOCUS_NO", $self->{'_locusNo'});

}

################################################################################
sub _populateByNumber{
################################################################################
# This is a generic populator, that will populate an object using either a locus_no
# or a feature_no, depending on what was passed in

    my ($self, $column, $string, $number) = @_; 

    my ($fdictyBaseid, $ldictyBaseid);
    
    my $sth = $dbh->prepare("
	SELECT FEATURE_NO, FEATURE_NAME, 
	       f.CHROMOSOME, START_COORD,
               STOP_COORD, STRAND, IS_ON_PMAP, s.dictyBaseID, BRIEF_ID,
               l.LOCUS_NO, LOCUS_NAME, l.CHROMOSOME,
               GENETIC_POSITION, ENZYME, DESCRIPTION, NAME_DESCRIPTION 
        FROM   CGM_DDB.FEATURE f, CGM_DDB.LOCUS l, CGM_DDB.dictyBaseID s
        WHERE  $column = ?
        AND    $string(+)
        AND    (    (s.primary_key = f.feature_no 
		     and s.tab_name = 'FEATURE'
                     and s.dictyBaseid_type = 'Primary')
                 or (s.primary_key = l.locus_no
                     and s.tab_name = 'LOCUS'
                     and s.dictyBaseid_type = 'Primary'))
   ");
    
   $sth->execute($number);
    
   ($self->{'_featureNo'}, $self->{'_featureName'}, $self->{'chr'}, $self->{'begCoord'},
   $self->{'endCoord'}, $self->{'_strand'}, $self->{'_isOnPMap'}, $self->{'_primarydictyBaseID'} , $self->{'_briefId'},
   $self->{'_locusNo'}, $self->{'_locusName'}, $self->{'_genChr'}, $self->{'_genPos'},
   $self->{'_enzyme'}, $self->{'_description'}, $self->{'_nameDescription'}) = $sth->fetchrow_array;
    
   $sth->finish; 



   
}

#############################################################################
sub _populateByAlias{
#############################################################################
# This subroutine fleshes out the object.  It absolutely requires that the
# self->{'_aliasNo'} parameter has been set

# also have to be mindful that an alias could be used for multiple loci

    my ($self) = shift;

    if (!$self->{'_aliasNo'}){

	die '_populateByAlias called without having previously set $self->{\'_aliasNo\'}\n';

    }

    my $sth = $dbh->prepare("SELECT FEATURE_NO
                                    FROM CGM_DDB.ALIAS
                                    WHERE ALIAS_NO = ?");

    my ($locusNo, $count);

    $sth->execute($self->{'_aliasNo'});

    $self->{'_featureNo'} = $sth->fetchrow;

    $sth->finish;

    if ($self->{'_featureNo'}){

	$self->_populateByFeature;

    }else{

	$sth = $dbh->prepare("SELECT LOCUS_NO
                                     FROM CGM_DDB.LOCUS_ALIAS
                                     WHERE ALIAS_NO = ?");

	$sth->execute($self->{'_aliasNo'});

	while ($locusNo = $sth->fetchrow){

	    $self->{'_locusNo'} = $locusNo;
	    $count++;

	}

	$sth->finish;

	if ($count > 1){ # the alias is used by more than one locus

	    $self->{'_isMultiple'} = 1;
	    return;

	}else{

	    $self->_populateByLocus;

	}

    }

}

#############################################################################
sub _initCommonURLs{
#############################################################################
# This methods adds a number of URLs to the object that will be required by
# both the locus page and the wild card search.  These have been extracted
# from the others to make the program more efficient, so that it only initialiazes
# other URLs when it needs them

    my ($self) = @_;

    $self->{'physGen'} = $wine.$self->{'_linker'}."/PGMAP/PGmap?dictyBaseid=".$self->{'_primarydictyBaseID'};

}

#############################################################################
sub _initORFMapURLs{
#############################################################################
# This subroutine initializes the URLs required for making an ORF map link
# It has been abstracted to allow it's reuse

    my ($self) = @_;

    my $ORFMapBase = $wine.$self->{'_linker'}."/ORFMAP/";

    $self->{'miniORFMap'} = $ORFMapBase."miniORFmap?dictyBaseid=".$self->{'_primarydictyBaseID'};
    $self->{'ORFMap'} = $ORFMapBase."ORFmap?dictyBaseid=".$self->{'_primarydictyBaseID'};
    
}

#############################################################################
sub _initURLs{
#############################################################################
# This subroutine adds a number of URLs to the object, that can be used as 
# links that are pertinent to the locus to which the object corresponds

# This is currently a hard-wired set of links.  This method will transition
# to using the urls that will be stored in the database in future, so that
# maintainability is significantly enhanced

    my ($self) = @_;

   
    my $ncbi = "http://www.ncbi.nlm.nih.gov/";

    $self->{'litGuide'} = $wine.$self->{'_linker'}."/reference/geneinfo.pl?locus=".$self->{'_standard name'};
    $self->{'pubMed'} = $ncbi."entrez/query.fcgi?cmd=search&db=PubMed&term=%28".$self->{'_standard name'}."%5bTEXT%3anoexp%5d%20AND%20Dictyostelium%20discoideum%5bALL%5d%29";

    #my $seqResourcesBase = $wine.$self->{'_linker'}."/getSeq?seq=".$self->{'_standard name'}."&flankl=0&flankr=0&";
    my $seqResourcesBase = $wine.$self->{'_linker'}."/getSeq?seq=".$self->{'_featureName'}."&flankl=0&flankr=0&";

    #my $seqResourcesBase1k = $wine.$self->{'_linker'}."/getSeq?seq=".$self->{'_standard name'}."&flankl=1000&flankr=1000&";    

    my $seqResourcesBase1k = $wine.$self->{'_linker'}."/getSeq?seq=".$self->{'_featureName'}."&flankl=1000&flankr=1000&";

    # for links that imply the object codes for a protein, I only want them
    # if the object really does code for a protein


    #check which sequences exist for given feature
    if ($self->{'_featureNo'}){

        my $genomicObj;
        my $codingObj;
        my $proteinObj;
        my $KObj;

        $genomicObj = Display_seq->new(dbh=>$dbh,
                                      feature_no=>$self->{'_featureNo'},
                                      display_seq_type=>'Genomic DNA');

        $codingObj = Display_seq->new(dbh=>$dbh,
                                      feature_no=>$self->{'_featureNo'},
                                      display_seq_type=>'DNA coding sequence');

        $proteinObj = Display_seq->new(dbh=>$dbh,
                                      feature_no=>$self->{'_featureNo'},
                                      display_seq_type=>'Protein');
        $KObj = Display_seq->new(dbh=>$dbh,
                                      feature_no=>$self->{'_featureNo'},
                                      display_seq_type=>'Genomic DNA +/- 1000bp');



        if ($genomicObj) {
           $self->{'intron'}   = $seqResourcesBase."map=a3map";      
        }

        if ($codingObj) {
           $self->{'noIntron'} = $seqResourcesBase."map=n3map";
        }

        if ($proteinObj) {
            $self->{'trans'}    = $seqResourcesBase."map=p3map";
        }

        if ($KObj) {
           $self->{'upDownStream'} = $seqResourcesBase1k."map=a3map"; 
        }

    }



    if ($self->isCoding){

	# sequence retrieval
       
	#$self->{'trans'}    = $seqResourcesBase."map=p3map";
	#$self->{'noIntron'} = $seqResourcesBase."map=n3map";

	# sequence comparison

	$self->{'blastp'}   = $Dictyostelium."blast.pl?name=".$self->orf."&suffix=prot";
	$self->{'fastap'}    = $Dictyostelium."dictyBase/nph-fastadictyBase?name=".$self->orf."&suffix=prot";

	# premade similarity reports

	$self->{'wormComp'} = $bread."dictyBase/worm/getblast2.pl?name=".$self->{'_standard name'}."&db=worm";
	$self->{'mamComp'}  = $bread."dictyBase/Sacch3D/getblast?name=".$self->orf."&db=mammal";
      	$self->{'candidaComp'} = $self->_getCandidaCompUrl;

	if ($self->_hasFungalAlign) {
	    $self->{'fungalAlign'} = $fungiRoot."nph-showAlign?locus=".$self->{'_featureName'};
	}

	if ($self->orf) {
	    $self->{'syntenyViewer'} = $fungiRoot."FungiMap?locus=".$self->orf;
	}

	# need links for protein and DNA similarity, but don't know if they yet
	# exist - do we have premade BLAST reports?
	
	#$self->{'protSim'} = 
	#$self->{'nucSim'}  =

        #link to DB of Interacting Proteins
	$self->{'dip'} = $self->_getDipUrl;

        #blastp at NCBI
        $self->{'bpNCBI'} = $self->_getBpNCBI;
    }

    # some links will only work if the object is a feature

    if ($self->{'_featureNo'}){

        #$self->{'intron'}   = $seqResourcesBase."map=a3map";
 
	#$self->{'upDownStream'} = $seqResourcesBase1k."map=a3map";
	$self->{'frags'}    = $seqResourcesBase."map=fmap";
	$self->{'trans6'}   = $seqResourcesBase."map=rmap";
	
	# sequence comparison

	$self->{'blastn'}   = $Dictyostelium."blast.pl?name=".$self->orf;
	$self->{'fastan'}    = $Dictyostelium."dictyBase/nph-fastadictyBase?name=".$self->orf;
	$self->{'restrMap'} = $wine.$self->{'_linker'}."/PATMATCH/RestrictionMapper?name=".$self->orf;
	$self->{'primer'}   = $Dictyostelium."dictyBase/web-primer?name=".$self->orf;

 	$self->{'seqResources'} = $wine.$self->{'_linker'}."/seqTools?back=1&seqname=".$self->orf;
   }
    
    $self->{'chrFeaturesMap'}   = $self->{'ORFMap'};
    $self->{'chrFeaturesTable'} = $wine.$self->{'_linker'}."/featureform?dictyBaseid=".$self->{'_primarydictyBaseID'};
    $self->{'physMap'}  = $wine.$self->{'_linker'}."/PHYSMAP/PHYSmap?dictyBaseid=".$self->{'_primarydictyBaseID'};
    $self->{'ratioMap'} = $wine.$self->{'_linker'}."/PGMAP/ratioMap?dictyBaseid=".$self->{'_primarydictyBaseID'};

    #link to single page format.
    $self->{'singlepageUrl'} = $wine.$self->{'_linker'}."/singlepageformat?dictyBaseid=".$self->{'_primarydictyBaseID'};
    
    # now set up various links to functional analyses
    # get query_url from database
#    my %url4dataset;
#    $self->_getExpressionConnectionUrl(\%url4dataset);
    
    $self->{'curagen'}   = "http://portal.curagen.com/extpc/com.curagen.portal.servlet.DictyosteliumSearchResults?keywordIn=".$self->{'_featureName'};
    $self->{'grid'}   = "http://biodata.mshri.on.ca/grid/servlet/SearchResults?keywords=".$self->{'_featureName'};
    $self->{'bind'}   = "http://bind.ca/index2.phtml?site=search&id=".$self->{'_featureName'};
    $self->{'scop'} = "http://supfam.org/SUPERFAMILY/cgi-bin/gene.cgi?genome=sc&seqid=" .$self->{'_primarydictyBaseID'};
    $self->{'triples'}   = "http://ygac.med.yale.edu/triples/execqry.asp?server=oracle&orf=".$self->{'_featureName'};
    $self->{'marcotte'}  = "http://cgi.doe-mbi.ucla.edu/cgi-bin/marcotte/neighbors.pl?orf_name=".$self->{'_featureName'};
    $self->{'ygmv'}  = "http://transcriptome.ens.fr/ymgv/incoming.php?query=".$self->{'_featureName'};
    $self->{'openBio'} = "http://www.openbiosystems.com/Query/stanford.php?yko=" . $self->{'_featureName'};

    $self->{'_expression'} = $wine.$self->{'_linker'}."/expression/expressionConnection.pl";
    

    $self->{'diauxic'} = $self->{'_expression'}."?dataset=diauxic&orf=".$self->{'_featureName'};
    $self->{'sporulation'} = $self->{'_expression'}."?dataset=sporulation&orf=".$self->{'_featureName'};
    $self->{'alpha_conc'} = $self->{'_expression'}."?dataset=alpha_conc&orf=".$self->{'_featureName'};
    $self->{'alpha_time'} = $self->{'_expression'}."?dataset=alpha_time&orf=".$self->{'_featureName'};
    $self->{'histone'} = $self->{'_expression'}."?dataset=histone&orf=".$self->{'_featureName'};

    $self->{'evolution'} = $self->{'_expression'}."?dataset=evolution&orf=".$self->{'_standard name'};
    $self->{'cellcycle'} = $self->{'_expression'}."?dataset=cellcycle&orf=".$self->{'_standard name'};
    $self->{'stressResponse'} = $self->{'_expression'}."?dataset=stressResponse&orf=".$self->{'_featureName'};
    $self->{'dnaDamage'} = $self->{'_expression'}."?dataset=DNAdamage&orf=".$self->{'_featureName'};
    $self->{'zinc'} = $self->{'_expression'}."?dataset=zinc&orf=".$self->{'_featureName'};
    $self->{'phosphate'} = $self->{'_expression'}."?dataset=phosphate&orf=".$self->{'_featureName'};

    # now set up other internal links
    
    if ($self->_paragraphExists){
#	$self->{'paragraph'} = $bread."dbrun/SacchDB?find+LongText+%22Gene+Summary+Paragraph+for+".$self->{'_standard name'}."%22";
	$self->{'paragraph'} = "#summaryParagraph";
    }

    $self->{'communityAnnotation'} = $wine.$self->{'_linker'}."/DisplayCommuAnno?dictyBaseid=".$self->{'_primarydictyBaseID'};

    if ($self->associatedResearchers){
	$self->{'researchers'} = $wine.$self->{'_linker'}."/colleague/colleagueSearch?dictyBaseid=".$self->{'_primarydictyBaseID'};
    }
    
    if ($self->_hasMappingData){
	$self->{'mappingData'} = $wine.$self->{'_linker'}."/geneticData/displayTwoPoint?dictyBaseid=".$self->{'_primarydictyBaseID'};
    }

    $self->{'history'} = $wine.$self->{'_linker'}."/locusHistory.pl?dictyBaseid=".$self->{'_primarydictyBaseID'};
    $self->{'globalGH'} = $wine.$self->{'_linker'}."/geneHunter?locus=".$self->{'_standard name'};
    $self->{'exprConn'} = $wine.$self->{'_linker'}."/expression/expressionConnection.pl?query=".$self->{'_standard name'}."&noSearch=1";   
    # program needs to change to accommodate this
    $self->{'FuncJunc'} = $wine.$self->{'_linker'}."/functionJunction?locus=".$self->{'_standard name'};
    $self->{'Sacch3D'}  = $bread."dictyBase/Sacch3D/get?class=gene&item=".$self->{'_standard name'};
    $self->{'Mapping Data'} = "";

}

#############################################################################
sub _getExpressionConnectionUrl {
#############################################################################
    my ($self, $url4datasetRef) = @_;
    my $arrayRef = Expression_connection->GetECDetail(dbh=>$dbh);
    foreach my $rowRef (@$arrayRef) {
	my ($ec_no, $name, $source, $title, $x_blocksize, $y_blocksize, 
            $xaxis_title, $website_url, $query_url, $date_created, 
            $created_by) = @$rowRef;
	$$url4datasetRef{$name} = $query_url;
    }
}

#############################################################################
sub _getCandidaCompUrl {
#############################################################################
    my ($self) = @_;
    my $sth = $dbh->prepare("
        SELECT EI.external_id, TU.template_url
        FROM   CGM_DDB.external_id EI, CGM_DDB.template_url TU, 
               CGM_DDB.ei_tu ET
        WHERE  EI.primary_key = ?
        AND    EI.tab_name = 'FEATURE'
        AND    EI.source = 'CandidaDB'
        AND    EI.external_id_no = ET.external_id_no
        AND    ET.template_url_no = TU.template_url_no
    ");
    $sth->execute($self->{'_featureNo'});
    my ($candidaId, $candidaUrl) = $sth->fetchrow;
    $candidaUrl =~ s/_SUBSTITUTE_THIS_/$candidaId/;
    return $candidaUrl;
}

#############################################################################
sub _getDipUrl {
#############################################################################
    my ($self) = @_;
    my $sth = $dbh->prepare("
        SELECT EI.external_id, TU.template_url
        FROM   CGM_DDB.external_id EI, CGM_DDB.template_url TU, 
               CGM_DDB.ei_tu ET
        WHERE  EI.primary_key = ?
        AND    EI.tab_name = 'FEATURE'
        AND    EI.source = 'DIP ID'
        AND    EI.external_id_no = ET.external_id_no
        AND    ET.template_url_no = TU.template_url_no
    ");
    $sth->execute($self->{'_featureNo'});
    my ($dipId, $dipUrl) = $sth->fetchrow;
    $dipUrl =~ s/_SUBSTITUTE_THIS_/$dipId/;
    $sth->finish;
    return $dipUrl;
}

#############################################################################
sub _getBpNCBI {
#############################################################################
    my ($self) = @_;
    my $sth = $dbh->prepare("
        SELECT EI.external_id, TU.template_url
        FROM   CGM_DDB.external_id EI, CGM_DDB.template_url TU, 
               CGM_DDB.ei_tu ET
        WHERE  EI.primary_key = ?
        AND    EI.tab_name = 'FEATURE'
        AND    EI.source = 'RefSeq Version'
        AND    EI.external_id_no = ET.external_id_no
        AND    ET.template_url_no = TU.template_url_no 
        AND    TU.source = 'BLASTP at NCBI'
    ");
    $sth->execute($self->{'_featureNo'});
    my ($bpId, $bpUrl) = $sth->fetchrow;
    $bpUrl =~ s/_SUBSTITUTE_THIS_/$bpId/;
    $sth->finish;
    return $bpUrl;
}

#############################################################################
sub _hasFungalAlign {
#############################################################################
    my ($self) = @_;

    my $sth = $dbh->prepare("
        SELECT count(F1.feature_name)
        FROM   gus.feature F1, gus.feature F2, 
               gus.alignment A
        WHERE  upper(F2.feature_name) = ?
        AND    F2.feature_no = A.target_no
        AND    A.query_no = F1.feature_no
        AND    A.alignment_type != 'unresolved'
    ");
    $sth->execute(uc($self->{'_featureName'}));

    my $count = $sth->fetchrow;
    $sth->finish;

    return $count;
    
}

#############################################################################
sub _paragraphExists{
#############################################################################
# This subroutine returns a boolean to indicate whether a paragraph exists
# for the query

    my ($self) = shift;

    my $paragraphNo;

    # my $sth = $dbh->prepare("SELECT ACELINK_NO
    #                               FROM CGM_DDB.ACELINK
    #                               WHERE TAB_NAME = ?
    #                               AND ACE_CLASS = 'Gene Summary'
    #                               AND PRIMARY_KEY = ?");
    my $sth = $dbh->prepare("SELECT PARAGRAPH_NO
                                    FROM CGM_DDB.LOCUS
                                    WHERE LOCUS_NO = ?
                                   ");

    if ($self->{'_locusNo'}){

	$sth->execute($self->{'_locusNo'});
	$paragraphNo = $sth->fetchrow;
	$sth->finish;

    }

    return ($paragraphNo);

}

#############################################################################
sub associatedResearchers{
#############################################################################
# This subroutine returns a boolean to indicate whether the current object
# has any associated researchers
    
    my ($self) = @_;

    my $colleagueNo;

    if ($self->{'_locusNo'}){

	my $sth = $dbh->prepare("SELECT COLLEAGUE_NO
                                        FROM CGM_DDB.COLL_LOCUS
                                        WHERE LOCUS_NO = ?");

	$sth->execute($self->{'_locusNo'});

	$colleagueNo = $sth->fetchrow;

	$sth->finish;

    }

    return ($colleagueNo);

}

#############################################################################
sub _hasMappingData{
#############################################################################
# This subroutine returns a boolean to indicate whether the current object has any
# associated mapping data

    my ($self) = @_;

    my $twoPoint;

    if ($self->{'_locusNo'}){

	my $sth = $dbh->prepare("SELECT TWO_POINT_NO
                                        FROM CGM_DDB.LOCUS_TWOPT
                                        WHERE LOCUS_NO = ?");

	$sth->execute($self->{'_locusNo'});

	$twoPoint = $sth->fetchrow;

	$sth->finish;

    }

    return ($twoPoint);

}

#######################################################################################################
sub positionalInfo{
#######################################################################################################
# This method returns a string that can be used in the locus page to indicate where the locus in
# question is

    my ($self) = @_;

    my ($string, $string2);

    my $featureType = $self->featureType;

    if ($self->{'chr'}){
	
	$string .= "Chr".$arab2Rom{$self->{'chr'}}.": ";

    }elsif ($self->{'_genChr'}){

	$string .= "Chr".$arab2Rom{$self->{'_genChr'}};
   	
    }

    $string =~ s/ChrQ:/Mito:/;

    if ($self->{'begCoord'} && $self->{'endCoord'}){

	$string .= "coordinates ".$self->{'begCoord'}." to ".$self->{'endCoord'};
	if ($featureType !~ /Deleted/i){ # if it's not deleted, make it a link
	    $string .= " [ ".a({-href=>$self->orfMap},
			"ORF Map")." ]"; # only link it if we have coordinates
	}

    }

    #### exon and intron info
    #Mira Aug 12 02 don't display subfeature info when subfeature is deleted
    my $sth = $dbh->prepare("
        SELECT SFT.subfeature_type, SF.start_coord, SF.stop_coord
        FROM   CGM_DDB.subfeature SF, CGM_DDB.subfeature_type SFT
        WHERE  SF.feature_no = ?
        AND    SF.subfeature_no = SFT.subfeature_no
        AND    SF.subfeature_no NOT IN 
                  (SELECT subfeature_no FROM CGM_DDB.subfeature_type 
                     WHERE subfeature_type = 'Deleted') 
        ORDER BY SF.start_coord
    ");
    $sth->execute($self->featureNo);
    my $subFeatArrayRef = $sth->fetchall_arrayref();
    $sth->finish;
    
    my $row;
    foreach my $rowRef (@$subFeatArrayRef) {
	   my ($subtype, $beg, $end) = @$rowRef;
	   my ($subfeatBeg, $subfeatEnd);
	   if ($self->strand =~ /^C/i) {
	       $subfeatBeg = $self->{'begCoord'} - $beg + 1;
	       $subfeatEnd = $self->{'begCoord'} - $end + 1;
	   }
	   else {
	       $subfeatBeg = $self->{'begCoord'} + $beg - 1;
	       $subfeatEnd = $self->{'begCoord'} + $end - 1;
	   }
	   $row .= Tr({-align=>'right'}, 
			  th("$subtype").
			  td($beg." - ".$end).
			  td($subfeatBeg." - ".$subfeatEnd));
	   
    }
    if (@$subFeatArrayRef) {
	$string .= br.table(Tr(td(br).
			       th("Coordinates").
			       th("Chromosomal Coord")).
			    $row);
    }
    
    if ($self->strand =~ /^C/i && $featureType !~ /^(ARS|CEN)/i) {
	$string .= i(b("Note:")." this gene is encoded on the Crick strand.");
    }
   
    if ($self->{'_genPos'}){
	
	if ($self->{'begCoord'}){
	    $string2 = "Genetic position: ".a({-href=>$self->{'physGen'}},
					      $self->{'_genPos'});
	}else{
	    $string2 = "Genetic position: ".$self->{'_genPos'};
	}
	if ($string) { $string2 = p.$string2 };

    }

    return $string.$string2;

}

#######################################################################################################
sub positionalInfoSinglePage {
#######################################################################################################
# This method returns a string that can be used in the locus page to indicate where the locus in
# question is

    my ($self) = @_;

    my ($string, $string2);

    my $featureType = $self->featureType;

    if ($self->{'chr'}){
	
	$string .= "Chr".$arab2Rom{$self->{'chr'}}.": ";

    }elsif ($self->{'_genChr'}){

	$string .= "Chr".$arab2Rom{$self->{'_genChr'}};
   	
    }

    $string =~ s/ChrQ:/Mito:/;

    if ($self->{'begCoord'} && $self->{'endCoord'}){

	$string .= $self->{'begCoord'}." to ".$self->{'endCoord'} . br;
#	if ($featureType !~ /Deleted/i){ # if it's not deleted, make it a link
#	    $string .= " [ ".a({-href=>$self->orfMap},
#			"ORF Map")." ]"; # only link it if we have coordinates
#	}

    }

    ## exon and intron info
    #Mira Aug 12 02 don't display subfeature info when subfeature is deleted
    my $sth = $dbh->prepare("
        SELECT SFT.subfeature_type, SF.start_coord, SF.stop_coord
        FROM   CGM_DDB.subfeature SF, CGM_DDB.subfeature_type SFT
        WHERE  SF.feature_no = ?
        AND    SF.subfeature_no = SFT.subfeature_no
        AND    SF.subfeature_no NOT IN 
                  (SELECT subfeature_no FROM CGM_DDB.subfeature_type 
                     WHERE subfeature_type = 'Deleted') 
        ORDER BY SF.start_coord
    ");
    $sth->execute($self->featureNo);
    my $subFeatArrayRef = $sth->fetchall_arrayref();
    $sth->finish;

    my $row;
    foreach my $rowRef (@$subFeatArrayRef) {
       my ($subtype, $beg, $end) = @$rowRef;
       my ($subfeatBeg, $subfeatEnd); 
       if ($self->strand =~ /^W/i) {
           $subfeatBeg = $self->{'begCoord'} + $beg - 1;
           $subfeatEnd = $self->{'begCoord'} + $end - 1;
       }
       else {
           $subfeatBeg = $self->{'begCoord'} - $beg + 1;
           $subfeatEnd = $self->{'begCoord'} - $end + 1;
       }
       $string .= "$subtype: " .$beg." - ".$end.br;
    }

    if ($self->{'_genPos'}){
	
	if ($self->{'begCoord'}){
	    $string2 = "Genetic position: ".a({-href=>$self->{'physGen'}},
					      $self->{'_genPos'});
	}else{
	    $string2 = "Genetic position: ".$self->{'_genPos'};
	}
    }

    return $string.$string2;

}

#####################################################################
sub _initFeatureType{
#####################################################################
# This subroutine retrieves the feature types for a feature,
# and stores them as a comma delimited string

    my $self = shift;

    my $sth = $dbh->prepare("SELECT FEATURE_TYPE
                             FROM CGM_DDB.FEATURE_TYPE
                             WHERE FEATURE_NO = ?");

    $sth->execute($self->{'_featureNo'});

    my ($type, @types);

    my $isORF;
    
    while ($type = $sth->fetchrow){

	if ($type eq "ORF") {
	    $isORF = 1;
	}
	else {
	    push (@types, $type);
	}

    }

    if ($isORF) { unshift (@types, "ORF"); }

    $sth->finish;

    $self->{'_featureType'} = join (", ", @types);

}

#####################################################################
sub GetProcessFunction{
#####################################################################
    my ($self) = @_;
    my $goInfoArrayRef;
    if ($self->{'_locusName'}) {
	my $locusObj = Locus->new(dbh=>$dbh,
				  locus_name=>$self->{'_locusName'});
	if ($locusObj) {
	    $goInfoArrayRef = $locusObj->goInfoArrayRef;
	}
    }
    elsif ($self->{'_featureName'}) {
	my $featObj = Feature->new(dbh=>$dbh,
				   feature_name=>$self->{'_featureName'});
	if ($featObj) {
	    $goInfoArrayRef = $featObj->goInfoArrayRef;
	}
    }

    if (!$goInfoArrayRef) {
	return;
    }
    my %count4goAspect;
    my %goTerm4goAspect;
    my %annotNum4goAspect;
    foreach my $rowRef (@$goInfoArrayRef) {
	my ($goid, $goTerm, $goAspect, $goDef) = @$rowRef;
	$count4goAspect{$goAspect}++;
	my $goObj = Go->new(dbh=>$dbh,
			    goid=>$goid);
	if (!$goObj) { next; }
	my $locusArrayRef = $goObj->getLocusArrayRef;
	my $featArrayRef = $goObj->getFeatureArrayRef;
	my $annotCount = @$locusArrayRef + @$featArrayRef;
	if ($annotCount > $annotNum4goAspect{$goAspect}) {
	    $annotNum4goAspect{$goAspect} = $annotCount;
	    $goTerm4goAspect{$goAspect} = $goTerm;
	}
    }
    $self->{'process'} = $goTerm4goAspect{'P'} || 'not yet annotated';
    if ($count4goAspect{'P'} > 1) {
	$self->{'process'} .= "*";
    }
    $self->{'function'} = $goTerm4goAspect{'F'} || 'not yet annotated';
    if ($count4goAspect{'F'} > 1) {
	$self->{'function'} .= "*";
    }
    $self->{'component'} = $goTerm4goAspect{'C'} || 'not yet annotated';
    if ($count4goAspect{'C'} > 1) {
	$self->{'component'} .= "*";
    }
}

#####################################################################
#
#  Now we want a series of get methods
#
#####################################################################
###############################################################################
sub externalLinks{
###############################################################################
# This works out the urls for a links, and returns a single formatted string
# eg YPD, EC, Entrez Protein_ID, MIPS, PIR, SwissProt, YPD

    my ($self) = @_;

    my ($id, $url, $source, @links);

    my $featureType = $self->featureType;

    return if $featureType=~/Deleted/i; # no external links if it's been deleted

    # first get the template url

    my $sth = $dbh->prepare("select external_id, template_url, t.source
                                    from CGM_DDB.external_id e, CGM_DDB.ei_tu et, CGM_DDB.template_url t
                                    where tab_name = ?
                                    and primary_key = ?
                                    and e.external_id_no = et.external_id_no
                                    and et.template_url_no = t.template_url_no");

    if ($self->{'_featureNo'}){

	$sth->execute("FEATURE", $self->{'_featureNo'});

	while (($id, $url, $source)  = $sth->fetchrow_array){
# uncomment the following line to inhibit MIPS link on Locus page.  JMC 5/01
#	unless ($source eq "MIPS") {
	    unless ($source =~ /^(NCBI|CandidaDB|DB of Interacting Proteins|BLASTP|MetaCyc)/i) {

		$url =~ s/_SUBSTITUTE_THIS_/$id/;

		push (@links, a({-href=>$url},
				$source));

	    } 
	}	

	$sth->finish;

    }

    if ($self->{'_locusNo'}){

	$sth->execute("LOCUS", $self->{'_locusNo'});

	while (($id, $url, $source)  = $sth->fetchrow_array){

	    $url =~ s/_SUBSTITUTE_THIS_/$id/;

	    push (@links, a({-href=>$url},
			    $source));

	}

	$sth->finish;

    }
    
    return join (" | ", @links);

}

#####################################################################
sub pathwayRow {
#####################################################################
# This method will return rows of pathway links for displaying on locus

    my ($self) = @_;

    my $sth = $dbh->prepare("
                  select e.external_id_name, e.external_id, t.template_url
                  from CGM_DDB.external_id e, CGM_DDB.ei_tu et, CGM_DDB.template_url t
                  where e.tab_name = 'FEATURE'
                    and e.primary_key = ?
                    and e.source = 'MetaCyc'
                    and e.external_id_no = et.external_id_no
                    and et.template_url_no = t.template_url_no
    ");
    
    $sth->execute($self->{'_featureNo'});

    my @row;
    while ( my($name, $exId, $url) = $sth->fetchrow ) {
	$url =~ s/_SUBSTITUTE_THIS_/$exId/;
        push (@row, a({-href=>$url}, $name));
    }

    return \@row;
}


#####################################################################
sub phenotype{
#####################################################################
# This method will return the phenotype (in the locus_pheno, 
# feat_pheno,and phenotype tables). It will check to see if it has 
# already tried to get the phenotype, and if so return the previous 
# result, otherwise it will look in the tables,
# then set a variable to say it has looked

    my ($self) = @_;

    return $self->{'_phenotype'} if ($self->{'_gotPhenotype'});

    my ($sth);

    if ($self->{'_locusNo'}){

	$sth = $dbh->prepare("
		SELECT L.phenotype_type, P.phenotype, P.phenotype_no
                FROM   CGM_DDB.locus_pheno L, CGM_DDB.phenotype P
                WHERE  L.locus_no = ?
                AND    L.phenotype_no = P.phenotype_no
                ORDER BY L.phenotype_type desc
	");
	$sth->execute($self->{'_locusNo'});
    }
    elsif ($self->{'_featureNo'}){

	$sth = $dbh->prepare("
		SELECT F.phenotype_type, P.phenotype, P.phenotype_no
                FROM   CGM_DDB.feat_pheno F, CGM_DDB.phenotype P
                WHERE  F.feature_no = ?
                AND    F.phenotype_no = P.phenotype_no
                ORDER BY F.phenotype_type desc
        ");
	$sth->execute($self->{'_featureNo'});
    }
    $self->{'_phenotype'} = $sth->fetchall_arrayref();
    $sth->finish;
    $self->{'_gotPhenotype'} = 1;
    return $self->{'_phenotype'};
}



##########################################################################
sub molFunction{
##########################################################################
# This method returns an array reference, which points to an array which
# contains all the molecular functions to which a gene has been annotated

    my ($self) = @_;

    return ($self->{'_molFunction'}, $self->{'_molFunctionHash'},
	    $self->{'_molFunctionRefHash'}) if ($self->{'_gotMolFunction'});

    ($self->{'_molFunction'}, $self->{'_molFunctionHash'}, 
     $self->{'_molFunctionRefHash'}) = $self->_goTerm('F');

    $self->{'_gotMolFunction'} = 1;

    return ($self->{'_molFunction'}, $self->{'_molFunctionHash'},
	    $self->{'_molFunctionRefHash'});

}

###########################################################################
sub bioProcess{
###########################################################################
# This method returns array reference, which points to an array which
# contains all the biological processes to which a gene has been annotated

    my ($self) = @_;

    return ($self->{'_bioProcess'}, $self->{'_bioProcessHash'}, 
	    $self->{'_bioProcessRefHash'}) if ($self->{'_gotBioProcess'});

    ($self->{'_bioProcess'}, $self->{'_bioProcessHash'},
     $self->{'_bioProcessRefHash'}) = $self->_goTerm('P');

    $self->{'_gotBioProcess'} = 1;

    return ($self->{'_bioProcess'}, $self->{'_bioProcessHash'},
	    $self->{'_bioProcessRefHash'});

}

############################################################################
sub cellComponent{
############################################################################
# This method returns array reference, which points to an array which
# contains all the cellular components to which a gene has been annotated

    my ($self) = @_;

    return ($self->{'_cellComponent'}, $self->{'_cellComponentHash'},
	    $self->{'_cellComponentRefHash'}) if ($self->{'_gotCellComponent'});

    ($self->{'_cellComponent'}, $self->{'_cellComponentHash'},
     $self->{'_cellComponentRefHash'})  = $self->_goTerm('C');

    $self->{'_gotCellComponent'} = 1;

    return ($self->{'_cellComponent'}, $self->{'_cellComponentHash'},
	    $self->{'_cellComponentRefHash'});

}

######################################################################################
sub _goTerm{
######################################################################################
# This method is used as a general way of retrieving goterms, and returns an array
# reference that points to an array thhat contains the terms for a particular go aspect
# for the invoking dictyBase object.

# In addition a hash reference is returned, that hashes the term to it's goid

    my ($self, $aspect) = @_;

    my ($sth, @terms, $table, $word, $val, %goidHash, %refNoHash);

    if ($self->{'_locusNo'}){

	$word = $table = "LOCUS";
	$val = $self->{'_locusNo'};

    }elsif ($self->{'_featureNo'}){

	$word = "FEATURE";
	$table = "FEAT";
	$val = $self->{'_featureNo'};

    }

    if ($val){

	# need distinct, as the same association may have been done with multiple
	# pieces of evidence
	
	$sth = $dbh->prepare("
            SELECT DISTINCT GO_TERM, G.GOID, GL.IS_NOT, GL.GO_EVIDENCE_NO
            FROM CGM_DDB.GO_${table}_GOEV GL, CGM_DDB.GO G 
            WHERE ${word}_NO = ?
            AND G.GOID = GL.GOID
            AND GO_ASPECT = ?
        ");
		
	$sth->execute($val, $aspect);
	
	my %term;
	while (my ($term, $goid, $isNot, $goevNo) = $sth->fetchrow_array){
	    if (!$term{$term}) { 
		push (@terms, $term);
		$term{$term}++;
	    }
	    $goidHash{$term} = $goid;
	    if ($isNot =~ /^Y/i) {
		my $tabNm = "GO_".$table."_GOEV";
		my $priKey = $goid."::".$val."::".$goevNo."::".$isNot;
		my $priKeyCol = "GOID::".$word."_NO::GO_EVIDENCE_NO::IS_NOT";
		$refNoHash{$term} = 
		   Reflink->GetRefNoListBYtabNmPrikeyPrikeycol(dbh=>$dbh,
						tab_name=>$tabNm,
						primary_key=>$priKey,
		                                primary_key_col=>$priKeyCol); 
	    }
	}	
	$sth->finish;
    }
    return (\@terms, \%goidHash, \%refNoHash);

}

#####################################################################################
sub description{
#####################################################################################
# This method returns a description for the object.  If it is a locus, then the 
# description will be from the locus table, otherwise, it will be the brief_id from 
# the feature table

    my ($self) = @_;

    return $self->{'_description'} if $self->{'_description'};

    return $self->{'_briefId'};

}

#####################################################################################
sub nameDescription{
#####################################################################################
# This method returns a name_description for the object.  If it is a locus, then the 
# name description will be from the locus table; it is not applicable for features

    my ($self) = @_;

    return $self->{'_nameDescription'} if $self->{'_nameDescription'};

}

########################################################################################
sub product{
########################################################################################
# This method returns an array reference that points to an array of products that
# correspond to a locus

    my ($self) = @_;

    return $self->{'_product'} if ($self->{'_gotProduct'});

    my ($product, @products);

    if ($self->{'_locusNo'}){

	my $sth = $dbh->prepare("SELECT GENE_PRODUCT
                                        FROM CGM_DDB.LOCUS_GP L, CGM_DDB.GENE_PRODUCT G
                                        WHERE LOCUS_NO = ?
                                        AND L.GENE_PRODUCT_NO = G.GENE_PRODUCT_NO");

	$sth->execute($self->{'_locusNo'});

	while ($product = $sth->fetchrow){

	    push (@products, $product);

	}

	$sth->finish;

    }

    $self->{'_gotProduct'} = 1;

    $self->{'_product'} = \@products;

    return ($self->{'_product'});

}

#######################################################################################
sub deletedNote{
#######################################################################################
# This method returns a note, and whether it is public, for a deleted dictyBasei

    my ($self) = @_;

    my $sth = $dbh->prepare("
         SELECT C.note, C.is_public
         FROM   CGM_DDB.curator_note C, 
                CGM_DDB.dictyBaseid S
         WHERE  S.dictyBaseid = ?
         AND    S.curator_note_no = C.curator_note_no
   ");

    $sth->execute($self->{'_dictyBaseid'});

    my ($note, $public) = $sth->fetchrow_array;

    $sth->finish;

    return ($note, $public);

}

########################################################################################
sub isCoding{
########################################################################################
# This subroutine returns a boolean to indicate whether the object encodes a protein or 
# not - this is based on whether there is a feature_type 'ORF' associated

    my ($self) = @_;

    if (!exists($self->{'_isCoding'})){

	if ($self->{'_featureNo'}){

	    my $sth = $dbh->prepare("SELECT FEATURE_NO
                                            FROM CGM_DDB.FEATURE_TYPE
                                            WHERE FEATURE_NO = ?
                                            AND FEATURE_TYPE LIKE '%ORF'");

	    $sth->execute($self->{'_featureNo'});

	    my $coding = $sth->fetchrow;

	    $sth->finish;

	    if ($coding) {

		$self->{'_isCoding'} = 1;

	    }

	}else{

	   $self->{'_isCoding'} = 0; 
    
       }

    }

    return $self->{'_isCoding'};

}

##########################################################################################
sub genBankSeqs{
##########################################################################################
# This subroutine returns a url that can be used to retrieve genbank sequences for a 
# particular object.  If there are no Genbank sequences for an object, then undef is returned

    my ($self) = @_;

    my $primarydictyBaseID = $self->{'_primarydictyBaseID'};

    my $object;

    my $sth = $dbh->prepare("SELECT ACE_OBJECT
                             FROM CGM_DDB.ACELINK
                             WHERE ACE_CLASS = 'Sequence'
                             AND TAB_NAME = ?
                             AND PRIMARY_KEY = ?");

    if ($self->{'_locusNo'}){

	$sth->execute("LOCUS", $self->{'_locusNo'});

	$object = $sth->fetchrow;

	$sth->finish;

    }

    if (!$object && $self->{'_featureNo'}){

	$sth->execute("FEATURE", $self->{'_featureNo'});

	$object = $sth->fetchrow;

	$sth->finish;

    }

    if ($object) {
	
	return ($wine.$self->{'_linker'}."/getGenBank.pl?locus=".$primarydictyBaseID);
	
    }

    return;

}

##################################################################################################
sub protInfo{
##################################################################################################
# This method returns a URL which can be used to link to protein info

    my ($self) = @_;

    my $sth = $dbh->prepare("SELECT protein_info_no
                             FROM   CGM_DDB.protein_info
                             WHERE  feature_no = ?
    ");
    $sth->execute($self->{'_featureNo'});
    my $proteinInfoId = $sth->fetchrow;
    if ($proteinInfoId) {
	return ($wine.$self->{'_linker'}."/protein/protein?dictyBaseid=".$self->{'_primarydictyBaseID'});
    }
    return;
}

######################################################################################################
sub pdbInfo {
######################################################################################################
# This method returns a boolean to indicate whether the orf name has PDB homolog info associated with.
    my ($self) = shift;

    return if !$self->{'_featureNo'}; # if there's no feature number, then it can't have structural info

    my $seqObj = Pdb_sequence->new(dbh=>$dbh,
				   sequence_name=>$self->featureName);

    if ($seqObj) { 
	return ($wine.$self->{'_linker'}."/protein/get3d?dictyBaseid=".$self->{'_primarydictyBaseID'}); 
    }
    return;
}


######################################################################################################
sub motifInfo {
######################################################################################################
# This method returns a boolean to indicate whether the orf name has motif info associated with.
    my ($self) = shift;

    return if !$self->{'_featureNo'}; # if there's no feature number, then it can't have motif info

#    my $motifObj = Motif->new(seqname=>$self->featureName);

#    if($motifObj->hasMotif) {
	return ($wine.$self->{'_linker'}."/protein/getDomain?dictyBaseid=".$self->{'_primarydictyBaseID'});
#    }
}


######################################################################################################
sub isReserved{
######################################################################################################
# This method returns a boolean to indicate whether the gene name of an object is a resevered one

    my ($self) = shift;

    return (0) if !$self->{'_locusNo'}; # if there's no locus number, then it can't be reserved
    
    my $sth = $dbh->prepare("SELECT RESERVATION_NO
                             FROM CGM_DDB.GENE_RESERVATION
                             WHERE LOCUS_NO = ?
                             AND   IS_STANDARDIZED = 'N'");

    $sth->execute($self->{'_locusNo'});

    my $number = $sth->fetchrow;

    $sth->finish;

    if ($number){ return 1 }else{ return 0 };

}


########################################################################################################
sub fetchRelatedInteractors{
########################################################################################################
# This method returns the dictyBaseid, the interaction type and description of entities that have an interaction
# with the object.  These are returned in a single array

    my ($self) = shift;

    my ($dictyBaseid, $interaction, $desc, @data);

    my $sth = $dbh->prepare("SELECT dictyBaseID_2, INTERACTION_TYPE, DESCRIPTION
                                    FROM CGM_DDB.INTERACTION
                                    WHERE dictyBaseID_1 = ?
                                    AND INTERACTION_TYPE IN ('Alternative processing', 'Repeated', 'Nomenclature')");

    $sth->execute($self->{'_primarydictyBaseID'});

    while (($dictyBaseid, $interaction, $desc) = $sth->fetchrow_array){

	push (@data, $dictyBaseid, $interaction, $desc);

    }

    $sth->finish;

    return (@data);

}
                                    
########################################################################################################
sub primarydictyBaseID{
########################################################################################################
# this subroutine returns the primary dictyBaseID for an object.  This was retrieved during the instantiation 
# of the object 

    return $_[0]->{'_primarydictyBaseID'};
    
}

sub database       { $_[0]->{'_database'}     }

sub orf            { $_[0]->{'_featureName'}  }

sub featureName    { $_[0]->{'_featureName'}  }

sub gene           { $_[0]->{'_locusName'}    }

sub locusName     { $_[0]->{'_locusName'}    }

##########################################################################################################
sub alias{
##########################################################################################################
# This method returns a comma delimited string of all the aliases

    my ($self) = @_;

    if (!$self->{'_gotAliases'}){

	$self->_getAllAliases;
	
	$self->{'_gotAliases'} = 1;

    }

    return $self->{'_alias'};

}

    
###########################################################################################################
sub title{ 
###########################################################################################################
# This method returns a string that may be apropriate for use a at title

    my $self = shift;

    if (!$self->{'_title'}){

	if (($self->{'_featureName'} eq $self->{'_locusName'}) && $self->{'_featureName'}){
	    $self->{'_title'} = $self->{'_locusName'};
	}elsif ($self->{'_locusName'} && $self->{'_featureName'}){
	    $self->{'_title'} = $self->{'_locusName'}."/".$self->{'_featureName'};
	}elsif ($self->{'_featureName'}){
	    $self->{'_title'} = $self->{'_featureName'};
	}elsif ($self->{'_locusName'}){
	    $self->{'_title'} = $self->{'_locusName'};
	}else{
	    $self->{'_title'} = "Search for : ".$self->{'_query'};
	}

    }
    
    return $self->{'_title'}

}

###########################################################################
sub CommunityAnnotationTopics { 
###########################################################################
# This method returns arrayref of available topics of community annotation 
    my ($self) = @_;
    
    my $topicSth = $dbh->prepare ("
                       SELECT distinct topic 
                       FROM CGM_DDB.community_annotation 
                       WHERE dictyBaseid = ?
                   ");
    $topicSth->execute($self->{'_primarydictyBaseID'});

    my @topic;
    while (my $row = $topicSth->fetchrow) {
	push(@topic, $row);
    }

    if (@topic) {
	return \@topic;
    }
    else { return; }
}


#####################################################################
sub germOnline {
#####################################################################
# This method will return rows of GermOnline links for displaying on locus

    my ($self) = @_;

    my $sth = $dbh->prepare("
                  select e.external_id, t.template_url
                  from CGM_DDB.external_id e, CGM_DDB.ei_tu et, CGM_DDB.template_url t
                  where e.tab_name = 'FEATURE'
                    and e.primary_key = ?
                    and e.source = 'GermOnline'
                    and e.external_id_no = et.external_id_no
                    and et.template_url_no = t.template_url_no 
                    and t.source = 'GermOnline'
                        
    ");
    
    $sth->execute($self->{'_featureNo'});

    my($exId, $url) = $sth->fetchrow;
    $url =~ s/_SUBSTITUTE_THIS_/$exId/;

    return $url;
}

sub standardName   { $_[0]->{'_standard name'}}   

sub chr            { $_[0]->{'chr'}           }

sub begCoord       { $_[0]->{'begCoord'}      }

sub endCoord       { $_[0]->{'endCoord'}      }

sub orfMap         { $_[0]->{'ORFMap'}        }

sub miniORFMap     { $_[0]->{'miniORFMap'}    }

sub litGuide       { $_[0]->{'litGuide'}      }

sub pubMed         { $_[0]->{'pubMed'}        }

sub intronSeq      { $_[0]->{'intron'}        }

sub upDownStreamSeq{ $_[0]->{'upDownStream'}  }

sub noIntronSeq    { $_[0]->{'noIntron'}      }

sub translation    { $_[0]->{'trans'}         }

sub trans6frame    { $_[0]->{'trans6'}        }

sub restrict       { $_[0]->{'frags'}         }

sub process        { $_[0]->{'process'}       }

sub function       { $_[0]->{'function'}      }

sub component      { $_[0]->{'component'}     }

sub dictyBaseidType      { $_[0]->{'_dictyBaseidType'}    }

sub blastp         { $_[0]->{'blastp'}        }

sub blastn         { $_[0]->{'blastn'}        }

sub fastap         { $_[0]->{'fastap'}        }

sub fastan         { $_[0]->{'fastan'}        }

sub restrMap       { $_[0]->{'restrMap'}      }

sub primer         { $_[0]->{'primer'}        }


sub chrFeaturesMap { $_[0]->{'chrFeaturesMap'}}

sub chrFeaturesTable { $_[0]->{'chrFeaturesTable'} }

sub physGen        { $_[0]->{'physGen'}       }

sub physMap        { $_[0]->{'physMap'}       }

sub ratioMap       { $_[0]->{'ratioMap'}      }

sub wormComp       { $_[0]->{'wormComp'}      }

sub mamComp        { $_[0]->{'mamComp'}       }

sub candidaComp    { $_[0]->{'candidaComp'}   }

sub syntenyViewer    { $_[0]->{'syntenyViewer'}   }
 
sub fungalAlign    { $_[0]->{'fungalAlign'}   }

sub cellcycle      { $_[0]->{'cellcycle'}     }

sub curagen        { $_[0]->{'curagen'}       }

sub grid           { $_[0]->{'grid'}       }

sub bind           { $_[0]->{'bind'}       }

sub scop           { $_[0]->{'scop'} }

sub dip            { $_[0]->{'dip'} }

sub bpNCBI         { $_[0]->{'bpNCBI'} }

sub openBio        { $_[0]->{'openBio'} }

sub triples        { $_[0]->{'triples'}       }

sub marcotte       { $_[0]->{'marcotte'}      }

sub ygmv           { $_[0]->{'ygmv'}      }        

sub sporulation    { $_[0]->{'sporulation'}   }

sub diauxic        { $_[0]->{'diauxic'}       }

sub evolution      { $_[0]->{'evolution'}     }

sub alpha_conc     { $_[0]->{'alpha_conc'}    }

sub alpha_time     { $_[0]->{'alpha_time'}    }

sub histone        { $_[0]->{'histone'}       }

sub stressResponse { $_[0]->{'stressResponse'}}

sub dnaDamage 	   { $_[0]->{'dnaDamage'}     }

sub zinc           { $_[0]->{'zinc'}          }

sub phosphate      { $_[0]->{'phosphate'}     }

sub paragraph      { $_[0]->{'paragraph'}     }

sub communityAnnotation { $_[0]->{'communityAnnotation'}}

sub researchers    { $_[0]->{'researchers'}   }

sub history        { $_[0]->{'history'}       }

sub globalGH       { $_[0]->{'globalGH'}      }

sub exprConn       { $_[0]->{'exprConn'}      }

sub FuncJunc       { $_[0]->{'FuncJunc'}      }

sub mappingData    { $_[0]->{'mappingData'}   }

sub seqResources   { $_[0]->{'seqResources'}  }

sub singlepageUrl   { $_[0]->{'singlepageUrl'}  }

sub featureNo      { $_[0]->{'_featureNo'}    }

sub locusNo        { $_[0]->{'_locusNo'}      }

sub dbh            { $dbh                     }

######################################################################################
sub featureType{
######################################################################################
#

    my $self = shift;

    if (!$self->{'_gotFeatureType'}){
	
	$self->_initFeatureType if ($self->{'_featureNo'});
	$self->{'_gotFeatureType'} = 1;
	
    }

    return $self->{'_featureType'};

}

sub isMultiple     { $_[0]->{'_isMultiple'}   }


sub strand         { $_[0]->{'_strand'}       }

sub isOnPmap       { $_[0]->{'_isOnPMap'}     }

sub enzyme         { $_[0]->{'_enzyme'}       }

sub genPos         { $_[0]->{'_genPos'}       }

sub genChr         { $_[0]->{'_genChr'}       }

sub briefId        { $_[0]->{'_briefId'}      }

END{ # static finalizing method, to close database connection

    $dbh && $dbh->disconnect;

}

}


1; # to make require happy

=pod

=head1 Abstract

This perl library (dictyBaseObject) is used as a simple class for instantiating objects
that encapsulate information pertaining to entities in the Dictyostelium genome
database, namely features and loci.  Once an object has been instantiated (see 
below), several methods are available to retrieve many other attributes of the 
object.

=head1 Limitations

The object has not yet been optimized fully, so creating many objects
may have an associated overhead.  In addition it is probably prudent
to not have multiple objects created simultaneously, as there is a
memory overhead that grows quickly.  Further relegation of many things
that happen during object construction to their relevant accessors
will optimize both speed and size of the objects, especially if you
make relatively few method calls.  This is planned for the future.

If you intend to use the object to retrieve various urls pertinent to
the object, you currently need to call the private method _initURLs.
This is obviously bad, but will change as I deal with the above concerns.

=head1 Instantiating a New dictyBaseObject


To instantiate a new dictyBaseObject, you should use the syntax:

my $object = dictyBaseObject->new(database=>$database,
                            %args);

In this case %args is a hash, which will in all likelihood contain a
single entry, which will be the argument type, and the argument value.

The following argument types are valid for constructor:

=over 4

=item *

query

=item *

locusNo

=item *

dictyBaseid

=item *

locusName

=item *

featureNo

=item *

featureName

=item *

aliasNo

=item *

aliasName

=back

The named argument query will accept dictyBaseIDs, locus_names,
feature_names, and alias_names.  The other named arguments will expect
the argument value to be of the indicated type.  Using the argument
type 'query' is the least efficient way of calling the constructor.
Note that when instantiating an object with the arguments query,
aliasNo, or aliasName, it is possible (because an alias may map to
several loci/features, or a locus_name may also be an alias), that the
argument value does not unambiguously define a single entity in the
database.  I this case it is explicitly up to the client of the object
to check, using the isMultiple method, if many entities are described
by the argument value.  If the result from that method is true, then
no other methods should be called, as their results are unreliable.
Instead the client should use other information, which they have to
define, to use an alternative, unambiguous constructor.

Examples:

my $object=dictyBaseObject->new(database=>$database, # unambigous
			  dictyBaseid=>"S0003341");

my $object = dictyBaseObject->new(database=>$database, # ambigous
                            query=>'MPR1');

# so :

if ($object->isMultiple){

    # Do something about it

}else{

    # continue as normal

}

etc.

In terms of efficiency, the locusNo and featureNo constructor
arguments require the least work to fill out the object parameters,
the locusName, featureName, dictyBaseid, aliasNo, and aliasName arguments
are a little less efficient, whereas the query argument is the least
efficient of all.

Note that if you want to have many dictyBase objects used through the life
of a program, that you may not have some objects out of one database
(dictyBase) and some out of the other (sdev).  The object will only maintain
a single connection for efficiency purposes, which will be made using
the database passed in during construction of the first object.  If
you subsequently create a second object, with a different database,
the program will die, with the appropriate error message.

=head1 Accessor Methods

=head2 isFeatureName

Usage : 

if ($object->isFeatureName){
    
    # blah

}

returns : a boolean

This accessor simply returns a boolean to indicate whether the
original query used to construct the object was a feature name

=head2 isLocusName

Usage : 

if ($object->isLocusName){
    
    # blah

}

returns : a boolean

This accessor simply returns a boolean to indicate whether the
original query used to construct the object was a locus name

=head2 isAliasName

Usage : 

if ($object->isAliasName){
    
    # blah

}

returns : a boolean

This accessor simply returns a boolean to indicate whether the
original query used to construct the object was an alias name

=head2 associatedResearchers

Usage :

    if ($object->associatedResearchers){

	# blah

    }

returns a boolean to indicate whether an dictyBaseObject has any associated
researchers with it.

=head2 positionalInfo

Usage :

my $postionalInfo = $object->positionalInfo;

returns a formatted string that contains urls, and the link text for
all the positional information that is know for the object.  This is
not a good method in terms of seperating data and display, but as the
locus page currently relies on it, it will be here for the forseeable
future.  Seperate accessor methods for all the seperate information
are planned.

=head2 externalLinks

Usage:

my $links = $object->externalLinks;

This accessor returns a string that contains all the available
external links for an object, with correct links and link text.  Each
link is delimited by the pipe ('|') character.

=head2 molFunction

Usage :

my ($arrayRef, $hashRef) = $object->molFunction;

This method retruns an array reference, and a hash reference.  The
array reference points to an array that contains all the molecular
functions to which the object has been annotated.  The hash reference
points to a hash that hashes those functions to their goids.  The
goids are only an integer, eg GOID GO:0000082 would be represented as 82.

=head2 bioProcess

Usage :

my ($arrayRef, $hashRef) = $object->bioProcess;

This method retruns an array reference, and a hash reference.  The
array reference points to an array that contains all the biological
processes to which the object has been annotated.  The hash reference
points to a hash that hashes those process to their goids.  The
goids are only an integer, eg GOID GO:0000082 would be represented as 82.

=head2 cellComponent

Usage :

my ($arrayRef, $hashRef) = $object->cellComponent;

This method retruns an array reference, and a hash reference.  The
array reference points to an array that contains all the cellular
components to which the object has been annotated.  The hash reference
points to a hash that hashes those components to their goids.  The
goids are only an integer, eg GOID GO:0000082 would be represented as
82.

=head2 description

Usage:

my $description = $object->description;

This method returns a string that contains the description from the
locus table, or if the object is not a locus, the brief_id from the
feature table.  If their is nothing for these, the undef will be
returned.

=head2 product

Usage:

my $arrayRef = $object->product;

This method returns an array reference that points to an array
containing the names of all the products for a locus.

=head2 deletedNote

Usage:

my ($note, $isPublic) = $object->deletedNote;

This method returns a note for an object which has been constructed
using a deleted dictyBaseID (you can use the dictyBaseidType method to determine
this), and whether the not is public ('Y') or not ('N')

=head2 isCoding

Usage :

if ($object->isCoding){

   # blah

}

This method returns a boolean to indicate whether the calling object
is coding.  The criteria for coding are that it is either designated
as an ORF or a Ty ORF.

=head2 genBankSeqs

Usage :

my $genbankSeqs = $object->genBankSeqs;

This method (somewhat poorly designed) returns a url that will link to
a program that will display the GenBank sequences for the object.  If
there are no Genbank sequences, than it will return undef.  It
therefore can be used as if it returned a boolean, eg:

if ($object->genBankSeqs){

   # blah

}

=head2 protInfo


Usage :

my $protInfo = $object->protInfo;

This method (somewhat poorly designed) returns a url that will link to
the protein info for the object.  If
there is no protein info, than it will return undef.  It
therefore can be used as if it returned a boolean, eg:

if ($object->protInfo){

   # blah

}

=head2 isReserved

Usage:

if ($object->isReserved){

   # blah

}

This method simply returns a boolean to indicate whether a locus name
is a reserved locus name or not.

=head2 primarydictyBaseID

Usage:

my $primarydictyBaseID = $object->primarydictyBaseID;

This method returns the primary dictyBaseid for an object.  If the object is
a feature, then it will come form the feature table.  Otherwise it
will come from the locus table.  For the rare occurence where there is
no primary dictyBaseid (eg the locus MPR1) then it will return undef.

=head2 database

Usage :

$database = $object->database;

This is an pretty useless accessor, that simply returns the database
that was used to construct the object (sdev or dictyBase)

=head2 orf

Usage:

my $orf = $object->orf;

This method has been deprecated.  It will still continue to work, but
is an alias to the preferred method, featureName.

=head2 featureName

Usage:

my $orf = $object->featureName;

This method returns the featureName of the object, if one exists,
otherwise it returns undef.

=head2 gene

Usage:

my $gene = $object->gene;

This method has been deprecated.  It will still continue to work, but
is an alias for the preferred method, locusName.

=head2 locusName

Usage:

my $gene = $object->locusName;

This method returns the locusName for the object, if one exists,
otherwise it returns undef.

=head2 alias

Usage:

my $alias = $object->alias;

This method returns a comma delimited string of all the known aliases
for an object.

=head2 title

Usage:

my $title = $object->title;

This method returns a string, whose contents depends on the object
itself.  If the object has a locus name and a featurename the string
will be:

locusName/featureName

if there is just a locus name, it will be

locusName

if there is just a feature name it will be

featureName

if the query used to instantiate the object did not resolve, it will
be:

Search for : $query

=head2 standardName

Usage:

my $standardName = $object->standardName;

This method is probably badly named, in terms of expectations based on
Ace.  If an entity has a locusName, that will be returned, otherwise
the featureName will be returned.  The method may be better called
display name, but its not :-(

=head2 chr

Usage:

my $chr = $object->chr;

This method returns the chromosome that the feature of the object (if
there is one) is know to map.  Otherwise it returns undef.

=head2 begCoord

Usage:

my $begCoord = $object->begCoord;

This method returns the beginning coordinate of the objects feature,
if there is one, otherwise it returns undef.

=head2 endCoord

Usage:

my $endCoord = $object->endCoord;

This method returns the endning coordinate of the objects feature, if
there is one, otherwise it returns undef.

=head2 orfMap

Usage:

my $orfMap = $object->orfMap;

This method returns a url that can be used to link to the ORFMap
program.

=head2 miniORFMap

Usage:

my $miniORFMap = $object->miniORFMap;

This method returns a url that can be used to link to the miniORFMap
program.

=head2 litGuide

Usage:

my $litGuide = $object->litGuide;

This method is poorly name, and returns a url that may be used to link
to the gene info for the object.

=head2 pubMed

Usage:

my $pubMed = $object->pubMed;

This method returns a url that can be used to make a link to pub med
for the object.

=head2 intronSeq

Usage:

my $intronSeq = $object->intronSeq;

This method returns a url to retrieve the sequence (with introns) of
the objects feature (if there is one), otherwise returns undef.

=head2 noIntronSeq

Usage:

my $noIntronSeq = $object->noIntronSeq;

This method returns a url to retrieve the sequence (without introns)
of the objects feature (if there is one), otherwise returns undef.

=head2 translation

Usage:

my $translation = $object->translation;

This method returns a url to retrieve the translation of the sequence
of the objects feature, if has one that encodes a protein, otherwise
it returns undef.

=head2 trans6frame

Usage:

my $trans6frame = $object->trans6frame;

This method returns a url to retrieve the 6-frame translation of the
sequence of the objects feature, if has one, otherwise it returns
undef.

=head2 restrict

Usage:

my $restrict = $object->retrict;

This method returns a url that can be used to link to a restriction
map for the objects feature, if it has one, otherwise it returns
undef.

=head2 process

Usage:

my $process = $object->process;

This method returns the process for the objects feature, otherwise it returns undef.

=head2 function

Usage:

my $function = $object->function;

This method returns the function for the objects feature, otherwise it returns undef.

=head2 component

Usage:

my $component = $object->component;

This method returns the component for the objects feature, otherwise it returns undef.

=head2 dictyBaseidType

Usage:

my $type = $object->dictyBaseidType;

If the object was constructed using an dictyBaseID, and the dictyBaseID was in the
dictyBaseID_OTHER table, then this method will return the type of that
dictyBaseid, otherwise it will return undef.

=head2 blastp

Usage:

my $blastp = $object->blastp;

This method returns a url which can be used to link to blastp for the
object, if it has a feature which may code for a protein, otherwise it
returns undef.

=head2 blastn

Usage:

my $blastn = $object->blastn;

This method returns a url which can be used to link to blastn for the
object, if it has a feature, otherwise it returns undef.  

=head2 fastap

Usage:

my $fastap = $object->fastap;

This method returns a url which can be used to link to fastap for the
object, if it has a feature which may code for a protein, otherwise it
returns undef.

=head2 fastan

Usage:

my $fastan = $object->fastan;

This method returns a url which can be used to link to fastan for the
object, if it has a feature, otherwise it returns undef.  

=head2 restrMap

Usage:

my $restrMap = $object->restrMap;

This method returns a url that links to restriction mapper, if the
object has a feature, otherwise it returns undef.

=head2 primer

Usage:

my $primer = $object->primer;

This method returns a url that links to web primer, if the object has
an associated feature, otherwise it returns undef.

=head2 featureNo

Usage:

my $featureNo = $object->featureNo;

This method returns the featureNo associated with an object, if there
is one, otherwise it returns undef.

=head2 locusNo

Usage:

my $locusNo = $object->locusNo;

This method returns the locusNo associated with an object, if there
is one, otherwise it returns undef.

=head2 featureType

Usage:

my $featureType = $object->featureType;

This method returns a comma delimited string of any feature types
associated with the object.

=head2 isMultiple

Usage:

if ($object->isMultiple({

   # blah

}else{

   # whatever

}

This method returns a boolean to indicate whether the original query
resolved to more than one entity in the database.  If it returns 1
(true), then any other method calls on the object should be considered
unreliable.  In future I will try to make this more fool-proof, with
additional methods to resolve the query further.

=head2 strand

Usage:

my $strand = $object->strand;

This method reaturns the strand (C or W) from the feature table, if
one exists, otherwise returns undef.

=head2 isOnPmap

Usage:

if ($object->isOnPmap eq "Y"){

   # blah

}

This method returns either Y or N to indicate if an object that
corresponds to a feature is on the phhysical map.  If the object does
not have an associated feature, then it will return undef.

=head2 enzyme

Usage:

my $enzyme = $object->enzyme;

This method returns the enzyme that is associated with an objectt, as
found in the locus table.  If there is no associated enzyme (as of
8-25-2000 no loci have enzyme designations) then it will return undef.

=head2 genPos

Usage:

my $genPos = $object->genPos;

This method returns the genetic posiyion of a the locus associated
with an object, otherwise it returns undef.

=head2 genChr

Usage:

my $genChr = $object->genChr;

This method returns the chromosme to with the locus associated with an
object has been mapped genetically, otherwise it returns undef.

=head2 briefId

Usage:

my $briefId = $object->briefId;

This method returns the brief ID that is associated with the feature
of an object, if one exists, otherwise it returns undef.

=cut

