sync last commit
[bioperl-live.git] / Bio / Map / PositionHandler.pm
blob5bcf8d9244d5683c3f674ee71a36961518241c22
1 # $Id$
3 # BioPerl module for Bio::Map::PositionHandler
5 # Cared for by Sendu Bala <bix@sendu.me.uk>
7 # Copyright Sendu Bala
9 # You may distribute this module under the same terms as perl itself
11 # POD documentation - main docs before the code
13 =head1 NAME
15 Bio::Map::PositionHandler - A Position Handler Implementation
17 =head1 SYNOPSIS
19 # This is used by modules when they want to implement being a
20 # Position or being something that has Positions (when they are
21 # a L<Bio::Map::EntityI>)
23 # Make a PositionHandler that knows about you
24 my $ph = Bio::Map::PositionHandler->new($self);
26 # Register with it so that it handles your Position-related needs
27 $ph->register;
29 # If you are a position, get/set the map you are on and the marker you are
30 # for
31 $ph->map($map);
32 $ph->element($marker);
33 my $map = $ph->map;
34 my $marker = $ph->element;
36 # If you are a marker, add a new position to yourself
37 $ph->add_positions($pos);
39 # And then get all your positions on a particular map
40 foreach my $pos ($ph->get_positions($map)) {
41 # do something with this Bio::Map::PositionI
44 # Or find out what maps you exist on
45 my @maps = $ph->get_other_entities;
47 # The same applies if you were a map
49 =head1 DESCRIPTION
51 A Position Handler copes with the coordination of different Bio::Map::EntityI
52 objects, adding and removing them from each other and knowning who belongs to
53 who. These relationships between objects are based around shared Positions,
54 hence PositionHandler.
56 This PositionHandler is able to cope with Bio::Map::PositionI objects,
57 Bio::Map::MappableI objects and Bio::Map::MapI objects.
59 =head1 FEEDBACK
61 =head2 Mailing Lists
63 User feedback is an integral part of the evolution of this and other
64 Bioperl modules. Send your comments and suggestions preferably to
65 the Bioperl mailing list. Your participation is much appreciated.
67 bioperl-l@bioperl.org - General discussion
68 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
70 =head2 Reporting Bugs
72 Report bugs to the Bioperl bug tracking system to help us keep track
73 of the bugs and their resolution. Bug reports can be submitted via the
74 web:
76 http://bugzilla.open-bio.org/
78 =head1 AUTHOR - Sendu Bala
80 Email bix@sendu.me.uk
82 =head1 APPENDIX
84 The rest of the documentation details each of the object methods.
85 Internal methods are usually preceded with a _
87 =cut
89 # Let the code begin...
91 package Bio::Map::PositionHandler;
92 use strict;
94 use base qw(Bio::Root::Root Bio::Map::PositionHandlerI);
96 # globally accessible hash, via private instance methods
97 my $RELATIONS = {};
99 =head2 General methods
101 =cut
103 =head2 new
105 Title : new
106 Usage : my $position_handler = Bio::Map::PositionHandler->new(-self => $self);
107 Function: Get a Bio::Map::PositionHandler that knows who you are.
108 Returns : Bio::Map::PositionHandler object
109 Args : -self => Bio::Map::EntityI that is you
111 =cut
113 sub new {
114 my ($class, @args) = @_;
115 my $self = $class->SUPER::new(@args);
117 my ($you) = $self->_rearrange([qw(SELF)], @args);
119 $self->throw('Must supply -self') unless $you;
120 $self->throw('-self must be a reference (object)') unless ref($you);
121 $self->throw('This is [$you], not a Bio::Map::EntityI object') unless $you->isa('Bio::Map::EntityI');
122 $self->{_who} = $you;
123 $self->{_rel} = $RELATIONS;
124 return $self;
127 =head2 register
129 Title : register
130 Usage : $position_handler->register();
131 Function: Ask this Position Handler to look after your entity relationships.
132 Returns : n/a
133 Args : none
135 =cut
137 sub register {
138 my $self = shift;
139 my $you = $self->{_who};
141 $self->throw("Trying to re-register [$you], which could be bad") if $you->get_position_handler->index;
143 $self->{_index} = ++$self->{_rel}->{assigned_indices};
144 $self->{_rel}->{registered}->{$self->{_index}} = $you;
147 =head2 index
149 Title : index
150 Usage : my $index = $position_handler->index();
151 Function: Get the unique registry index for yourself, generated during the
152 resistration process.
153 Returns : int
154 Args : none
156 =cut
158 sub index {
159 my $self = shift;
160 return $self->{_index};
163 =head2 get_entity
165 Title : get_entity
166 Usage : my $entity = $position_handler->get_entity($index);
167 Function: Get the entity that corresponds to the supplied registry index.
168 Returns : Bio::Map::EntityI object
169 Args : int
171 =cut
173 sub get_entity {
174 my ($self, $index) = @_;
175 return $self->{_rel}->{registered}->{$index} || $self->throw("Requested registy index '$index' but that index isn't in the registry");
178 =head2 Methods for Bio::Map::PositionI objects
180 =cut
182 =head2 map
184 Title : map
185 Usage : my $map = $position_handler->map();
186 $position_handler->map($map);
187 Function: Get/Set the map you are on. You must be a Position.
188 Returns : L<Bio::Map::MapI>
189 Args : none to get, OR
190 new L<Bio::Map::MapI> to set
192 =cut
194 sub map {
195 my ($self, $entity) = @_;
196 return $self->_pos_get_set($entity, 'position_maps', 'Bio::Map::MapI');
199 =head2 element
201 Title : element
202 Usage : my $element = $position_handler->element();
203 $position_handler->element($element);
204 Function: Get/Set the map element you are for. You must be a Position.
205 Returns : L<Bio::Map::MappableI>
206 Args : none to get, OR
207 new L<Bio::Map::MappableI> to set
209 =cut
211 sub element {
212 my ($self, $entity) = @_;
213 return $self->_pos_get_set($entity, 'position_elements', 'Bio::Map::MappableI');
216 =head2 Methods for all other Bio::Map::EntityI objects
218 =cut
220 =head2 add_positions
222 Title : add_positions
223 Usage : $position_handler->add_positions($pos1, $pos2, ...);
224 Function: Add some positions to yourself. You can't be a position.
225 Returns : n/a
226 Args : Array of Bio::Map::PositionI objects
228 =cut
230 sub add_positions {
231 my $self = shift;
232 $self->throw('Must supply at least one Bio::Map::EntityI') unless @_ > 0;
233 my $you_index = $self->_get_you_index(0);
234 my $kind = $self->_get_kind;
236 foreach my $pos (@_) {
237 $self->_check_object($pos, 'Bio::Map::PositionI');
238 my $pos_index = $self->_get_other_index($pos);
240 $self->_pos_set($pos_index, $you_index, $kind);
244 =head2 get_positions
246 Title : get_positions
247 Usage : my @positions = $position_handler->get_positions();
248 Function: Get all your positions. You can't be a Position.
249 Returns : Array of Bio::Map::PositionI objects
250 Args : none for all, OR
251 Bio::Map::EntityI object to limit the Positions to those that
252 are shared by you and this other entity.
254 =cut
256 sub get_positions {
257 my ($self, $entity) = @_;
258 my $you_index = $self->_get_you_index(0);
260 my @positions = keys %{$self->{_rel}->{has}->{$you_index}};
262 if ($entity) {
263 my $entity_index = $self->_get_other_index($entity);
264 my $pos_ref = $self->{_rel}->{has}->{$entity_index};
265 @positions = grep { $pos_ref->{$_} } @positions;
268 return map { $self->get_entity($_) } @positions;
271 =head2 purge_positions
273 Title : purge_positions
274 Usage : $position_handler->purge_positions();
275 Function: Remove all positions from yourself. You can't be a Position.
276 Returns : n/a
277 Args : none to remove all, OR
278 Bio::Map::PositionI object to remove only that entity, OR
279 Bio::Map::EntityI object to limit the removal to those Positions that
280 are shared by you and this other entity.
282 =cut
284 sub purge_positions {
285 my ($self, $thing) = @_;
286 my $you_index = $self->_get_you_index(0);
287 my $kind = $self->_get_kind;
289 my @pos_indices;
290 if ($thing) {
291 $self->throw("Must supply an object") unless ref($thing);
292 if ($thing->isa("Bio::Map::PositionI")) {
293 @pos_indices = ($self->_get_other_index($thing));
295 else {
296 my $entity_index = $self->_get_other_index($thing);
297 my $pos_ref = $self->{_rel}->{has}->{$entity_index};
298 @pos_indices = grep { $pos_ref->{$_} } keys %{$self->{_rel}->{has}->{$you_index}};
301 else {
302 @pos_indices = keys %{$self->{_rel}->{has}->{$you_index}};
305 foreach my $pos_index (@pos_indices) {
306 $self->_purge_pos_entity($pos_index, $you_index, $kind);
310 =head2 get_other_entities
312 Title : get_other_entities
313 Usage : my @entities = $position_handler->get_other_entities();
314 Function: Get all the entities that share your Positions. You can't be a
315 Position.
316 Returns : Array of Bio::Map::EntityI objects
317 Args : none
319 =cut
321 sub get_other_entities {
322 my $self = shift;
323 my $you_index = $self->_get_you_index(0);
324 my $kind = $self->_get_kind;
325 my $want = $kind eq 'position_elements' ? 'position_maps' : 'position_elements';
327 my %entities;
328 while (my ($pos_index) = each %{$self->{_rel}->{has}->{$you_index}}) {
329 my $entity_index = $self->{_rel}->{$want}->{$pos_index};
330 $entities{$entity_index} = 1 if $entity_index;
333 return map { $self->get_entity($_) } keys %entities;
336 # do basic check on an object, make sure it is the right type
337 sub _check_object {
338 my ($self, $object, $interface) = @_;
339 $self->throw("Must supply an arguement") unless $object;
340 $self->throw("This is [$object], not an object") unless ref($object);
341 $self->throw("This is [$object], not a $interface") unless $object->isa($interface);
344 # get the object we are the handler of, its index, and throw depending on if
345 # we're a Position
346 sub _get_you_index {
347 my ($self, $should_be_pos) = @_;
348 my $you = $self->{_who};
349 if ($should_be_pos) {
350 $self->throw("This is not a Position, method invalid") unless $you->isa('Bio::Map::PositionI');
352 else {
353 $self->throw("This is a Position, method invalid") if $you->isa('Bio::Map::PositionI');
355 return $self->index;
358 # check an entity is registered and get its index
359 sub _get_other_index {
360 my ($self, $entity) = @_;
361 $self->throw("Must supply an object") unless ref($entity);
362 my $index = $entity->get_position_handler->index;
363 $self->throw("Entity doesn't seem like it's been registered") unless $index;
364 $self->throw("Entity may have been registered with a different PositionHandler, can't deal with it") unless $entity eq $self->get_entity($index);
365 return $index;
368 # which of the position hashes should we be recorded under?
369 sub _get_kind {
370 my $self = shift;
371 my $you = $self->{_who};
372 return $you->isa('Bio::Map::MapI') ? 'position_maps' : $you->isa('Bio::Map::MappableI') ? 'position_elements' : $self->throw("This is [$you] which is an unsupported kind of entity");
375 # get/set position entity
376 sub _pos_get_set {
377 my ($self, $entity, $kind, $interface) = @_;
378 my $you_index = $self->_get_you_index(1);
380 my $entity_index;
381 if ($entity) {
382 $self->_check_object($entity, $interface);
383 my $new_entity_index = $self->_get_other_index($entity);
384 $entity_index = $self->_pos_set($you_index, $new_entity_index, $kind);
387 $entity_index ||= $self->{_rel}->{$kind}->{$you_index} || 0;
388 if ($entity_index) {
389 return $self->get_entity($entity_index);
391 return;
394 # set position entity
395 sub _pos_set {
396 my ($self, $pos_index, $new_entity_index, $kind) = @_;
397 my $current_entity_index = $self->{_rel}->{$kind}->{$pos_index} || 0;
399 if ($current_entity_index) {
400 if ($current_entity_index == $new_entity_index) {
401 return $current_entity_index;
404 $self->_purge_pos_entity($pos_index, $current_entity_index, $kind);
407 $self->{_rel}->{has}->{$new_entity_index}->{$pos_index} = 1;
408 $self->{_rel}->{$kind}->{$pos_index} = $new_entity_index;
409 return $new_entity_index;
412 # disassociate position from one of its current entities
413 sub _purge_pos_entity {
414 my ($self, $pos_index, $entity_index, $kind) = @_;
415 delete $self->{_rel}->{has}->{$entity_index}->{$pos_index};
416 delete $self->{_rel}->{$kind}->{$pos_index};