33739aabb69a88d2f776aafed7426edc4912cfc5
[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    * group members
51    */
52   private Vector sequences = new Vector();
53   /**
54    * representative sequence for this group (if any)
55    */
56   private SequenceI seqrep = null;
57   int width = -1;
58
59   /**
60    * Colourscheme applied to group if any */
61   public ColourSchemeI cs;
62
63   int startRes = 0;
64
65   int endRes = 0;
66
67   public Color outlineColour = Color.black;
68
69   public Color idColour = null;
70
71   public int thresholdTextColour = 0;
72
73   public Color textColour = Color.black;
74
75   public Color textColour2 = Color.white;
76
77   /**
78    * Creates a new SequenceGroup object.
79    */
80   public SequenceGroup()
81   {
82     groupName = "JGroup:" + this.hashCode();
83   }
84
85   /**
86    * Creates a new SequenceGroup object.
87    * 
88    * @param sequences
89    * @param groupName
90    * @param scheme
91    * @param displayBoxes
92    * @param displayText
93    * @param colourText
94    * @param start
95    *                first column of group
96    * @param end
97    *                last column of group
98    */
99   public SequenceGroup(Vector sequences, String groupName,
100           ColourSchemeI scheme, boolean displayBoxes, boolean displayText,
101           boolean colourText, int start, int end)
102   {
103     this.sequences = sequences;
104     this.groupName = groupName;
105     this.displayBoxes = displayBoxes;
106     this.displayText = displayText;
107     this.colourText = colourText;
108     this.cs = scheme;
109     startRes = start;
110     endRes = end;
111     recalcConservation();
112   }
113   /**
114    * copy constructor
115    * @param seqsel
116    */
117   public SequenceGroup(SequenceGroup seqsel)
118   {
119     if (seqsel!=null)
120     {
121       sequences=new Vector(seqsel.sequences);
122       groupName = new String(seqsel.groupName);
123       displayBoxes = seqsel.displayBoxes;
124       displayText = seqsel.displayText;
125       colourText = seqsel.colourText;
126       startRes = seqsel.startRes;
127       endRes = seqsel.endRes;
128       cs =seqsel.cs;
129       if (seqsel.description!=null)
130         description = new String(seqsel.description);
131       groupName = new String(seqsel.groupName);
132       hidecols = seqsel.hidecols;
133       hidereps = seqsel.hidereps;
134       idColour = seqsel.idColour;
135       outlineColour = seqsel.outlineColour;
136       seqrep = seqsel.seqrep;
137       textColour = seqsel.textColour;
138       textColour2 = seqsel.textColour2;
139       thresholdTextColour = seqsel.thresholdTextColour;
140       width = seqsel.width;
141       if (seqsel.conserve!=null)
142       {
143         recalcConservation(); // safer than 
144         // aaFrequency = (Vector) seqsel.aaFrequency.clone(); // ??
145       }
146     }
147   }
148
149   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
150   {
151     int iSize = sequences.size();
152     SequenceI[] seqs = new SequenceI[iSize];
153     SequenceI[] inorder = getSequencesInOrder(align);
154
155     for (int i = 0, ipos = 0; i < inorder.length; i++)
156     {
157       SequenceI seq = inorder[i];
158
159       seqs[ipos] = seq.getSubSequence(startRes, endRes + 1);
160       if (seqs[ipos] != null)
161       {
162         seqs[ipos].setDescription(seq.getDescription());
163         seqs[ipos].setDBRef(seq.getDBRef());
164         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
165         if (seq.getDatasetSequence() != null)
166         {
167           seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
168         }
169
170         if (seq.getAnnotation() != null)
171         {
172           AlignmentAnnotation[] alann = align.getAlignmentAnnotation();
173           // Only copy annotation that is either a score or referenced by the
174           // alignment's annotation vector
175           for (int a = 0; a < seq.getAnnotation().length; a++)
176           {
177             AlignmentAnnotation tocopy = seq.getAnnotation()[a];
178             if (alann != null)
179             {
180               boolean found = false;
181               for (int pos = 0; pos < alann.length; pos++)
182               {
183                 if (alann[pos] == tocopy)
184                 {
185                   found = true;
186                   break;
187                 }
188               }
189               if (!found)
190                 continue;
191             }
192             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
193                     .getAnnotation()[a]);
194             newannot.restrict(startRes, endRes);
195             newannot.setSequenceRef(seqs[ipos]);
196             newannot.adjustForAlignment();
197             seqs[ipos].addAlignmentAnnotation(newannot);
198           }
199         }
200         ipos++;
201       }
202       else
203       {
204         iSize--;
205       }
206     }
207     if (iSize != inorder.length)
208     {
209       SequenceI[] nseqs = new SequenceI[iSize];
210       System.arraycopy(seqs, 0, nseqs, 0, iSize);
211       seqs = nseqs;
212     }
213     return seqs;
214
215   }
216
217   /**
218    * If sequence ends in gaps, the end residue can be correctly calculated here
219    * 
220    * @param seq
221    *                SequenceI
222    * @return int
223    */
224   public int findEndRes(SequenceI seq)
225   {
226     int eres = 0;
227     char ch;
228
229     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
230     {
231       ch = seq.getCharAt(j);
232       if (!jalview.util.Comparison.isGap((ch)))
233       {
234         eres++;
235       }
236     }
237
238     if (eres > 0)
239     {
240       eres += seq.getStart() - 1;
241     }
242
243     return eres;
244   }
245
246   public Vector getSequences(Hashtable hiddenReps)
247   {
248     if (hiddenReps == null)
249     {
250       return sequences;
251     }
252     else
253     {
254       Vector allSequences = new Vector();
255       SequenceI seq, seq2;
256       for (int i = 0; i < sequences.size(); i++)
257       {
258         seq = (SequenceI) sequences.elementAt(i);
259         allSequences.addElement(seq);
260         if (hiddenReps.containsKey(seq))
261         {
262           SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
263           for (int h = 0; h < hsg.getSize(); h++)
264           {
265             seq2 = hsg.getSequenceAt(h);
266             if (seq2 != seq && !allSequences.contains(seq2))
267             {
268               allSequences.addElement(seq2);
269             }
270           }
271         }
272       }
273
274       return allSequences;
275     }
276   }
277
278   public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
279   {
280     Vector tmp = getSequences(hiddenReps);
281     if (tmp == null)
282     {
283       return null;
284     }
285     SequenceI[] result = new SequenceI[tmp.size()];
286     for (int i = 0; i < result.length; i++)
287     {
288       result[i] = (SequenceI) tmp.elementAt(i);
289     }
290
291     return result;
292   }
293
294   /**
295    * DOCUMENT ME!
296    * 
297    * @param col
298    *                DOCUMENT ME!
299    * 
300    * @return DOCUMENT ME!
301    */
302   public boolean adjustForRemoveLeft(int col)
303   {
304     // return value is true if the group still exists
305     if (startRes >= col)
306     {
307       startRes = startRes - col;
308     }
309
310     if (endRes >= col)
311     {
312       endRes = endRes - col;
313
314       if (startRes > endRes)
315       {
316         startRes = 0;
317       }
318     }
319     else
320     {
321       // must delete this group!!
322       return false;
323     }
324
325     return true;
326   }
327
328   /**
329    * DOCUMENT ME!
330    * 
331    * @param col
332    *                DOCUMENT ME!
333    * 
334    * @return DOCUMENT ME!
335    */
336   public boolean adjustForRemoveRight(int col)
337   {
338     if (startRes > col)
339     {
340       // delete this group
341       return false;
342     }
343
344     if (endRes >= col)
345     {
346       endRes = col;
347     }
348
349     return true;
350   }
351
352   /**
353    * DOCUMENT ME!
354    * 
355    * @return DOCUMENT ME!
356    */
357   public String getName()
358   {
359     return groupName;
360   }
361
362   public String getDescription()
363   {
364     return description;
365   }
366
367   /**
368    * DOCUMENT ME!
369    * 
370    * @param name
371    *                DOCUMENT ME!
372    */
373   public void setName(String name)
374   {
375     groupName = name;
376   }
377
378   public void setDescription(String desc)
379   {
380     description = desc;
381   }
382
383   /**
384    * DOCUMENT ME!
385    * 
386    * @return DOCUMENT ME!
387    */
388   public Conservation getConservation()
389   {
390     return conserve;
391   }
392
393   /**
394    * DOCUMENT ME!
395    * 
396    * @param c
397    *                DOCUMENT ME!
398    */
399   public void setConservation(Conservation c)
400   {
401     conserve = c;
402   }
403
404   /**
405    * Add s to this sequence group
406    * 
407    * @param s
408    *                alignment sequence to be added
409    * @param recalc
410    *                true means Group's conservation should be recalculated
411    */
412   public void addSequence(SequenceI s, boolean recalc)
413   {
414     if (s != null && !sequences.contains(s))
415     {
416       sequences.addElement(s);
417     }
418
419     if (recalc)
420     {
421       recalcConservation();
422     }
423   }
424
425   /**
426    * calculate residue conservation for group
427    */
428   public void recalcConservation()
429   {
430     if (cs == null)
431     {
432       return;
433     }
434
435     try
436     {
437       cs.setConsensus(AAFrequency
438               .calculate(sequences, startRes, endRes + 1));
439
440       if (cs instanceof ClustalxColourScheme)
441       {
442         ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
443       }
444
445       if (cs.conservationApplied())
446       {
447         Conservation c = new Conservation(groupName,
448                 ResidueProperties.propHash, 3, sequences, startRes,
449                 endRes + 1);
450         c.calculate();
451         c.verdict(false, 25);
452
453         cs.setConservation(c);
454
455         if (cs instanceof ClustalxColourScheme)
456         {
457           ((ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
458         }
459       }
460     } catch (java.lang.OutOfMemoryError err)
461     {
462       // TODO: catch OOM
463       System.out.println("Out of memory loading groups: " + err);
464     }
465
466   }
467
468   /**
469    * DOCUMENT ME!
470    * 
471    * @param s
472    *                DOCUMENT ME!
473    * @param recalc
474    *                DOCUMENT ME!
475    */
476   public void addOrRemove(SequenceI s, boolean recalc)
477   {
478     if (sequences.contains(s))
479     {
480       deleteSequence(s, recalc);
481     }
482     else
483     {
484       addSequence(s, recalc);
485     }
486   }
487
488   /**
489    * DOCUMENT ME!
490    * 
491    * @param s
492    *                DOCUMENT ME!
493    * @param recalc
494    *                DOCUMENT ME!
495    */
496   public void deleteSequence(SequenceI s, boolean recalc)
497   {
498     sequences.removeElement(s);
499
500     if (recalc)
501     {
502       recalcConservation();
503     }
504   }
505
506   /**
507    * DOCUMENT ME!
508    * 
509    * @return DOCUMENT ME!
510    */
511   public int getStartRes()
512   {
513     return startRes;
514   }
515
516   /**
517    * DOCUMENT ME!
518    * 
519    * @return DOCUMENT ME!
520    */
521   public int getEndRes()
522   {
523     return endRes;
524   }
525
526   /**
527    * DOCUMENT ME!
528    * 
529    * @param i
530    *                DOCUMENT ME!
531    */
532   public void setStartRes(int i)
533   {
534     startRes = i;
535   }
536
537   /**
538    * DOCUMENT ME!
539    * 
540    * @param i
541    *                DOCUMENT ME!
542    */
543   public void setEndRes(int i)
544   {
545     endRes = i;
546   }
547
548   /**
549    * DOCUMENT ME!
550    * 
551    * @return DOCUMENT ME!
552    */
553   public int getSize()
554   {
555     return sequences.size();
556   }
557
558   /**
559    * DOCUMENT ME!
560    * 
561    * @param i
562    *                DOCUMENT ME!
563    * 
564    * @return DOCUMENT ME!
565    */
566   public SequenceI getSequenceAt(int i)
567   {
568     return (SequenceI) sequences.elementAt(i);
569   }
570
571   /**
572    * DOCUMENT ME!
573    * 
574    * @param state
575    *                DOCUMENT ME!
576    */
577   public void setColourText(boolean state)
578   {
579     colourText = state;
580   }
581
582   /**
583    * DOCUMENT ME!
584    * 
585    * @return DOCUMENT ME!
586    */
587   public boolean getColourText()
588   {
589     return colourText;
590   }
591
592   /**
593    * DOCUMENT ME!
594    * 
595    * @param state
596    *                DOCUMENT ME!
597    */
598   public void setDisplayText(boolean state)
599   {
600     displayText = state;
601   }
602
603   /**
604    * DOCUMENT ME!
605    * 
606    * @return DOCUMENT ME!
607    */
608   public boolean getDisplayText()
609   {
610     return displayText;
611   }
612
613   /**
614    * DOCUMENT ME!
615    * 
616    * @param state
617    *                DOCUMENT ME!
618    */
619   public void setDisplayBoxes(boolean state)
620   {
621     displayBoxes = state;
622   }
623
624   /**
625    * DOCUMENT ME!
626    * 
627    * @return DOCUMENT ME!
628    */
629   public boolean getDisplayBoxes()
630   {
631     return displayBoxes;
632   }
633
634   /**
635    * DOCUMENT ME!
636    * 
637    * @return DOCUMENT ME!
638    */
639   public int getWidth()
640   {
641     // MC This needs to get reset when characters are inserted and deleted
642     if (sequences.size() > 0)
643     {
644       width = ((SequenceI) sequences.elementAt(0)).getLength();
645     }
646
647     for (int i = 1; i < sequences.size(); i++)
648     {
649       SequenceI seq = (SequenceI) sequences.elementAt(i);
650
651       if (seq.getLength() > width)
652       {
653         width = seq.getLength();
654       }
655     }
656
657     return width;
658   }
659
660   /**
661    * DOCUMENT ME!
662    * 
663    * @param c
664    *                DOCUMENT ME!
665    */
666   public void setOutlineColour(Color c)
667   {
668     outlineColour = c;
669   }
670
671   /**
672    * DOCUMENT ME!
673    * 
674    * @return DOCUMENT ME!
675    */
676   public Color getOutlineColour()
677   {
678     return outlineColour;
679   }
680
681   /**
682    * 
683    * returns the sequences in the group ordered by the ordering given by al
684    * 
685    * @param al
686    *                Alignment
687    * @return SequenceI[]
688    */
689   public SequenceI[] getSequencesInOrder(AlignmentI al)
690   {
691     int sSize = sequences.size();
692     int alHeight = al.getHeight();
693
694     SequenceI[] seqs = new SequenceI[sSize];
695
696     int index = 0;
697     for (int i = 0; i < alHeight && index < sSize; i++)
698     {
699       if (sequences.contains(al.getSequenceAt(i)))
700       {
701         seqs[index++] = al.getSequenceAt(i);
702       }
703     }
704
705     return seqs;
706   }
707
708   /**
709    * @return the idColour
710    */
711   public Color getIdColour()
712   {
713     return idColour;
714   }
715
716   /**
717    * @param idColour
718    *                the idColour to set
719    */
720   public void setIdColour(Color idColour)
721   {
722     this.idColour = idColour;
723   }
724
725   /**
726    * @return the representative sequence for this group
727    */
728   public SequenceI getSeqrep()
729   {
730     return seqrep;
731   }
732
733   /**
734    * set the representative sequence for this group.
735    * Note - this affects the interpretation of the Hidereps attribute.
736    * @param seqrep the seqrep to set (null means no sequence representative)
737    */
738   public void setSeqrep(SequenceI seqrep)
739   {
740     this.seqrep = seqrep;
741   }
742   /**
743    * 
744    * @return true if group has a sequence representative
745    */
746   public boolean hasSeqrep()
747   {
748     return seqrep != null;
749   }
750   /**
751    * visibility of rows or represented rows covered by group
752    */
753   private boolean hidereps=false;
754   /**
755    * set visibility of sequences covered by (if no sequence representative is defined) 
756    * or represented by this group.
757    * @param visibility
758    */
759   public void setHidereps(boolean visibility)
760   {
761     hidereps = visibility;
762   }
763   /**
764    * 
765    * @return true if sequences represented (or covered) by this group should be hidden
766    */
767   public boolean isHidereps()
768   {
769     return hidereps;
770   }
771   /**
772    * visibility of columns intersecting this group
773    */
774   private boolean hidecols=false;
775   /**
776    * set intended visibility of columns covered by this group
777    * @param visibility
778    */
779   public void setHideCols(boolean visibility)
780   {
781     hidecols = visibility;
782   }
783   /**
784    * 
785    * @return true if columns covered by group should be hidden
786    */
787   public boolean isHideCols()
788   {
789     return hidecols;
790   }
791   /**
792    * create a new sequence group from the intersection of this group
793    * with an alignment Hashtable of hidden representatives
794    * 
795    * @param alignment (may not be null)
796    * @param hashtable (may be null)
797    * @return new group containing sequences common to this group and alignment
798    */
799   public SequenceGroup intersect(AlignmentI alignment, Hashtable hashtable)
800   {
801     SequenceGroup sgroup = new SequenceGroup(this);
802     Enumeration en = getSequences(hashtable).elements();
803     while (en.hasMoreElements())
804     {
805       SequenceI elem = (SequenceI) en.nextElement();
806       if (alignment.getSequences().contains(elem))
807       {
808         sgroup.addSequence(elem, false);
809       }
810     }
811     return sgroup;
812   }
813 }