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