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