=head1 NAME

Bio::Graphics::Browser::Plugin -- Base class for gbrowse plugins.

=head1 SYNOPSIS

     package Bio::Graphics::Browser::Plugin::MyPlugin;
     use Bio::Graphics::Browser::Plugin;
     use CGI ':standard';
     @ISA = 'Bio::Graphics::Browser::Plugin';

     # called by gbrowse to return name of plugin for popup menu
     sub name        { 'Example Plugin' }

     # called by gbrowse to return description of plugin
     sub description { 'This is an example plugin' }

     # called by gbrowse to return type of plugin
     sub type        { 'annotator' }

     # called by gbrowse to configure default settings for plugin
     sub config_defaults {
         my $self = shift;
         return {foo => $value1,
                 bar => $value2}
     }

     # called by gbrowse to reconfigure plugin settings based on CGI parameters
     sub reconfigure {
       my $self = shift;
       my $current = $self->configuration;
       $current->{foo} = $self->config_param('foo');
       $current->{bar} = $self->config_param('bar');
     }

     # called by gbrowse to create a <form> fragment for changing settings
     sub configure_form {
       my $self    = shift;
       my $current = $self->configuration;
       my $form = textfield(-name  => $self->config_name('foo'),
                            -value => $current->{foo})
                  .
                  textfield(-name  => $self->config_name('bar'),
                            -value => $current->{bar});
       return $form;
     }

     # called by gbrowse to annotate the DNA, returning features
     sub annotate {
        my $self     = shift;
        my $segment  = shift;
        my $config   = $self->configuration;
        # do something with the sequence segment
        my @features = do_something();
        return \@features;
     }

=head1 DESCRIPTION

This is the base class for Generic Genome Browser plugins. Plugins are
perl .pm files that are stored in the gbrowse.conf/plugins directory.
Plugins are activated in the gbrowse.conf/ configuration file by
including them on the list indicated by the "plugins" setting:

      plugins = BatchDumper FastaDumper GFFDumper
                OligoFinder RestrictionAnnotator

Site-specific plugins may be placed in one or more site-specific
directories and added to the plugin search path using the plugin_path
setting:

      plugin_path = /usr/local/gbrowse_plugins

GBrowse currently recognizes five distinct types of plugins:

=over

=item 1. dumpers

These plugins receive the genomic segment object and generate a dump
-- the output can be text, html or some other specialized format.
Example: GAME dumper.

=item 2. finders

These plugins accept input from the user and return a list of
genomic regions. The main browser displays the found regions and
allows the user to select among them. Example: BLAST search.

=item 3. annotators

These plugins receive the genomic segment object and return a list
of features which are overlayed on top of the detailed view.
Example: restriction site annotator.

=item 4. highlighters

These plugins receive each feature in turn before it is rendered and
can return a color name in order to highlight the background of the
feature with that color.

=item 5. filters

These plugins take filtering options from the user and are converted into
a filter closure. Every feature from the track is passed to this closure.
All features for which a true value is returned by the filter closure show
up in the track.  
 

=back

All plug-ins inherit from Bio::Graphics::Browser::Plugin, which defines
reasonable (but uninteresting) defaults for each of the methods.
Specific behavior is then implemented by selectively overriding certain
methods.

The best way to understand how this works is to look at the source code
for some working plugins. Examples provided with the gbrowse
distribution include:

=over

=item GFFDumper.pm

A simple dumper which produces GFF format output representing the
features of the currently-selected segment.

=item FastaDumper.pm

A more complex dumper that illustrates how to create and manage
persistent user-modifiable settings.

=item SequenceDumper.pm

Another dumper that shows how plugins interact with the Bio::SeqIO system.

=item OligoFinder.pm

A finder that searches for short oligos in the entire database.
(Only works with Bio::DB::GFF databases.)

=item RestrictionAnnotator.pm

An annotator that finds restriction sites in the currently selected
region of the genome. It creates a new track for each type of
restriction site selected.

=item RandomGene.pm

An example annotator that generates random gene-like structures in
the currently displayed region of the genome. It's intended as a
template for front-ends to gene prediction programs.

