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