ensembl-hive  2.6
Collection.pm
Go to the documentation of this file.
1 =head1 LICENSE
2 
3 Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
4 Copyright [2016-2024] EMBL-European Bioinformatics Institute
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10  http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 =cut
19 
20 =pod
21 
22 =head1 NAME
23 
24 Bio::EnsEMBL::Hive::Utils::Collection - A collection object
25 
26 =cut
27 
28 package Bio::EnsEMBL::Hive::Utils::Collection;
29 
30 use strict;
31 use warnings;
32 
33 sub new {
34  my $class = shift @_;
35 
36  my $self = bless {}, $class;
37 
38  $self->listref( shift @_ || [] );
39 
40  return $self;
41 }
42 
43 
44 sub listref {
45  my $self = shift @_;
46 
47  if(@_) {
48  $self->{'_listref'} = shift @_;
49  }
50  return $self->{'_listref'};
51 }
52 
53 
54 sub list {
55  my $self = shift @_;
56 
57  return @{ $self->listref };
58 }
59 
60 
61 sub present {
62  my $self = shift @_;
63  my $candidate = shift @_;
64 
65  foreach my $element (@{ $self->listref }) {
66  return 1 if($element eq $candidate);
67  }
68  return 0;
69 }
70 
71 
72 sub add {
73  my $self = shift @_;
74 
75  push @{ $self->listref }, @_;
76 }
77 
78 
79 sub add_once {
80  my $self = shift @_;
81  my $candidate = shift @_;
82 
83  unless( $self->present( $candidate ) ) {
84  $self->add( $candidate );
85  }
86 }
87 
88 
89 sub forget {
90  my $self = shift @_;
91  my $candidate = shift @_;
92 
93  my $listref = $self->listref;
94 
95  for(my $i=scalar(@$listref)-1;$i>=0;$i--) {
96  if($listref->[$i] eq $candidate) {
97  splice @$listref, $i, 1;
98  }
99  }
100 }
101 
102 
103 sub find_one_by {
104  my ($self, %method_to_filter_value) = @_;
105 
106  ELEMENT: foreach my $element (@{ $self->listref }) {
107  keys %method_to_filter_value; # sic! This is to "rewind" the each% operator to the beginning each time
108  while(my ($filter_name, $filter_value) = each %method_to_filter_value) {
109  my $actual_value = (ref($element) eq 'HASH') ? $element->{$filter_name} : $element->$filter_name();
110  next ELEMENT unless( defined($actual_value) # either both defined and equal or neither defined
111  ? defined($filter_value) && ( (ref($filter_value) eq 'CODE')
112  ? &$filter_value( $actual_value )
113  : ( $filter_value eq $actual_value )
114  )
115  : !defined($filter_value)
116  );
117  }
118  return $element;
119  }
120 
121  return undef; # have to be explicit here to avoid surprises
122 }
123 
124 
125 sub find_all_by {
126  my ($self, %method_to_filter_value) = @_;
127 
128  my @filtered_elements = ();
129 
130  ELEMENT: foreach my $element (@{ $self->listref }) {
131  keys %method_to_filter_value; # sic! This is to "rewind" the each% operator to the beginning each time
132  while(my ($filter_name, $filter_value) = each %method_to_filter_value) {
133  my $actual_value = (ref($element) eq 'HASH') ? $element->{$filter_name} : $element->$filter_name();
134  next ELEMENT unless( defined($actual_value) # either both defined and equal or neither defined
135  ? defined($filter_value) && ( (ref($filter_value) eq 'CODE')
136  ? &$filter_value( $actual_value )
137  : ( $filter_value eq $actual_value )
138  )
139  : !defined($filter_value)
140  );
141  }
142  push @filtered_elements, $element;
143  }
144 
145  return \@filtered_elements;
146 }
147 
148 
149 sub _find_all_by_subpattern { # subpatterns can be combined into full patterns using +-,
150  my ($self, $pattern) = @_;
151 
152  my $filtered_elements = [];
153  $pattern //= '';
154 
155  if( $pattern=~/^\d+$/ ) {
156 
157  $filtered_elements = $self->find_all_by( 'dbID', $pattern );
158 
159  } elsif( $pattern=~/^(\d+)\.\.(\d+)$/ ) {
160 
161  $filtered_elements = $self->find_all_by( 'dbID', sub { return $1<=$_[0] && $_[0]<=$2; } );
162 
163  } elsif( $pattern=~/^(\d+)\.\.$/ ) {
164 
165  $filtered_elements = $self->find_all_by( 'dbID', sub { return $1<=$_[0]; } );
166 
167  } elsif( $pattern=~/^\.\.(\d+)$/ ) {
168 
169  $filtered_elements = $self->find_all_by( 'dbID', sub { return $_[0]<=$1; } );
170 
171  } elsif( $pattern=~/^\w+$/) {
172 
173  $filtered_elements = $self->find_all_by( 'name', $pattern );
174 
175  } elsif( $pattern=~/^[\w\%]+$/) {
176 
177  $pattern=~s/\%/.*/g;
178  $filtered_elements = $self->find_all_by( 'name', sub { return $_[0]=~/^${pattern}$/; } );
179 
180  } elsif( $pattern=~/^(\w+)==(.*)$/) {
181 
182  $filtered_elements = $self->find_all_by( $1, $2 );
183 
184  } elsif( $pattern=~/^(\w+)!=(.*)$/) {
185 
186  $filtered_elements = $self->find_all_by( $1, sub { return $_[0] ne $2; } );
187 
188  } elsif( $pattern=~/^(\w+)<=(.*)$/) { # NB: the order is important - all digraphs should be parsed before their proper prefixes
189 
190  $filtered_elements = $self->find_all_by( $1, sub { return $_[0] <= $2; } );
191 
192  } elsif( $pattern=~/^(\w+)>=(.*)$/) {
193 
194  $filtered_elements = $self->find_all_by( $1, sub { return $_[0] >= $2; } );
195 
196  } elsif( $pattern=~/^(\w+)<(.*)$/) {
197 
198  $filtered_elements = $self->find_all_by( $1, sub { return $_[0] < $2; } );
199 
200  } elsif( $pattern=~/^(\w+)>(.*)$/) {
201 
202  $filtered_elements = $self->find_all_by( $1, sub { return $_[0] > $2; } );
203 
204  } elsif( $pattern=~/^(\w+)~(.*)$/) {
205 
206  my $field = $1;
207  $pattern = $2;
208  $pattern =~ s/\%/.*/g;
209  $filtered_elements = $self->find_all_by( $field, sub { return $_[0 ]=~ /^(.*,)?${pattern}(,.*)?$/; } );
210 
211  } else {
212  die "The pattern '$pattern' is not recognized\n";
213  }
214 
215  return $filtered_elements;
216 }
217 
218 
219 =head2 find_all_by_pattern
220 
221  Arg [1] : (optional) string $pattern
222  Example : my $first_fifteen_analyses_and_two_more = $collection->find_all_by_pattern( '1..15,analysis_X,21' );
223  Example : my $two_open_ranges = $collection->>find_all_by_pattern( '..7,10..' );
224  Example : my $double_exclusion = $collection->find_all_by_pattern( '1..15-3..5+4' );
225  Example : my $blast_related_with_exceptions = $collection->find_all_by_pattern( 'blast%-12-%funnel' );
226  Description: Filters an arrayref of non-repeating objects from the given collection by interpreting a pattern.
227  The pattern can contain individual analyses_ids, individual logic_names,
228  open and closed ranges of analysis_ids, wildcard patterns of logic_names,
229  merges (+ or ,) and exclusions (-) of the above subsets.
230  Exceptions : none
231  Caller : both beekeeper.pl (for scheduling) and runWorker.pl (for specialization)
232 
233 =cut
234 
235 sub find_all_by_pattern {
236  my ($self, $pattern) = @_;
237 
238  if( not defined($pattern) ) {
239 
240  return [ $self->list ];
241 
242  } else {
243 
244  # By using the grouping, we ask Perl to return the pattern and their delimiters
245  my @syll = split(/([+\-,])/, $pattern);
246 
247  my %uniq = map { ("$_" => $_) } @{ $self->_find_all_by_subpattern( shift @syll ) }; # initialize with the first syllable
248 
249  while(@syll) {
250  my $operation = shift @syll; # by construction this is one of [+-,]
251  my $subpattern = shift @syll; # can be an empty string
252 
253  foreach my $element (@{ $self->_find_all_by_subpattern( $subpattern ) }) {
254  if($operation eq '-') {
255  delete $uniq{ "$element" };
256  } else {
257  $uniq{ "$element" } = $element;
258  }
259  }
260  }
261 
262  return [ values %uniq ];
263  }
264 }
265 
266 
267 sub dark_collection { # contain another collection of objects marked for deletion
268  my $self = shift @_;
269 
270  if(@_) {
271  $self->{'_dark_collection'} = shift @_;
272  }
273  return $self->{'_dark_collection'};
274 }
275 
276 
277 sub forget_and_mark_for_deletion {
278  my $self = shift @_;
279  my $candidate = shift @_;
280 
281  $self->forget( $candidate );
282 
283  unless( $self->dark_collection ) {
284  $self->dark_collection( Bio::EnsEMBL::Hive::Utils::Collection->new );
285  }
286  $self->dark_collection->add( $candidate );
287 }
288 
289 
290 sub DESTROY {
291  my $self = shift @_;
292 
293  $self->listref( [] );
294  $self->dark_collection( undef );
295 }
296 
297 1;
map
public map()
Bio::EnsEMBL::Hive::Version
Definition: Version.pm:19
Bio::EnsEMBL::Hive::Utils::Collection
Definition: Collection.pm:5
Bio::EnsEMBL::Hive::Utils::Collection::listref
public listref()