=item FilterTest.pm

An example filter that filters the features from the 'ORFs' track (yeast example database).
The user can pass options to filter the ORFs based on strand or length.
Only those ORFs meeting the passed criteria are displayed.

=back

=head1 METHODS

The remainder of this document describes the methods available to the
programmer.

=head1 INITIALIZATION

The initialization methods establish the human-readable name,
description, and basic operating parameters of the plugin. They should
be overridden in each plugin you write.

=over

=item    $name = $self->name()

Return a short human-readable name for the plugin. This will be
displayed to the user in a menu using one of the following forms:

            Dump <name>
            Find <name>
            Annotate <name>

=item     $description = $self->description()

This method returns a longer description for the plugin. The text
may contain HTML tags, and should describe what the plugin does and
who wrote it. This text is displayed when the user presses the
"About..." button.

=item     $type = $self->type()

This tells gbrowse what the plugin's type is. It must return one of
the scripts "dumper", "finder", "annotator", "highlighter" or "filter" as
described in the introduction to this documentation. If the method is
not overridden, type() will return "dumper."

=item     $self->init()

This method is called before any methods are invoked and allows the
plugin to do any run-time initialization it needs. The default is to
do nothing. Ordinarily this method does not need to be implemented.

=back

=head1  ACCESS TO THE ENVIRONMENT

The following methods give the plugin access to the environment,
including the gbrowse page settings, the sequence features database, and
the plugin's own configuration settings.

These methods do not generally need to be overridden.

=over

=item     $config = $self->configuration()

Call this method to retrieve the persistent configuration for this
plugin. The configuration is a hashref containing the default
configuration settings established by config_defaults(), possibly
modified by the user. Due to cookie limitations, the values of the
hashref must be scalars or array references.

See CONFIGURATION METHODS for instructions on how to create and
maintain the plugin's persistent configuration information.

=item     $database = $self->database

This method returns a copy of the sequence database. Depending on
the data source chosen by the gbrowse administrator, this may be a
Bio::DB::GFF database, a Bio::DB::Das::Chado database, a Bio::Das
database, a Bio::DB::Das::BioSQL database, or any of the other
Das-like databases that gbrowse supports.

=item     @track_names = $self->selected_tracks

This method returns the list of track names that the user currently
has turned on. Track names are the internal names identified in
gbrowse configuration file stanzas, for example "ORFs" in the
01.yeast.conf example file.

=item     @feature_types = $self->selected_features

This method returns the list of feature types that the user
currently has turned on. Feature types are the feature identifiers
indicated by the "feature" setting in each track in the gbrowse
configuration file, for example "ORF:sgd" in the 01.yeast.conf
[ORFs] track.

=item     $gbrowse_settings = $self->page_settings

This method returns a big hash containing the current gbrowse
persistent user settings. These settings are documented in the
gbrowse executable source code. You will not ordinarily need to
access the contents of this hash, and you should *not* change its
values.

=item     $browser_config = $self->browser_config

This method returns a copy of the Bio::Graphics::Browser object that
drives gbrowse. This object allows you to interrogate (and change!)
the values set in the current gbrowse configuration file.

The recommended use for this object is to recover plugin-specific
settings from the gbrowse configuration file. These can be defined
by the gbrowse administrator by placing the following type of stanza
into the gbrowse config file:

          [GOSearch:plugin]
          traverse_isa = 1
          use_server   = http://amigo.geneontology.org

"GOSearch" is the package name of the plugin, and the ":plugin" part
of the stanza name tells gbrowse that this is a plugin-private
configuration section.

You can now access these settings from within the plugin by using
the following idiom:

           my $browser_config = $self->browser_config; 
           my $traverse_isa = $browser_config->plugin_setting('traverse_isa');
           my $server       = $browser_config->plugin_setting('use_server');

This facility is intended to be used for any settings that should
not be changed by the end user. Persistent user preferences should
be stored in the hash returned by configuration().

=item     $config_path = $self->config_path

This method returns the path to the directory in which gbrowse
stores its configuration files. This is very useful for storing
plugin-specific configuration files. See the sourcecode of
RestrictionAnnotator for an exmaple of this.

