merge from 2_4_Release branch
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.datamodel;
20
21 import java.util.*;
22
23 import java.awt.*;
24
25 import jalview.analysis.*;
26 import jalview.schemes.*;
27
28 /**
29  * Collects a set contiguous ranges on a set of sequences
30  * 
31  * @author $author$
32  * @version $Revision$
33  */
34 public class SequenceGroup
35 {
36   String groupName;
37
38   String description;
39
40   Conservation conserve;
41
42   Vector aaFrequency;
43
44   boolean displayBoxes = true;
45
46   boolean displayText = true;
47
48   boolean colourText = false;
49
50   private Vector sequences = new Vector();
51
52   int width = -1;
53
54   /** DOCUMENT ME!! */
55   public ColourSchemeI cs;
56
57   int startRes = 0;
58
59   int endRes = 0;
60
61   public Color outlineColour = Color.black;
62
63   public Color idColour = null;
64
65   public int thresholdTextColour = 0;
66
67   public Color textColour = Color.black;
68
69   public Color textColour2 = Color.white;
70
71   /**
72    * Creates a new SequenceGroup object.
73    */
74   public SequenceGroup()
75   {
76     groupName = "JGroup:" + this.hashCode();
77   }
78
79   /**
80    * Creates a new SequenceGroup object.
81    * 
82    * @param sequences
83    * @param groupName
84    * @param scheme
85    * @param displayBoxes
86    * @param displayText
87    * @param colourText
88    * @param start
89    *                first column of group
90    * @param end
91    *                last column of group
92    */
93   public SequenceGroup(Vector sequences, String groupName,
94           ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
95           boolean colourText, int start, int end)
96   {
97     this.sequences = sequences;
98     this.groupName = groupName;
99     this.displayBoxes = displayBoxes;
100     this.displayText = displayText;
101     this.colourText = colourText;
102     this.cs = scheme;
103     startRes = start;
104     endRes = end;
105     recalcConservation();
106   }
107
108   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
109   {
110     int iSize = sequences.size();
111     SequenceI[] seqs = new SequenceI[iSize];
112     SequenceI[] inorder = getSequencesInOrder(align);
113
114     for (int i = 0, ipos = 0; i < inorder.length; i++)
115     {
116       SequenceI seq = inorder[i];
117
118       seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
119       if (seqs[ipos] != null)
120       {
121         seqs[ipos].setDescription(seq.getDescription());
122         seqs[ipos].setDBRef(seq.getDBRef());
123         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
124         if (seq.getDatasetSequence() != null)
125         {
126           seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
127         }
128
129         if (seq.getAnnotation() != null)
130         {
131           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
132           // Only copy annotation that is either a score or referenced by the
133           // alignment's annotation vector
134           for (int a = 0; a < seq.getAnnotation().length; a++)
135           {
136             AlignmentAnnotation tocopy = seq.getAnnotation()[a];
137             if (alann != null)
138             {
139               boolean found = false;
140               for (int pos = 0; pos < alann.length; pos++)
141               {
142                 if (alann[pos] == tocopy)
143                 {
144                   found = true;
145                   break;
146                 }
147               }
148               if (!found)
149                 continue;
150             }
151             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
152                     .getAnnotation()[a]);
153             newannot.restrict(startRes, endRes);
154             newannot.setSequenceRef(seqs[ipos]);
155             newannot.adjustForAlignment();
156             seqs[ipos].addAlignmentAnnotation(newannot);
157           }
158         }
159         ipos++;
160       }
161       else
162       {
163         iSize--;
164       }
165     }
166     if (iSize != inorder.length)
167     {
168       SequenceI[] nseqs = new SequenceI[iSize];
169       System.arraycopy(seqs, 0, nseqs, 0, iSize);
170       seqs = nseqs;
171     }
172     return seqs;
173
174   }
175
176   /**
177    * If sequence ends in gaps, the end residue can be correctly calculated here
178    * 
179    * @param seq
180    *                SequenceI
181    * @return int
182    */
183   public int findEndRes(SequenceI seq)
184   {
185     int eres = 0;
186     char ch;
187
188     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
189     {
190       ch = seq.getCharAt(j);
191       if (!jalview.util.Comparison.isGap((ch)))
192       {
193         eres++;
194       }
195     }
196
197     if (eres > 0)
198     {
199       eres += seq.getStart() - 1;
200     }
201
202     return eres;
203   }
204
205   public Vector getSequences(Hashtable hiddenReps)
206   {
207     if (hiddenReps == null)
208     {
209       return sequences;
210     }
211     else
212     {
213       Vector allSequences = new Vector();
214       SequenceI seq, seq2;
215       for (int i = 0; i < sequences.size(); i++)
216       {
217         seq = (SequenceI) sequences.elementAt(i);
218         allSequences.addElement(seq);
219         if (hiddenReps.containsKey(seq))
220         {
221           SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
222           for (int h = 0; h < hsg.getSize(); h++)
223           {
224             seq2 = hsg.getSequenceAt(h);
225             if (seq2 != seq && !allSequences.contains(seq2))
226             {
227               allSequences.addElement(seq2);
228             }
229           }
230         }
231       }
232
233       return allSequences;
234     }
235   }
236
237   public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
238   {
239     Vector tmp = getSequences(hiddenReps);
240     if (tmp == null)
241     {
242       return null;
243     }
244     SequenceI[] result = new SequenceI[tmp.size()];
245     for (int i = 0; i < result.length; i++)
246     {
247       result[i] = (SequenceI) tmp.elementAt(i);
248     }
249
250     return result;
251   }
252
253   /**
254    * DOCUMENT ME!
255    * 
256    * @param col
257    *                DOCUMENT ME!
258    * 
259    * @return DOCUMENT ME!
260    */
261   public boolean adjustForRemoveLeft(int col)
262   {
263     // return value is true if the group still exists
264     if (startRes >= col)
265     {
266       startRes = startRes - col;
267     }
268
269     if (endRes >= col)
270     {
271       endRes = endRes - col;
272
273       if (startRes > endRes)
274       {
275         startRes = 0;
276       }
277     }
278     else
279     {
280       // must delete this group!!
281       return false;
282     }
283
284     return true;
285   }
286
287   /**
288    * DOCUMENT ME!
289    * 
290    * @param col
291    *                DOCUMENT ME!
292    * 
293    * @return DOCUMENT ME!
294    */
295   public boolean adjustForRemoveRight(int col)
296   {
297     if (startRes > col)
298     {
299       // delete this group
300       return false;
301     }
302
303     if (endRes >= col)
304     {
305       endRes = col;
306     }
307
308     return true;
309   }
310
311   /**
312    * DOCUMENT ME!
313    * 
314    * @return DOCUMENT ME!
315    */
316   public String getName()
317   {
318     return groupName;
319   }
320
321   public String getDescription()
322   {
323     return description;
324   }
325
326   /**
327    * DOCUMENT ME!
328    * 
329    * @param name
330    *                DOCUMENT ME!
331    */
332   public void setName(String name)
333   {
334     groupName = name;
335   }
336
337   public void setDescription(String desc)
338   {
339     description = desc;
340   }
341
342   /**
343    * DOCUMENT ME!
344    * 
345    * @return DOCUMENT ME!
346    */
347   public Conservation getConservation()
348   {
349     return conserve;
350   }
351
352   /**
353    * DOCUMENT ME!
354    * 
355    * @param c
356    *                DOCUMENT ME!
357    */
358   public void setConservation(Conservation c)
359   {
360     conserve = c;
361   }
362
363   /**
364    * Add s to this sequence group
365    * 
366    * @param s
367    *                alignment sequence to be added
368    * @param recalc
369    *                true means Group's conservation should be recalculated
370    */
371   public void addSequence(SequenceI s, boolean recalc)
372   {
373     if (s != null && !sequences.contains(s))
374     {
375       sequences.addElement(s);
376     }
377
378     if (recalc)
379     {
380       recalcConservation();
381     }
382   }
383
384   /**
385    * calculate residue conservation for group
386    */
387   public void recalcConservation()
388   {
389     if (cs == null)
390     {
391       return;
392     }
393
394     try
395     {
396       cs.setConsensus(AAFrequency
397               .calculate(sequences, startRes, endRes + 1));
398
399       if (cs instanceof ClustalxColourScheme)
400       {
401         ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
402       }
403
404       if (cs.conservationApplied())
405       {
406         Conservation c = new Conservation(groupName,
407                 ResidueProperties.propHash, 3, sequences, startRes,
408                 endRes + 1);
409         c.calculate();
410         c.verdict(false, 25);
411
412         cs.setConservation(c);
413
414         if (cs instanceof ClustalxColourScheme)
415         {
416           ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
417         }
418       }
419     } catch (java.lang.OutOfMemoryError err)
420     {
421       // TODO: catch OOM
422       System.out.println("Out of memory loading groups: " + err);
423     }
424
425   }
426
427   /**
428    * DOCUMENT ME!
429    * 
430    * @param s
431    *                DOCUMENT ME!
432    * @param recalc
433    *                DOCUMENT ME!
434    */
435   public void addOrRemove(SequenceI s, boolean recalc)
436   {
437     if (sequences.contains(s))
438     {
439       deleteSequence(s, recalc);
440     }
441     else
442     {
443       addSequence(s, recalc);
444     }
445   }
446
447   /**
448    * DOCUMENT ME!
449    * 
450    * @param s
451    *                DOCUMENT ME!
452    * @param recalc
453    *                DOCUMENT ME!
454    */
455   public void deleteSequence(SequenceI s, boolean recalc)
456   {
457     sequences.removeElement(s);
458
459     if (recalc)
460     {
461       recalcConservation();
462     }
463   }
464
465   /**
466    * DOCUMENT ME!
467    * 
468    * @return DOCUMENT ME!
469    */
470   public int getStartRes()
471   {
472     return startRes;
473   }
474
475   /**
476    * DOCUMENT ME!
477    * 
478    * @return DOCUMENT ME!
479    */
480   public int getEndRes()
481   {
482     return endRes;
483   }
484
485   /**
486    * DOCUMENT ME!
487    * 
488    * @param i
489    *                DOCUMENT ME!
490    */
491   public void setStartRes(int i)
492   {
493     startRes = i;
494   }
495
496   /**
497    * DOCUMENT ME!
498    * 
499    * @param i
500    *                DOCUMENT ME!
501    */
502   public void setEndRes(int i)
503   {
504     endRes = i;
505   }
506
507   /**
508    * DOCUMENT ME!
509    * 
510    * @return DOCUMENT ME!
511    */
512   public int getSize()
513   {
514     return sequences.size();
515   }
516
517   /**
518    * DOCUMENT ME!
519    * 
520    * @param i
521    *                DOCUMENT ME!
522    * 
523    * @return DOCUMENT ME!
524    */
525   public SequenceI getSequenceAt(int i)
526   {
527     return (SequenceI) sequences.elementAt(i);
528   }
529
530   /**
531    * DOCUMENT ME!
532    * 
533    * @param state
534    *                DOCUMENT ME!
535    */
536   public void setColourText(boolean state)
537   {
538     colourText = state;
539   }
540
541   /**
542    * DOCUMENT ME!
543    * 
544    * @return DOCUMENT ME!
545    */
546   public boolean getColourText()
547   {
548     return colourText;
549   }
550
551   /**
552    * DOCUMENT ME!
553    * 
554    * @param state
555    *                DOCUMENT ME!
556    */
557   public void setDisplayText(boolean state)
558   {
559     displayText = state;
560   }
561
562   /**
563    * DOCUMENT ME!
564    * 
565    * @return DOCUMENT ME!
566    */
567   public boolean getDisplayText()
568   {
569     return displayText;
570   }
571
572   /**
573    * DOCUMENT ME!
574    * 
575    * @param state
576    *                DOCUMENT ME!
577    */
578   public void setDisplayBoxes(boolean state)
579   {
580     displayBoxes = state;
581   }
582
583   /**
584    * DOCUMENT ME!
585    * 
586    * @return DOCUMENT ME!
587    */
588   public boolean getDisplayBoxes()
589   {
590     return displayBoxes;
591   }
592
593   /**
594    * DOCUMENT ME!
595    * 
596    * @return DOCUMENT ME!
597    */
598   public int getWidth()
599   {
600     // MC This needs to get reset when characters are inserted and deleted
601     if (sequences.size() > 0)
602     {
603       width = ((SequenceI) sequences.elementAt(0)).getLength();
604     }
605
606     for (int i = 1; i < sequences.size(); i++)
607     {
608       SequenceI seq = (SequenceI) sequences.elementAt(i);
609
610       if (seq.getLength() > width)
611       {
612         width = seq.getLength();
613       }
614     }
615
616     return width;
617   }
618
619   /**
620    * DOCUMENT ME!
621    * 
622    * @param c
623    *                DOCUMENT ME!
624    */
625   public void setOutlineColour(Color c)
626   {
627     outlineColour = c;
628   }
629
630   /**
631    * DOCUMENT ME!
632    * 
633    * @return DOCUMENT ME!
634    */
635   public Color getOutlineColour()
636   {
637     return outlineColour;
638   }
639
640   /**
641    * 
642    * returns the sequences in the group ordered by the ordering given by al
643    * 
644    * @param al
645    *                Alignment
646    * @return SequenceI[]
647    */
648   public SequenceI[] getSequencesInOrder(AlignmentI al)
649   {
650     int sSize = sequences.size();
651     int alHeight = al.getHeight();
652
653     SequenceI[] seqs = new SequenceI[sSize];
654
655     int index = 0;
656     for (int i = 0; i < alHeight && index < sSize; i++)
657     {
658       if (sequences.contains(al.getSequenceAt(i)))
659       {
660         seqs[index++] = al.getSequenceAt(i);
661       }
662     }
663
664     return seqs;
665   }
666
667   /**
668    * @return the idColour
669    */
670   public Color getIdColour()
671   {
672     return idColour;
673   }
674
675   /**
676    * @param idColour
677    *                the idColour to set
678    */
679   public void setIdColour(Color idColour)
680   {
681     this.idColour = idColour;
682   }
683 }