Newer
Older
/* Bases.java
*
* created: Sun Oct 11 1998
*
* This file is part of Artemis
*
* Copyright (C) 1998,1999,2000 Genome Research Limited
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/sequence/Bases.java,v 1.8 2004-12-23 17:01:15 tjc Exp $
*/
package uk.ac.sanger.artemis.sequence;
import uk.ac.sanger.artemis.util.*;
import uk.ac.sanger.artemis.io.Range;
import uk.ac.sanger.artemis.io.EmblStreamSequence;
import uk.ac.sanger.artemis.io.Sequence;
import org.biojava.bio.symbol.IllegalSymbolException;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.Enumeration;
import java.util.Iterator;
/**
* This class is a wrapper for the uk.ac.sanger.artemis.io.Sequence class
* that allows us to control what is done to the sequence and to send events
* to interested objects when changes happen. Note: a '@' character is used
* as a marker when we don't have a base letter, for example complementing a
* non-base letter returns '@'.
*
* @author Kim Rutherford
* @version $Id: Bases.java,v 1.8 2004-12-23 17:01:15 tjc Exp $ */
/**
* Indicates the bases should be read in the forward direction for a
* particular operation.
**/
static public final int FORWARD = 1;
/**
* Indicates the bases should be read in the reverse direction for a
* particular operation.
**/
static public final int REVERSE = 2;
/**
* The lowest possible value for use with addSequenceChangeListener ().
**/
static public final int MIN_PRIORITY = -5;
/**
* An arbitrary value for use with addSequenceChangeListener ().
**/
static public final int MEDIUM_PRIORITY = 0;
/**
* The highest possible value for use with addSequenceChangeListener ().
**/
static public final int MAX_PRIORITY = 5;
/**
* Create a new Bases object.
* @param sequence The raw sequence that the new object will use.
**/
this.embl_sequence = sequence;
forward_stop_codon_cache = null;
reverse_stop_codon_cache = null;
forward_strand = new Strand(this, FORWARD);
reverse_strand = new Strand(this, REVERSE);
for(int i = 0 ; i < listener_hash_map_array.length ; ++i)
listener_hash_map_array [i] = new WeakHashMap();
}
/**
* Return the object representing the forward sequence of bases for this
* object.
**/
return forward_strand;
}
/**
* Return the object representing the reverse complemented sequence of
* bases for this Bases objects.
**/
return reverse_strand;
}
/**
* Returns the length of the sequence in bases.
**/
public int getLength()
{
return embl_sequence.length();
}
/**
* Return a String representation of the sequence.
**/
public String toString()
{
return embl_sequence.toString();
}
/**
* Reverse and complement both of the Strand objects (by swapping them and
* reverse complementing the sequence).
* @exception ReadOnlyException If the Bases cannot be changed.
**/
public void reverseComplement()
throws ReadOnlyException
{
forward_stop_codon_cache = null;
reverse_stop_codon_cache = null;
final Strand temp = forward_strand;
forward_strand = reverse_strand;
reverse_strand = temp;
final String new_sequence =
reverseComplement(getSequence().getSubSequence(1, getLength()));
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
try {
getSequence ().setFromString (new_sequence);
} catch (IllegalSymbolException e) {
throw new Error ("internal error - unexpected exception: " + e);
}
final SequenceChangeEvent event =
new SequenceChangeEvent (this, SequenceChangeEvent.REVERSE_COMPLEMENT);
fireSequenceChangeEvent (event);
}
/**
* This array is used to convert between bases and indices. See
* getIndexOfBase ()
**/
public final static char [] letter_index = {
't', 'c', 'a', 'g', 'n'
};
/**
* Given a base letter return its index where t = 0, c = 1, a = 2, g = 3, 4
* otherwise.
* See letter_index.
**/
public static int getIndexOfBase (final char base) {
switch (base) {
case 'c':
return 1;
case 'a':
return 2;
case 'g':
return 3;
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
}
return 4;
}
/**
* Return the complement of the given Range. eg. if the sequence length is
* 100 and the Range is 1..10 then the return value will be 90..100.
**/
public Range complementRange (final Range range) {
final int real_start = getComplementPosition (range.getEnd ());
final int real_end = getComplementPosition (range.getStart ());
try {
final Range real_range = new Range (real_start, real_end);
return real_range;
} catch (OutOfRangeException e) {
throw new Error ("internal error - unexpected exception: " + e);
}
}
/**
* Return the complement of the given position on the sequence. eg. if the
* sequence length is 100 and the position is 10 then the return value will
* be 90.
**/
public int getComplementPosition (final int position) {
return getLength () - position + 1;
}
/**
* Return the raw of a base position on this object. The raw position of a
* base on the forward strand is the same as the position itself. The raw
* position of a base on the reverse strand is position of the
* corresponding bases on the forward strand.
* @param position The position of the base.
* @param direction The direction (strand) that the position refers to.
**/
public int getRawPosition (final int position, final int direction) {
if (direction == FORWARD) {
return position;
} else {
return getComplementPosition (position);
}
}
/**
* Translate a sequence of bases into the corresponding single letter amino
* acid codes.
* @param range The range of the bases to translated. If the range.start
* - range.end + 1 is not a multiple of three the last codon is
* incomplete and will not be translated. If the range is out of range
* ie. it has a start or end less than one or greater than the length of
* the sequence, then the out of range codons will be translated as '.'.
* @param direction The direction of the translation. If FORWARD the
* translation will happen as expected, if REVERSE the translation will
* be done on the reverse complement.
* @param unknown_is_x If this parameter is true codons that contain
* ambiguous bases will be translated as 'x', if false they will be
* translated as '.'
* @return The translated sequence in one letter abbreviated form.
**/
public AminoAcidSequence getTranslation (final Range range,
final int direction,
final boolean unknown_is_x) {
// getSubSequence () will return a sequence going in the right direction
// so we don't have to worry.
final String sub_sequence = getSubSequence (range, direction);
return AminoAcidSequence.getTranslation (sub_sequence, unknown_is_x);
}
public AminoAcidSequence getSpacedTranslation (final Range range,
final int direction,
final boolean unknown_is_x) {
// getSubSequence () will return a sequence going in the right direction
// so we don't have to worry.
final String sub_sequence = getSubSequence (range, direction);
return AminoAcidSequence.getSpacedTranslation (sub_sequence, unknown_is_x);
}
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/**
* Return an array containing the positions of the codons that match the
* strings given by the query_codons argument. Only those codons that are
* in the same frame as the first base of the range are returned.
* @param range The inclusive range of bases to get the codons from.
* @param direction The direction of the translation. REVERSE means
* translate the reverse complement bases (the positions in the range
* argument are complemented first.)
* @param query_codons The codons to search for. Each element of this
* vector should be a string that is 3 characters long.
* @return An array containing the positions of the first base of the
* codons. This array is padded with zeros at the end.
**/
public int [] getMatchingCodons (final Range range, final int direction,
final StringVector query_codons) {
final Range real_range;
if (direction == FORWARD) {
real_range = range;
} else {
real_range = complementRange (range);
}
// guess the number of codons in getCount () bases - there are
// query_codons.size() search codons in every 64 codons if G+C is 50%
// and we have getCount()/3 codons to look at.
float at_content = (100 - getAverageGCPercent ()) / 100;
int array_start_size =
(int) (range.getCount () *
at_content * at_content * (2-at_content) *
query_codons.size () / 64);
if (array_start_size < 20) {
array_start_size = 20;
}
// this array will be resized as necessary
int [] return_positions = new int [array_start_size];
int current_return_array_index = 0;
final String sequence_string =
getSequence ().getSubSequence (1, getLength ());
final int range_start_index = real_range.getStart () - 1;
final int range_end_index = real_range.getEnd () - 1;
if (direction == FORWARD) {
for (int i = range_start_index ; i < range_end_index - 2 ; i += 3) {
if (i < 0 || i >= sequence_string.length () - 2) {
continue;
}
boolean is_matching_codon =
isMatchingCodon (sequence_string, i, direction, query_codons);
if (is_matching_codon) {
if (current_return_array_index == return_positions.length) {
// first reallocate the array
final int [] new_array =
new int [return_positions.length * 3 / 2 + 1];
System.arraycopy (return_positions, 0,
new_array, 0,
return_positions.length);
return_positions = new_array;
}
return_positions[current_return_array_index] = i + 1;
++current_return_array_index;
}
}
} else {
for (int i = range_end_index ; i > range_start_index + 2 ; i -= 3) {
if (i < 2 || i >= sequence_string.length ()) {
continue;
}
boolean is_matching_codon =
isMatchingCodon (sequence_string, i, direction, query_codons);
if (is_matching_codon) {
if (current_return_array_index == return_positions.length) {
// first reallocate the array
final int [] new_array =
new int [return_positions.length * 3 / 2 + 1];
System.arraycopy (return_positions, 0,
new_array, 0,
return_positions.length);
return_positions = new_array;
}
// return the complemented base position
return_positions[current_return_array_index] =
sequence_string.length () - i;
++current_return_array_index;
}
}
}
return return_positions;
}
/**
* Check a three character substring and return true if and only if the
* three bases match an element of the query_codons argument. If the
* direction is REVERSE then the three bases to check are at start_index,
* start_index - 1 and start_index - 2. In that case true is returned if
* and only the complement of those three bases matches.
**/
private boolean isMatchingCodon (final String sequence_string,
final int start_index,
final int direction,
final StringVector query_codons) {
for (int query_codon_index = 0 ;
query_codon_index < query_codons.size () ;
++query_codon_index) {
if (isMatchingCodon (sequence_string, start_index, direction,
query_codons.elementAt (query_codon_index))) {
return true;
}
}
return false;
}
/**
* Check a three character substring and return true if and only if the
* three bases match the query_codon argument. If the direction is
* REVERSE then the three bases to check are at start_index, start_index -
* 1 and start_index - 2. In that case true is returned if and only the
* complement of those three bases matches.
**/
private boolean isMatchingCodon (final String sequence_string,
final int start_index,
final int direction,
final String query_codon) {
if (direction == FORWARD) {
if (query_codon.charAt (0) == sequence_string.charAt (start_index) &&
query_codon.charAt (1) == sequence_string.charAt (start_index + 1) &&
query_codon.charAt (2) == sequence_string.charAt (start_index + 2)) {
return true;
}
} else {
final char first_letter =
complement (sequence_string.charAt (start_index));
final char second_letter =
complement (sequence_string.charAt (start_index - 1));
final char third_letter =
complement (sequence_string.charAt (start_index - 2));
if (query_codon.charAt (0) == first_letter &&
query_codon.charAt (1) == second_letter &&
query_codon.charAt (2) == third_letter) {
return true;
}
}
return false;
}
/**
* A cache of the forward stop codon positions.
* 0 means not set/not cached yet, 1 not a stop codon, 2 is a stop codon.
**/
private byte [] forward_stop_codon_cache = null;
/**
* A cache of the reverse stop codon positions.
* 0 means not set/not cached yet, 1 not a stop codon, 2 is a stop codon.
**/
private byte [] reverse_stop_codon_cache = null;
/**
* Returns forward_stop_codon_cache after allocating it (if it is null).
**/
private byte [] getForwardStopCodonCache () {
if (forward_stop_codon_cache == null) {
forward_stop_codon_cache = new byte [getLength ()];
}
return forward_stop_codon_cache;
}
/**
* Returns reverse_stop_codon_cache after allocating it (if it is null).
**/
private byte [] getReverseStopCodonCache () {
if (reverse_stop_codon_cache == null) {
reverse_stop_codon_cache = new byte [getLength ()];
}
return reverse_stop_codon_cache;
}
/**
* Clear stop codon cache (forward and reverse).
**/
public void clearStopCodonCache()
{
forward_stop_codon_cache = null;
reverse_stop_codon_cache = null;
}
/**
* Return an array containing the positions of the stop codons. Only those
* codons that are in the same frame as the first base of the range are
* returned.
* @param range The inclusive range of bases to get the stop codons from.
* @param direction The direction of the translation. REVERSE means
* translate the reverse complement bases (the positions in the range
* argument are complemented first.)
* @return An array containing the positions of the first base of the stop
* codons. This array is padded with zeros at the end.
**/
public int[] getStopCodons(final Range range, final int direction)
{
// guess the number of stop codons in getCount() bases - there are 3
// stop codons in every 64 codons if G+C is 50% and we have getCount()/3
// codons to look at.
float at_content = (100 - getAverageGCPercent()) / 100;
array_start_size = 20;
// this array will be resized as necessary
int[] return_positions = new int[array_start_size];
int range_start_index = real_range.getStart();
int range_end_index = real_range.getEnd();
final int sequence_string_length = getLength();
if(range_start_index < 1)
range_start_index = 1;
if(range_end_index > sequence_string_length)
range_end_index = sequence_string_length;
final char sequence_string[] =
getSequence().getCharSubSequence(range_start_index, range_end_index);
range_start_index--;
range_end_index--;
final byte[] forward_stop_codon_flags = getForwardStopCodonCache();
final byte[] reverse_stop_codon_flags = getReverseStopCodonCache();
if(direction == FORWARD)
{
for(int i = range_start_index; i < range_end_index - 2; i += 3)
{
if(i < 0 || i >= sequence_string_length - 2)
if(isStopCodon (sequence_string, i-range_start_index, direction))
{
if(is_stop_codon)
{
if(current_return_array_index == return_positions.length)
{
final int[] new_array =
new int[return_positions.length * 3 / 2 + 1];
System.arraycopy(return_positions, 0,
new_array, 0,
return_positions.length);
// negative position marks an illegal codon
if(is_stop_codon)
return_positions[current_return_array_index] = -(i + 1);
++current_return_array_index;
}
}
}
else
{
for (int i = range_end_index ; i > range_start_index + 2 ; i -= 3)
{
if(i < 2 || i >= sequence_string_length)
if(reverse_stop_codon_flags[i] == 0)
{
if(isStopCodon(sequence_string, i-range_start_index, direction))
{
}
else
{
if(reverse_stop_codon_flags[i] == 2)
if(is_stop_codon)
{
if(current_return_array_index == return_positions.length)
{
final int[] new_array =
new int[return_positions.length * 3 / 2 + 1];
System.arraycopy(return_positions, 0,
new_array, 0,
return_positions.length);
// return the complemented base position
if(is_stop_codon)
++current_return_array_index;
}
}
}
return return_positions;
}
/**
* Return the base at the given position.
**/
public char getBaseAt (final int position)
throws OutOfRangeException
{
if(position > getLength())
throw new OutOfRangeException(position + " > " + getLength());
if(position < 1)
throw new OutOfRangeException(position + " < " + 1);
return toString().charAt(position - 1);
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
}
/**
* Return a sub sequence of the bases from this object.
* @param range The range of the bases to be extracted.
* @param direction The direction of the returned sequence. If FORWARD the
* sub sequence will be as expected, if REVERSE it will be reverse
* complemented.
* @return The extracted sequence, which will include the end bases of the
* range.
**/
public String getSubSequence (final Range range, final int direction) {
final Range real_range;
if (direction == FORWARD) {
real_range = range;
} else {
real_range = complementRange (range);
}
// we need to make sure that we pass in-range coordinates to
// Sequence.getSubSequence()
final int sub_seq_start_index;
final int sub_seq_end_index;
if (real_range.getStart () < 1) {
sub_seq_start_index = 1;
} else {
sub_seq_start_index = real_range.getStart ();
}
if (real_range.getEnd () > getLength ()) {
sub_seq_end_index = getLength ();
} else {
sub_seq_end_index = real_range.getEnd ();
}
String sub_sequence =
getSequence ().getSubSequence (sub_seq_start_index, sub_seq_end_index);
// sanity checks - if the user asks for more bases than we
// have, we return the symbol "@" for the out-of-range bases.
if (real_range.getStart () < 1) {
final int dummy_base_count = 1 - real_range.getStart ();
final char [] dummy_bases = new char [dummy_base_count];
for (int i = 0 ; i < dummy_base_count ; ++i) {
dummy_bases[i] = '@';
}
sub_sequence = new String (dummy_bases) + sub_sequence;
}
if (real_range.getEnd () > getLength ()) {
final int dummy_base_count = real_range.getEnd () - getLength ();
final char [] dummy_bases = new char [dummy_base_count];
for (int i = 0 ; i < dummy_base_count ; ++i) {
dummy_bases[i] = '@';
}
sub_sequence = sub_sequence + new String (dummy_bases);
}
if (FORWARD == direction) {
return sub_sequence;
} else {
return reverseComplement (sub_sequence);
}
}
/**
* This method truncates the sequence use the start and end of the argument.
* @param constraint This contains the start and end base of the new
* sequence.
* @return the Bases truncated into the new coordinate system.
**/
public Bases truncate (final Range constraint) {
final String bases_string = getSubSequence (constraint, FORWARD);
final Sequence new_sequence = new EmblStreamSequence (bases_string);
return new Bases (new_sequence);
}
/**
* Delete the bases in the given range and send out a SequenceChange event
* to all the listeners.
* @param range The inclusive range of bases to delete.
* @return A String containing the deleted bases.
* @exception ReadOnlyException If this Bases object cannot be changed.
**/
public String deleteRange (final Range range)
throws ReadOnlyException {
forward_stop_codon_cache = null;
reverse_stop_codon_cache = null;
final String removed_bases =
getSequence ().getSubSequence (range.getStart (), range.getEnd ());
final String new_sequence =
getSequence ().getSubSequence (1, range.getStart () - 1) +
getSequence ().getSubSequence (range.getEnd () + 1,
embl_sequence.length ());
try {
embl_sequence.setFromString (new_sequence);
} catch (IllegalSymbolException e) {
throw new Error ("internal error - unexpected exception: " + e);
}
final SequenceChangeEvent event =
new SequenceChangeEvent (this,
SequenceChangeEvent.DELETION,
range.getStart (),
removed_bases);
fireSequenceChangeEvent (event);
return removed_bases;
}
/**
* Insert the given bases at the given base position and send out a
* SequenceChange event to all the listeners.
* @param position The bases are inserted just before this base position if
* direction is FORWARD or just after if direction is REVERSE.
* @param direction If this is FORWARD, then the bases is the bases String
* will be inserted just before the base given by position. If this is
* REVERSE the bases will be reversed, complemented and inserted just
* after the position.
* @param bases The bases to add (or the reverse complement of the bases to
* add if direction is REVERSE).
* @exception ReadOnlyException If this Bases object cannot be changed.
**/
public void addBases (final int position, final int direction,
final String bases)
throws ReadOnlyException, IllegalSymbolException {
forward_stop_codon_cache = null;
reverse_stop_codon_cache = null;
final String new_sequence;
final int real_position;
final String real_bases;
if (direction == FORWARD) {
real_position = position;
real_bases = bases.toLowerCase ();
} else {
real_position = position + 1;
real_bases = reverseComplement (bases.toLowerCase ());
}
new_sequence =
getSequence ().getSubSequence (1, real_position - 1) +
real_bases +
getSequence ().getSubSequence (real_position, getLength ());
getSequence ().setFromString (new_sequence);
final SequenceChangeEvent event =
new SequenceChangeEvent (this,
SequenceChangeEvent.INSERTION,
real_position,
real_bases);
fireSequenceChangeEvent (event);
return;
}
/**
* There is one element in this array for each possible
* SequenceChangeListener priority. This array is changed by
* addSequenceChangeListener() and removeSequenceChangeListener().
**/
final private WeakHashMap listener_hash_map_array [] =
new WeakHashMap [MAX_PRIORITY - MIN_PRIORITY + 1];
/**
* Adds the specified event listener to the list of object that receive
* sequence change events from this object.
* @param l the event change listener.
* @param priority The listeners are stored in a priority queue using this
* value. Larger priority means that the listener will receive the event
* sooner (than lower priority listeners). Values less than MIN_PRIORITY
* will be treated like MIN_PRIORITY values higher than MAX_PRIORITY will
* be treated like MAX_PRIORITY.
**/
public void addSequenceChangeListener (final SequenceChangeListener l,
int priority) {
if (priority < MIN_PRIORITY) {
priority = MIN_PRIORITY;
}
if (priority > MAX_PRIORITY) {
priority = MAX_PRIORITY;
}
listener_hash_map_array [priority - MIN_PRIORITY].put (l, null);
}
/**
* Removes the specified event listener so that it no longer receives
* sequence change events from this object.
* @param l the event change listener.
**/
public void removeSequenceChangeListener (final SequenceChangeListener l) {
for (int i = 0 ; i < listener_hash_map_array.length ; ++i) {
final WeakHashMap this_hash_map = listener_hash_map_array [i];
if (this_hash_map.containsKey (l)) {
this_hash_map.remove (l);
return;
}
}
}
/**
* Send a SequenceChangeEvent to each object that is listening for it.
**/
private void fireSequenceChangeEvent (final SequenceChangeEvent event) {
for (int i = listener_hash_map_array.length - 1 ; i >= 0 ; --i) {
final WeakHashMap this_hash_map = listener_hash_map_array [i];
if (this_hash_map != null) {
final Iterator iter = this_hash_map.keySet ().iterator ();
while (iter.hasNext()) {
final SequenceChangeListener this_listener =
(SequenceChangeListener) iter.next();
this_listener.sequenceChanged (event);
}
}
}
}
/**
* Return the average gc percent for the sequence.
**/
public float getAverageGCPercent () {
return ((float)(getSequence ().getCCount () +
getSequence ().getGCount ())) /
getSequence ().length () * 100;
}
/**
* Return the average AG percent for the sequence as a percentage.
**/
public float getAverageAGPercent () {
return ((float)(getSequence ().getACount () +
getSequence ().getGCount ())) /
getSequence ().length () * 100;
}
/**
* Return the number of 'A's in this Bases object.
**/
public int getACount () {
return getSequence ().getACount ();
}
/**
* Return the number of 'T's in this Bases object.
**/
public int getTCount () {
return getSequence ().getTCount ();
}
/**
* Return the number of 'G's in this Bases object.
**/
public int getGCount () {
return getSequence ().getGCount ();
}
/**
* Return the number of 'C's in this Bases object.
**/
public int getCCount () {
return getSequence ().getCCount ();
}
/**
* Return a String containing the reverse complement of the argument
* String. For example an argument of "aatc" will result in "gatt".
**/
public static String reverseComplement (final String sequence_string) {
StringBuffer return_buffer = new StringBuffer (sequence_string.length ());
for (int i = sequence_string.length () - 1 ; i >= 0 ; --i) {
return_buffer.append (complement (sequence_string.charAt (i)));
}
return return_buffer.toString ();
}
/**
* Return a String containing the complement of the argument String. For
* example an argument of "aatc" will result in "ttag".
**/
public static String complement (final String sequence_string) {
StringBuffer return_buffer = new StringBuffer (sequence_string.length ());
for (int i = 0 ; i < sequence_string.length () ; ++i) {
return_buffer.append (complement (sequence_string.charAt (i)));
}
return return_buffer.toString ();
}
/**
* Returns the complement base of it's argument - c for g, a for t etc.
* The argument may be upper or lower case, but the result is always lower
* case. This also works for IUB base codes: the complement of 'y' is 'r'
* because 'y' is 'c' or 't' and 'r' is 'a' or 'g', the complement of 'n'
* or 'x' (any base) is 'n'.
**/
public static char complement (final char base) {
switch (base) {
case 'a': case 'A': return 't';
case 't': case 'T': case 'u': case 'U': return 'a';
case 'g': case 'G': return 'c';
case 'c': case 'C': return 'g';
case 'r': case 'R': return 'y';
case 'y': case 'Y': return 'r';
case 'k': case 'K': return 'm';
case 'm': case 'M': return 'k';
case 's': case 'S': return 's';
case 'w': case 'W': return 'w';
case 'b': case 'B': return 'v';
case 'd': case 'D': return 'h';
case 'h': case 'H': return 'd';