=back

=head1 METHODS TO BE IMPLEMENTED IN DUMPERS

All plugins that act as feature dumpers should override one or more of
the methods described in this section.

=over

=item     $self->dump($segment)

Given a Bio::Das::SegmentI object, produce some output from its
sequence and/or features. This can be used to dump something as
simple as a FASTA file, or as complex as a motif analysis performed
on the sequence.

As described in the Bio::Das::SegmentI manpage, the segment object
represents the region of the genome currently on display in the
gbrowse "detail" panel. You may call its seq() method to return the
sequence as a string, or its features() method to return a list of
all features that have been annotated onto this segment of the
genome.

At the time that dump() is called, gbrowse will already have set up
the HTTP header and performed other initialization. The dump()
method merely needs to begin printing output using the appropriate
MIME type. By default, the MIME type is text/plain, but this can be
changed with the mime_type() method described next.

The following trivial example shows a dump() method that prints the
name and length of the segment:

          sub dump {
             my $self = shift;
             my $segment = shift;
             print "name   = ",$segment->seq_id,"\n";
             print "length = ",$segment->length,"\n";
          }

=item     $type = $self->mime_type

Return the MIME type of the information produced by the plugin. By
default, this method returns "text/plain". Override it to return
another MIME type, such as "text/xml".

=back

=head1 METHODS TO BE IMPLEMENTED IN FINDERS

All finder plugins will need to override one or more of the methods
described in this section.

=over

=item     $features = $self->find($segment);

The find() method will be passed a Bio::Das::SegmentI segment
object, as described earlier for the dump() method. Your code should
search the segment for features of interest, and return an arrayref
of Bio::SeqFeatureI objects (see the Bio::SeqFeatureI manpage).
These synthetic feature objects should indicate the position, name
and type of the features found.

Depending on the type of find you are performing, you might search
the preexisting features on the segment for matches, or create your
own features from scratch in the way that the annotator plugins do.
You may choose to ignore the passed segment and perform the search
on the entire database, which you can obtain using the database()
method call.

To create features from scratch I suggest you use either
Bio::Graphics::Feature, or Bio::SeqFeature::Generic to generate the
features. See their respective manual pages for details, and the
OligoFinder.pm plugin for an example of how to do this.

If the plugin requires user input before it can perform its task,
find() should return undef. Gbrowse will invoke configure_form()
followed by reconfigure() in order to prompt the user for input. If
nothing is found, the plugin should return an empty list. The
following is an example of how to prompt the user for input -- in
this case, a gene ontology term:

          sub find {
             my $self = shift;
             my $segment  = shift;  # we ignore this!
             my $config   = $self->configuration;
             my $query    = $config->{query} or return undef;  # PROMPT FOR INPUT
             my $database = $self->database;
             my @features = $database->features(-attributes=>{GO_Term => $query});
             return \@features; 
          }

          sub configure_form {
             my $self = shift;
             return "Enter a GO Term: "
                    . textfield(-name=>$self->config_name('query'));
          }

          sub reconfigure {
             my $self = shift;
             my $config = $self->configuration;
             $config->{query} = $self->config_param('query');
          }

See the sections below for more description of the configure_form()
and reconfigure() methods.

NOTE: If you need to use auxiliary files like BLAST files, you can
store the location of those files in the gbrowse .conf file under
the stanza [YourPlugin:plugin]:

           [YourPlugin:plugin]
           blast_path = /usr/local/blast/databases

           sub find {
              my $self = shift;
              my $segment = shift;  # ignored
              my $blast_path = $self->browser_config->plugin_setting('blast_path');
              # etc etc etc  
           }

=back

=head1 METHODS TO BE IMPLEMENTED IN ANNOTATORS

All annotator plugins will need to override the method described in this
section.

=over

=item     $feature_file = $plugin->annotate($segment)

The annotate() method will be invoked with a Bio::Das::SegmentI
segment representing the region of the genome currently on view in
the gbrowse detail panel. The method should create one or more
Bio::Graphics::Feature objects and add them to a
Bio::Graphics::FeatureFile feature set. The latter acts as a
container for a set of sequence features.

