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