Merge branch 'JABAWS_Release_2_5' into develop
[jabaws.git] / binaries / src / jpred / lib / DSSP.pm
diff --git a/binaries/src/jpred/lib/DSSP.pm b/binaries/src/jpred/lib/DSSP.pm
new file mode 100644 (file)
index 0000000..d82fc89
--- /dev/null
@@ -0,0 +1,260 @@
+package DSSP;
+
+use strict;
+use warnings;
+use Carp;
+use IO::String;
+use UNIVERSAL qw(isa);
+
+use base qw(Root Read);
+
+=head1 DSSP
+
+Object to hold some of the information found in a DSSP file.
+
+=head2 my $dssp = DSSP->new;
+
+Creates new object. Any accessor method is valid to use as an argument in a hash key/value set of pairs.
+
+=head2 $dssp->read($string);
+
+Reads in the DSSP file contained within the $string. Returns true if it there is information in the file, otherwise false.
+
+=cut
+
+sub read {
+       my ($self, $data) = @_;
+
+       defined $data or croak "No filehandle passed\n";
+       local $/ = "\n";
+       my $flag = 0; # Test to see if there's any information in the file
+
+       while (<$data>) {
+               if (/^  #  RESIDUE AA STRUCTURE/) {
+                       $flag = 1;
+                       my $chain;
+
+                       while (<$data>) {
+                               chomp;
+                               my $aa_pri = substr($_, 13, 1);
+
+                               # Chain break, has to come here in order to remember what the
+                               # chain was
+                               if ($aa_pri eq '!') {
+                                       $self->add_position(
+                                               DSSP::Position->new(
+                                                       chain => $chain,
+                                                       break => 1
+                                               )
+                                       );
+                                       next;
+                               }
+
+                               # If there isn't a chain break, continue as normal
+                               my $aa_sec = substr($_, 16, 1);
+                               my $aa_acc = substr($_, 35, 3);
+                               $chain = substr($_, 11, 1);
+
+                               # Field is six wide to include alternatives, but I'm ignoring
+                               # those
+                               my $pos = substr($_, 5, 6);
+                               my $seq_pos = substr($_, 0, 5);
+
+                               # Replace empty chain defn with NULL
+                               $chain = 'NULL' if $chain eq ' ';
+
+                               # Replace lower case letters (cystine
+                               # bridge pairs) with 'c'
+                               $aa_pri =~ tr/[a-z]/c/;
+
+                               $aa_sec =~ tr/ /-/;
+
+                               # Remove any padding whitespace
+                               for ($aa_pri, $aa_sec, $aa_acc, $chain, $pos, $seq_pos) {
+                                       s/ //g
+                               }
+
+                               $self->add_position(
+                                       DSSP::Position->new(
+                                               chain => $chain,
+                                               pri => $aa_pri,
+                                               sec => $aa_sec,
+                                               acc => $aa_acc,
+                                               off => $pos,
+                                               seq_off => $seq_pos,
+                                               break => undef
+                                       )
+                               );
+                       }
+               }
+       }
+       return $flag;
+}
+
+=head2 $dssp->add_position($DSSP::Position);
+
+Adds a position object.
+
+=cut
+
+sub add_position {
+       my ($self, $pos) = @_;
+
+       unless (defined $pos and isa $pos, 'DSSP::Position') {
+               croak "No object passed"
+       }
+
+       my ($chain, $offset);
+
+       defined($chain = $pos->chain) or croak "Chain not set\n"; 
+       unless ($pos->break) {
+               defined($offset = $pos->off) or croak "Offset not set\n";
+       }
+
+       # Use a stack based system now
+       push @{ $self->{__PACKAGE__."pos"}{$chain} }, $pos;
+
+       # This is for backwards compatibility, shudder, also required changes in
+       # get_position() and position()
+       unless ($pos->break) {
+               $self->{__PACKAGE__."offset_compat"}{offset}{$chain}{$offset} = \$self->{__PACKAGE__."pos"}{$chain}->[ -1 ];
+
+               if (exists $self->{__PACKAGE__."pos"}{offset}{$chain}{$offset}) {
+                       my $out = "Overwriting DSSP position $offset in chain $chain";
+                       $out .= $self->path ? " for file ".$self->path."\n" : "\n";
+                       warn $out;
+               }
+       }
+
+       # Add the chain if not already present
+       my @chains = grep { defined } $self->chains;
+       unless (grep { $chain eq $_ } @chains) {
+               $self->chains(@chains, $chain);
+       }
+}
+
+=head2 $DSSP::Position = $dssp->get_chain_defs($chain)
+
+New method to replace get_position() and positions(). Returns all of the DSSP::Position objects for a particular chain in the order they are seen in the DSSP file. Then you have to decide what to do with them.
+
+=cut
+
+sub get_chain_defs {
+       my ($self, $chain) = @_;
+
+       croak "Chain not recognised" unless grep { $_ eq $chain } $self->chains;
+
+       return @{ $self->{__PACKAGE__."pos"}{$chain} };
+}
+
+=head2 $DSSP::Position = $dssp->get_position($chain, $offset)
+
+Depricated! Don't use this methods as it's broken, it's available for backwards compatibility.
+
+Returns the position object for a particular location.
+
+=cut
+
+sub get_position {
+       my ($self, $chain, $offset) = @_;
+
+       unless (defined $chain) { croak "No chain given" }
+       unless (defined $offset) { croak "No offset given" }
+
+       confess("No such chain '$chain'") and return unless exists $self->{__PACKAGE__."pos"}{$chain};
+
+       confess("No such offset $offset in chain $chain") and return
+               #unless exists $self->{__PACKAGE__."pos"}{$chain};
+               unless exists $self->{__PACKAGE__."offset_compat"}{offset}{$chain}{$offset};
+
+       return ${ $self->{__PACKAGE__."offset_compat"}{offset}{$chain}{$offset} };
+}
+
+=head2 $DSSP::Position = $dssp->positions($chain);
+
+Depricated! Don't use this methods as it's broken, it's available for backwards compatibility.
+
+Returns all the offsets that are available for a particular chain, sorted by offset.
+
+=cut
+
+sub positions {
+       my ($self, $chain) = @_;
+
+       unless (defined $chain) { croak "No chain given" }
+
+       croak "No such chain $chain" unless exists $self->{__PACKAGE__."pos"}{$chain};
+
+       no warnings;
+       return
+               map { join "", @{$_} }
+               sort { $a->[0] <=> $b->[0] || $a->[1] cmp $b->[1] }
+               map { [ /^(-?\d+)(\D+)?$/ ] }
+               #keys %{$self->{__PACKAGE__."pos"}{$chain}};
+               keys %{$self->{__PACKAGE__."offset_compat"}{offset}{$chain}};
+}
+
+=head2 @chains = $dssp->chains;
+
+Finds the chains in the structure, or alters them, but don't do that. Returns an empty list if no chains have been found.
+
+=cut
+
+sub chains {
+       my ($self, @chains) = @_;
+       if (@chains) { @{$self->{__PACKAGE__."chains"}} = @chains }
+       elsif ($self->{__PACKAGE__."chains"}) {
+               return @{ $self->{__PACKAGE__."chains"} };
+       }
+       else {
+               return ();
+       }
+}
+
+=head1 DSSP::Position
+
+Object to hold information on each position of a DSSP output.
+
+=head2 my $dssp_pos = DSSP::Position->new;
+
+Create a new object.
+
+=head2 Accessors
+
+=over
+
+=item * chain
+
+Holds the chain ID.
+
+=item * pri
+
+Holds the primary structre.
+
+=item * sec
+
+Holds the secondary structre.
+
+=item * acc
+
+Holds the solvent structre.
+
+=item * off
+
+Holds the position in the PDB structure.
+
+=back
+
+=cut
+
+use Class::Struct "DSSP::Position" => {
+       chain => '$',
+       pri => '$',
+       sec => '$',
+       acc => '$',
+       off => '$',
+       seq_off => '$',
+       break => '$'
+};
+
+1;