The reason that annotate() returns a Bio::Graphics::FeatureFile
rather than an array of features the way that find() does is because
Bio::Graphics::FeatureFile also allows you to set up how the
features will be rendered; you can define tracks, assign different
feature types to different tracks, and assign each feature type a
glyph, color, and other options.

See the Bio::Graphics::FeatureFile manpage for details, and the
RestrictionAnnotator.pm plugin for an example.

=back

=head1 METHODS TO BE IMPLEMENTED IN HIGHLIGHTERS

All annotator plugins will need to override the method described in this
section.

=over

=item     $color = $plugin->highlight($feature)

The highlight() method will be invoked with a Bio::Das::FeatureI
object representing one of the features currently being rendered.  The
method must either return a color name or undef.  In the former case,
the feature will be rendered on top of a rectangular background of the
indicated color.  In the latter case no highlighting will be applied.
See Bio::Graphics::Panel for a list of valid color names.

See the AttributeHiliter plugin for an example.

=back

=head1 METHODS TO BE IMPLEMENTED IN FILTERS

All filter plugins will need to override/implement the method described in this
section.

=over

=item     $stanza = $plugin->name

The name() method must return the name (stanza) of the track which
should be filtered.

=item     $plugin->filter

This method is invoked by gbrowse in order to set the filter option.
If desired, at the same time the 'key' option can be changed so that 
filtering (or failing) is clearly indicated.

See the FilterTest plugin for an example.

=back


=head1 PERSISTENT CONFIGURATION METHODS

The following methods can be called to retrieve data about the
environment in which the plugin is running. These methods are also used
by gbrowse to change the plugin state.

=over

=item     $config = $self->config_defaults()

This method will be called once at plugin startup time to give the
plugin a chance to set up its default configuration state. If you
implement this method you should return the configuration as a hash
reference in which the values of the hash are either scalar values
or array references. The contents of this hash will be placed in a
cookie in order to establish the state.

You will wish to implement this method if the plugin has
user-modifiable settings.

=item     $self->configure_form()

This method will be called when the user presses the "Configure
plugin" button. You should return the HTML for a fill-out form that
allows the user to change the current settings. The HTML should
contain the contents of an HTML <form> section, but not the actual
<form> and </form> tags. These tags, along with the Submit and
Cancel buttons, will be added automatically. Typically you will
build up the HTML to return using a series of .= append operations.

It is highly recommended that you use the CGI module to generate the
fill-out form. In order to avoid clashing with other parts of
gbrowse, plugin fill-out forms must respect a namespacing convention
in which the name of each form field is preceded by the plugin
package name and a dot. The package name is the last component of
the plugin's package; for example "GoSearch" is the package name for
Bio::Graphics::Browser::Plugin::GoSearch. To represent the "query"
field of the plugin named "GOSearch", the text field must be named
"GOSearch.query".

To make this easier to do right, the Plugin module provides a method
named config_name() which will add the prefix for you. Here is how
to use it with the "query" example:

           $html .= textfield(-name  => $self->config_name('query'));

=item     $self->reconfigure()

If you implement a configure_form() method, you must also implement
a reconfigure() method. This method is called after the user submits
the form and should be used to integrate the form values with the
current configuration.

Remember that the form fields are namespaced. You may recover them
using the CGI param() method by preceding them with the proper
prefix. To make this easier to manage, this module provides a
config_param() method that manages the namespaces transparently.

Here is a working example:

          sub reconfigure {
              my $self = shift;
              my $current_configuration = $self->configuration;
              $current_configuration->{query} = $self->config_param('query');
          }

All this does is to retrieve the current configuration by calling
the configuration() method. The value of the "query" key is then
replaced by a fill-out form parameter named "query", using
config_param() instead of the more familiar CGI module's param()
function.

=back

=head1 SEE ALSO

the Bio::Graphics::Browser manpage

=head1 AUTHOR

Lincoln Stein <lstein@cshl.org>.

Copyright (c) 2003 Cold Spring Harbor Laboratory

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.
