only return non-empty subsequences when getting selection as new sequences
[jalview.git] / src / jalview / datamodel / SequenceGroup.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 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  * DOCUMENT ME!
30  *
31  * @author $author$
32  * @version $Revision$
33  */
34 public class SequenceGroup
35 {
36   String groupName;
37   String description;
38   Conservation conserve;
39   Vector aaFrequency;
40   boolean displayBoxes = true;
41   boolean displayText = true;
42   boolean colourText = false;
43   private Vector sequences = new Vector();
44   int width = -1;
45
46   /** DOCUMENT ME!! */
47   public ColourSchemeI cs;
48   int startRes = 0;
49   int endRes = 0;
50   Color outlineColour = Color.black;
51   public int thresholdTextColour = 0;
52   public Color textColour = Color.black;
53   public Color textColour2 = Color.white;
54
55   /**
56    * Creates a new SequenceGroup object.
57    */
58   public SequenceGroup()
59   {
60     groupName = "JGroup:" + this.hashCode();
61   }
62
63   /**
64    * Creates a new SequenceGroup object.
65    *
66    * @param sequences DOCUMENT ME!
67    * @param groupName DOCUMENT ME!
68    * @param scheme DOCUMENT ME!
69    * @param displayBoxes DOCUMENT ME!
70    * @param displayText DOCUMENT ME!
71    * @param colourText DOCUMENT ME!
72    * @param start DOCUMENT ME!
73    * @param end DOCUMENT ME!
74    */
75   public SequenceGroup(Vector sequences, String groupName,
76                        ColourSchemeI scheme, boolean displayBoxes,
77                        boolean displayText,
78                        boolean colourText, int start, int end)
79   {
80     this.sequences = sequences;
81     this.groupName = groupName;
82     this.displayBoxes = displayBoxes;
83     this.displayText = displayText;
84     this.colourText = colourText;
85     this.cs = scheme;
86     startRes = start;
87     endRes = end;
88     recalcConservation();
89   }
90
91   public SequenceI[] getSelectionAsNewSequences(AlignmentI align)
92   {
93     int iSize = sequences.size();
94     SequenceI[] seqs = new SequenceI[iSize];
95     SequenceI[] inorder = getSequencesInOrder(align);
96     
97     for (int i = 0,ipos=0; i < inorder.length; i++)
98     {
99       SequenceI seq = inorder[i];
100
101       seqs[ipos] = seq.getSubSequence(startRes, endRes+1);
102       if (seqs[ipos]!=null)
103       {
104         seqs[ipos].setDescription(seq.getDescription());
105         seqs[ipos].setDBRef(seq.getDBRef());
106         seqs[ipos].setSequenceFeatures(seq.getSequenceFeatures());
107         if (seq.getDatasetSequence() != null)
108         {
109           seqs[ipos].setDatasetSequence(seq.getDatasetSequence());
110         }
111
112         if (seq.getAnnotation() != null)
113         {
114           for (int a = 0; a < seq.getAnnotation().length; a++)
115           {
116             AlignmentAnnotation newannot = new AlignmentAnnotation(seq
117                     .getAnnotation()[a]);
118             newannot.restrict(startRes, endRes);
119             newannot.setSequenceRef(seqs[ipos]);
120             newannot.adjustForAlignment();
121             seqs[ipos].addAlignmentAnnotation(newannot);
122           }
123         }
124         ipos++;
125       } else {
126         iSize--;
127       }
128     }
129     if (iSize!=inorder.length)
130     {
131       SequenceI[] nseqs = new SequenceI[iSize];
132       System.arraycopy(seqs, 0, nseqs, 0, iSize);
133       seqs = nseqs;
134     }
135     return seqs;
136
137   }
138
139   /**
140    * If sequence ends in gaps, the end residue can
141    * be correctly calculated here
142    * @param seq SequenceI
143    * @return int
144    */
145   public int findEndRes(SequenceI seq)
146   {
147     int eres = 0;
148     char ch;
149
150     for (int j = 0; j < endRes + 1 && j < seq.getLength(); j++)
151     {
152       ch = seq.getCharAt(j);
153       if (!jalview.util.Comparison.isGap( (ch)))
154       {
155         eres++;
156       }
157     }
158
159     if (eres > 0)
160     {
161       eres += seq.getStart() - 1;
162     }
163
164     return eres;
165   }
166
167   public Vector getSequences(Hashtable hiddenReps)
168   {
169     if (hiddenReps == null)
170     {
171       return sequences;
172     }
173     else
174     {
175       Vector allSequences = new Vector();
176       SequenceI seq, seq2;
177       for (int i = 0; i < sequences.size(); i++)
178       {
179         seq = (SequenceI) sequences.elementAt(i);
180         allSequences.addElement(seq);
181         if (hiddenReps.containsKey(seq))
182         {
183           SequenceGroup hsg = (SequenceGroup) hiddenReps.get(seq);
184           for (int h = 0; h < hsg.getSize(); h++)
185           {
186             seq2 = hsg.getSequenceAt(h);
187             if (seq2 != seq
188                 && !allSequences.contains(seq2))
189             {
190               allSequences.addElement(seq2);
191             }
192           }
193         }
194       }
195
196       return allSequences;
197     }
198   }
199
200   public SequenceI[] getSequencesAsArray(Hashtable hiddenReps)
201   {
202     Vector tmp = getSequences(hiddenReps);
203     if (tmp == null)
204     {
205       return null;
206     }
207     SequenceI[] result = new SequenceI[tmp.size()];
208     for (int i = 0; i < result.length; i++)
209     {
210       result[i] = (SequenceI) tmp.elementAt(i);
211     }
212
213     return result;
214   }
215
216   /**
217    * DOCUMENT ME!
218    *
219    * @param col DOCUMENT ME!
220    *
221    * @return DOCUMENT ME!
222    */
223   public boolean adjustForRemoveLeft(int col)
224   {
225     // return value is true if the group still exists
226     if (startRes >= col)
227     {
228       startRes = startRes - col;
229     }
230
231     if (endRes >= col)
232     {
233       endRes = endRes - col;
234
235       if (startRes > endRes)
236       {
237         startRes = 0;
238       }
239     }
240     else
241     {
242       // must delete this group!!
243       return false;
244     }
245
246     return true;
247   }
248
249   /**
250    * DOCUMENT ME!
251    *
252    * @param col DOCUMENT ME!
253    *
254    * @return DOCUMENT ME!
255    */
256   public boolean adjustForRemoveRight(int col)
257   {
258     if (startRes > col)
259     {
260       // delete this group
261       return false;
262     }
263
264     if (endRes >= col)
265     {
266       endRes = col;
267     }
268
269     return true;
270   }
271
272   /**
273    * DOCUMENT ME!
274    *
275    * @return DOCUMENT ME!
276    */
277   public String getName()
278   {
279     return groupName;
280   }
281
282   public String getDescription()
283   {
284     return description;
285   }
286
287   /**
288    * DOCUMENT ME!
289    *
290    * @param name DOCUMENT ME!
291    */
292   public void setName(String name)
293   {
294     groupName = name;
295   }
296
297   public void setDescription(String desc)
298   {
299     description = desc;
300   }
301
302   /**
303    * DOCUMENT ME!
304    *
305    * @return DOCUMENT ME!
306    */
307   public Conservation getConservation()
308   {
309     return conserve;
310   }
311
312   /**
313    * DOCUMENT ME!
314    *
315    * @param c DOCUMENT ME!
316    */
317   public void setConservation(Conservation c)
318   {
319     conserve = c;
320   }
321
322   /**
323    * DOCUMENT ME!
324    *
325    * @param s DOCUMENT ME!
326    * @param recalc DOCUMENT ME!
327    */
328   public void addSequence(SequenceI s, boolean recalc)
329   {
330     if (s != null && !sequences.contains(s))
331     {
332       sequences.addElement(s);
333     }
334
335     if (recalc)
336     {
337       recalcConservation();
338     }
339   }
340
341   /**
342    * DOCUMENT ME!
343    */
344   public void recalcConservation()
345   {
346     if (cs == null)
347     {
348       return;
349     }
350
351     try
352     {
353       cs.setConsensus(AAFrequency.calculate(sequences, startRes, endRes + 1));
354
355       if (cs instanceof ClustalxColourScheme)
356       {
357         ( (ClustalxColourScheme) cs).resetClustalX(sequences, getWidth());
358       }
359
360       if (cs.conservationApplied())
361       {
362         Conservation c = new Conservation(groupName,
363                                           ResidueProperties.propHash, 3,
364                                           sequences,
365                                           startRes, endRes + 1);
366         c.calculate();
367         c.verdict(false, 25);
368
369         cs.setConservation(c);
370
371         if (cs instanceof ClustalxColourScheme)
372         {
373           ( (ClustalxColourScheme) cs).resetClustalX(sequences,
374               getWidth());
375         }
376       }
377     }
378     catch (java.lang.OutOfMemoryError err)
379     {
380       System.out.println("Out of memory loading groups: " + err);
381     }
382
383   }
384
385   /**
386    * DOCUMENT ME!
387    *
388    * @param s DOCUMENT ME!
389    * @param recalc DOCUMENT ME!
390    */
391   public void addOrRemove(SequenceI s, boolean recalc)
392   {
393     if (sequences.contains(s))
394     {
395       deleteSequence(s, recalc);
396     }
397     else
398     {
399       addSequence(s, recalc);
400     }
401   }
402
403   /**
404    * DOCUMENT ME!
405    *
406    * @param s DOCUMENT ME!
407    * @param recalc DOCUMENT ME!
408    */
409   public void deleteSequence(SequenceI s, boolean recalc)
410   {
411     sequences.removeElement(s);
412
413     if (recalc)
414     {
415       recalcConservation();
416     }
417   }
418
419   /**
420    * DOCUMENT ME!
421    *
422    * @return DOCUMENT ME!
423    */
424   public int getStartRes()
425   {
426     return startRes;
427   }
428
429   /**
430    * DOCUMENT ME!
431    *
432    * @return DOCUMENT ME!
433    */
434   public int getEndRes()
435   {
436     return endRes;
437   }
438
439   /**
440    * DOCUMENT ME!
441    *
442    * @param i DOCUMENT ME!
443    */
444   public void setStartRes(int i)
445   {
446     startRes = i;
447   }
448
449   /**
450    * DOCUMENT ME!
451    *
452    * @param i DOCUMENT ME!
453    */
454   public void setEndRes(int i)
455   {
456     endRes = i;
457   }
458
459   /**
460    * DOCUMENT ME!
461    *
462    * @return DOCUMENT ME!
463    */
464   public int getSize()
465   {
466     return sequences.size();
467   }
468
469   /**
470    * DOCUMENT ME!
471    *
472    * @param i DOCUMENT ME!
473    *
474    * @return DOCUMENT ME!
475    */
476   public SequenceI getSequenceAt(int i)
477   {
478     return (SequenceI) sequences.elementAt(i);
479   }
480
481   /**
482    * DOCUMENT ME!
483    *
484    * @param state DOCUMENT ME!
485    */
486   public void setColourText(boolean state)
487   {
488     colourText = state;
489   }
490
491   /**
492    * DOCUMENT ME!
493    *
494    * @return DOCUMENT ME!
495    */
496   public boolean getColourText()
497   {
498     return colourText;
499   }
500
501   /**
502    * DOCUMENT ME!
503    *
504    * @param state DOCUMENT ME!
505    */
506   public void setDisplayText(boolean state)
507   {
508     displayText = state;
509   }
510
511   /**
512    * DOCUMENT ME!
513    *
514    * @return DOCUMENT ME!
515    */
516   public boolean getDisplayText()
517   {
518     return displayText;
519   }
520
521   /**
522    * DOCUMENT ME!
523    *
524    * @param state DOCUMENT ME!
525    */
526   public void setDisplayBoxes(boolean state)
527   {
528     displayBoxes = state;
529   }
530
531   /**
532    * DOCUMENT ME!
533    *
534    * @return DOCUMENT ME!
535    */
536   public boolean getDisplayBoxes()
537   {
538     return displayBoxes;
539   }
540
541   /**
542    * DOCUMENT ME!
543    *
544    * @return DOCUMENT ME!
545    */
546   public int getWidth()
547   {
548     // MC This needs to get reset when characters are inserted and deleted
549     if (sequences.size() > 0)
550     {
551       width = ( (SequenceI) sequences.elementAt(0)).getLength();
552     }
553
554     for (int i = 1; i < sequences.size(); i++)
555     {
556       SequenceI seq = (SequenceI) sequences.elementAt(i);
557
558       if (seq.getLength() > width)
559       {
560         width = seq.getLength();
561       }
562     }
563
564     return width;
565   }
566
567   /**
568    * DOCUMENT ME!
569    *
570    * @param c DOCUMENT ME!
571    */
572   public void setOutlineColour(Color c)
573   {
574     outlineColour = c;
575   }
576
577   /**
578    * DOCUMENT ME!
579    *
580    * @return DOCUMENT ME!
581    */
582   public Color getOutlineColour()
583   {
584     return outlineColour;
585   }
586
587   /**
588    *
589    * returns the sequences in the group ordered by the ordering given by al
590    *
591    * @param al Alignment
592    * @return SequenceI[]
593    */
594   public SequenceI[] getSequencesInOrder(AlignmentI al)
595   {
596     int sSize = sequences.size();
597     int alHeight = al.getHeight();
598
599     SequenceI[] seqs = new SequenceI[sSize];
600
601     int index = 0;
602     for (int i = 0; i < alHeight && index < sSize; i++)
603     {
604       if (sequences.contains(al.getSequenceAt(i)))
605       {
606         seqs[index++] = al.getSequenceAt(i);
607       }
608     }
609
610     return seqs;
611   }
612 }