alignmentProperties added
[jalview.git] / src / jalview / datamodel / Alignment.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 jalview.analysis.*;
24
25 /** Data structure to hold and manipulate a multiple sequence alignment
26  */
27 public class Alignment
28     implements AlignmentI
29 {
30   protected Alignment dataset;
31   protected Vector sequences;
32   protected Vector groups = new Vector();
33   protected char gapCharacter = '-';
34   protected int type = NUCLEOTIDE;
35   public static final int PROTEIN = 0;
36   public static final int NUCLEOTIDE = 1;
37
38   /** DOCUMENT ME!! */
39   public AlignmentAnnotation[] annotations;
40
41   HiddenSequences hiddenSequences = new HiddenSequences(this);
42
43   public Hashtable alignmentProperties;
44
45   private void initAlignment(SequenceI[] seqs)
46   {
47     int i = 0;
48
49     if (jalview.util.Comparison.isNucleotide(seqs))
50     {
51       type = NUCLEOTIDE;
52     }
53     else
54     {
55       type = PROTEIN;
56     }
57
58     sequences = new Vector();
59
60     for (i = 0; i < seqs.length; i++)
61     {
62       sequences.addElement(seqs[i]);
63     }
64
65   }
66
67   /** Make an alignment from an array of Sequences.
68    *
69    * @param sequences
70    */
71   public Alignment(SequenceI[] seqs)
72   {
73     initAlignment(seqs);
74   }
75
76   /**
77    * Make a new alignment from an array of SeqCigars
78    * @param seqs SeqCigar[]
79    */
80   public Alignment(SeqCigar[] alseqs)
81   {
82     SequenceI[] seqs = SeqCigar.createAlignmentSequences(alseqs, gapCharacter,
83         new ColumnSelection(), null);
84     initAlignment(seqs);
85   }
86
87   /**
88    * Make a new alignment from an CigarArray
89    * JBPNote - can only do this when compactAlignment does not contain hidden regions.
90    * JBPNote - must also check that compactAlignment resolves to a set of SeqCigars - or construct them appropriately.
91    * @param compactAlignment CigarArray
92    */
93   public static AlignmentI createAlignment(CigarArray compactAlignment)
94   {
95     throw new Error("Alignment(CigarArray) not yet implemented");
96     // this(compactAlignment.refCigars);
97   }
98
99   /**
100    * DOCUMENT ME!
101    *
102    * @return DOCUMENT ME!
103    */
104   public Vector getSequences()
105   {
106     return sequences;
107   }
108
109   public SequenceI[] getSequencesArray()
110   {
111     SequenceI[] reply = new SequenceI[sequences.size()];
112     for (int i = 0; i < sequences.size(); i++)
113     {
114       reply[i] = (SequenceI) sequences.elementAt(i);
115     }
116     return reply;
117   }
118
119   /**
120    * DOCUMENT ME!
121    *
122    * @param i DOCUMENT ME!
123    *
124    * @return DOCUMENT ME!
125    */
126   public SequenceI getSequenceAt(int i)
127   {
128     if (i < sequences.size())
129     {
130       return (SequenceI) sequences.elementAt(i);
131     }
132
133     return null;
134   }
135
136   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
137    *
138    * @param snew
139    */
140   public void addSequence(SequenceI snew)
141   {
142     if (dataset != null)
143     {
144       // maintain dataset integrity
145       if (snew.getDatasetSequence() != null)
146       {
147         getDataset().addSequence(snew.getDatasetSequence());
148       }
149       else
150       {
151         // derive new sequence
152         SequenceI adding = snew.deriveSequence();
153         getDataset().addSequence(adding.getDatasetSequence());
154         snew = adding;
155       }
156     }
157     if (sequences==null) {
158       initAlignment(new SequenceI[] { snew });
159     } else {
160       sequences.addElement(snew);
161     }
162     if (hiddenSequences!=null)
163       hiddenSequences.adjustHeightSequenceAdded();
164   }
165
166   /** Adds a sequence to the alignment.  Recalculates maxLength and size.
167    *
168    * @param snew
169    */
170   public void setSequenceAt(int i, SequenceI snew)
171   {
172     SequenceI oldseq = getSequenceAt(i);
173     deleteSequence(oldseq);
174
175     sequences.setElementAt(snew, i);
176   }
177
178   /**
179    * DOCUMENT ME!
180    *
181    * @return DOCUMENT ME!
182    */
183   public Vector getGroups()
184   {
185     return groups;
186   }
187
188   public void finalize()
189   {
190     if(getDataset()!=null)
191       getDataset().finalize();
192
193     dataset = null;
194     sequences = null;
195     groups = null;
196     annotations = null;
197     hiddenSequences = null;
198   }
199
200
201   /**
202    * DOCUMENT ME!
203    *
204    * @param s DOCUMENT ME!
205    */
206   public void deleteSequence(SequenceI s)
207   {
208     deleteSequence(findIndex(s));
209   }
210
211   /**
212    * DOCUMENT ME!
213    *
214    * @param i DOCUMENT ME!
215    */
216   public void deleteSequence(int i)
217   {
218     if (i > -1 && i < getHeight())
219     {
220       sequences.removeElementAt(i);
221       hiddenSequences.adjustHeightSequenceDeleted(i);
222     }
223   }
224
225   /**    */
226   public SequenceGroup findGroup(SequenceI s)
227   {
228     for (int i = 0; i < this.groups.size(); i++)
229     {
230       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
231
232       if (sg.getSequences(null).contains(s))
233       {
234         return sg;
235       }
236     }
237
238     return null;
239   }
240
241   /**
242    * DOCUMENT ME!
243    *
244    * @param s DOCUMENT ME!
245    *
246    * @return DOCUMENT ME!
247    */
248   public SequenceGroup[] findAllGroups(SequenceI s)
249   {
250     Vector temp = new Vector();
251
252     int gSize = groups.size();
253     for (int i = 0; i < gSize; i++)
254     {
255       SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
256       if (sg == null || sg.getSequences(null) == null)
257       {
258         this.deleteGroup(sg);
259         gSize--;
260         continue;
261       }
262
263       if (sg.getSequences(null).contains(s))
264       {
265         temp.addElement(sg);
266       }
267     }
268
269     SequenceGroup[] ret = new SequenceGroup[temp.size()];
270
271     for (int i = 0; i < temp.size(); i++)
272     {
273       ret[i] = (SequenceGroup) temp.elementAt(i);
274     }
275
276     return ret;
277   }
278
279   /**    */
280   public void addGroup(SequenceGroup sg)
281   {
282     if (!groups.contains(sg))
283     {
284       if (hiddenSequences.getSize() > 0)
285       {
286         int i, iSize = sg.getSize();
287         for (i = 0; i < iSize; i++)
288         {
289           if (!sequences.contains(sg.getSequenceAt(i)))
290           {
291             sg.deleteSequence(sg.getSequenceAt(i), false);
292             iSize--;
293             i--;
294           }
295         }
296
297         if (sg.getSize() < 1)
298         {
299           return;
300         }
301       }
302
303       groups.addElement(sg);
304     }
305   }
306
307   /**
308    * DOCUMENT ME!
309    */
310   public void deleteAllGroups()
311   {
312     groups.removeAllElements();
313   }
314
315   /**    */
316   public void deleteGroup(SequenceGroup g)
317   {
318     if (groups.contains(g))
319     {
320       groups.removeElement(g);
321     }
322   }
323
324   /**    */
325   public SequenceI findName(String name)
326   {
327     int i = 0;
328
329     while (i < sequences.size())
330     {
331       if (getSequenceAt(i).getName().equals(name))
332       {
333         return getSequenceAt(i);
334       }
335
336       i++;
337     }
338
339     return null;
340   }
341
342   public SequenceI[] findSequenceMatch(String name)
343   {
344     Vector matches = new Vector();
345     int i = 0;
346
347     while (i < sequences.size())
348     {
349       if (getSequenceAt(i).getName().equals(name))
350       {
351         matches.addElement(getSequenceAt(i));
352       }
353       i++;
354     }
355
356     SequenceI[] result = new SequenceI[matches.size()];
357     for (i = 0; i < result.length; i++)
358     {
359       result[i] = (SequenceI) matches.elementAt(i);
360     }
361
362     return result;
363
364   }
365
366   /**    */
367   public int findIndex(SequenceI s)
368   {
369     int i = 0;
370
371     while (i < sequences.size())
372     {
373       if (s == getSequenceAt(i))
374       {
375         return i;
376       }
377
378       i++;
379     }
380
381     return -1;
382   }
383
384   /**
385    * DOCUMENT ME!
386    *
387    * @return DOCUMENT ME!
388    */
389   public int getHeight()
390   {
391     return sequences.size();
392   }
393
394   /**
395    * DOCUMENT ME!
396    *
397    * @return DOCUMENT ME!
398    */
399   public int getWidth()
400   {
401     int maxLength = -1;
402
403     for (int i = 0; i < sequences.size(); i++)
404     {
405       if (getSequenceAt(i).getLength() > maxLength)
406       {
407         maxLength = getSequenceAt(i).getLength();
408       }
409     }
410
411     return maxLength;
412   }
413
414   /**
415    * DOCUMENT ME!
416    *
417    * @param gc DOCUMENT ME!
418    */
419   public void setGapCharacter(char gc)
420   {
421     gapCharacter = gc;
422
423     for (int i = 0; i < sequences.size(); i++)
424     {
425       Sequence seq = (Sequence) sequences.elementAt(i);
426       seq.setSequence(seq.getSequenceAsString()
427                       .replace('.', gc)
428                       .replace('-', gc)
429                       .replace(' ', gc)
430           );
431     }
432   }
433
434   /**
435    * DOCUMENT ME!
436    *
437    * @return DOCUMENT ME!
438    */
439   public char getGapCharacter()
440   {
441     return gapCharacter;
442   }
443
444   /**
445    * DOCUMENT ME!
446    *
447    * @return DOCUMENT ME!
448    */
449   public boolean isAligned()
450   {
451     int width = getWidth();
452
453     for (int i = 0; i < sequences.size(); i++)
454     {
455       if (getSequenceAt(i).getLength() != width)
456       {
457         return false;
458       }
459     }
460
461     return true;
462   }
463
464   /**
465    * DOCUMENT ME!
466    *
467    * @param aa DOCUMENT ME!
468    */
469   public void deleteAnnotation(AlignmentAnnotation aa)
470   {
471     if(aa.sequenceRef!=null)
472       aa.sequenceRef.removeAlignmentAnnotation(aa);
473
474     int aSize = 1;
475
476     if (annotations != null)
477     {
478       aSize = annotations.length;
479     }
480
481     if (aSize < 1)
482     {
483       return;
484     }
485
486     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize - 1];
487
488     int tIndex = 0;
489
490     for (int i = 0; i < aSize; i++)
491     {
492       if (annotations[i] == aa)
493       {
494         continue;
495       }
496
497       temp[tIndex] = annotations[i];
498       tIndex++;
499     }
500
501     annotations = temp;
502   }
503
504   /**
505    * DOCUMENT ME!
506    *
507    * @param aa DOCUMENT ME!
508    */
509   public void addAnnotation(AlignmentAnnotation aa)
510   {
511     int aSize = 1;
512     if (annotations != null)
513     {
514       aSize = annotations.length + 1;
515     }
516
517     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
518
519     temp[aSize - 1] = aa;
520
521     int i = 0;
522
523     if (aSize > 1)
524     {
525       for (i = 0; i < (aSize - 1); i++)
526       {
527         temp[i] = annotations[i];
528       }
529     }
530
531     annotations = temp;
532   }
533
534   public void setAnnotationIndex(AlignmentAnnotation aa, int index)
535   {
536     if (aa == null || annotations == null || annotations.length - 1 < index)
537     {
538       return;
539     }
540
541     int aSize = annotations.length;
542     AlignmentAnnotation[] temp = new AlignmentAnnotation[aSize];
543
544     temp[index] = aa;
545
546     for (int i = 0; i < aSize; i++)
547     {
548       if (i == index)
549       {
550         continue;
551       }
552
553       if (i < index)
554       {
555         temp[i] = annotations[i];
556       }
557       else
558       {
559         temp[i] = annotations[i - 1];
560       }
561     }
562
563     annotations = temp;
564   }
565
566   /**
567    * DOCUMENT ME!
568    *
569    * @return DOCUMENT ME!
570    */
571   public AlignmentAnnotation[] getAlignmentAnnotation()
572   {
573     return annotations;
574   }
575
576   public void setNucleotide(boolean b)
577   {
578     if (b)
579     {
580       type = NUCLEOTIDE;
581     }
582     else
583     {
584       type = PROTEIN;
585     }
586   }
587
588   public boolean isNucleotide()
589   {
590     if (type == NUCLEOTIDE)
591     {
592       return true;
593     }
594     else
595     {
596       return false;
597     }
598   }
599
600   public void setDataset(Alignment data)
601   {
602     if (dataset == null && data == null)
603     {
604       // Create a new dataset for this alignment.
605       // Can only be done once, if dataset is not null
606       // This will not be performed
607       Sequence[] seqs = new Sequence[getHeight()];
608       SequenceI currentSeq;
609       for (int i = 0; i < getHeight(); i++)
610       {
611         currentSeq = getSequenceAt(i);
612         if (currentSeq.getDatasetSequence() != null)
613         {
614           seqs[i] = (Sequence) currentSeq.getDatasetSequence();
615         }
616         else
617         {
618           seqs[i] = new Sequence(currentSeq.getName(),
619                                  AlignSeq.extractGaps(
620                                      jalview.util.Comparison.GapChars,
621                                      currentSeq.getSequenceAsString()
622                                  ),
623                                  currentSeq.getStart(),
624                                  currentSeq.getEnd());
625           seqs[i].sequenceFeatures = currentSeq.getSequenceFeatures();
626           seqs[i].setDescription(currentSeq.getDescription());
627           getSequenceAt(i).setSequenceFeatures(null);
628           getSequenceAt(i).setDatasetSequence(seqs[i]);
629         }
630       }
631
632       dataset = new Alignment(seqs);
633     }
634     else if (dataset == null && data != null)
635     {
636       dataset = data;
637     }
638   }
639
640   public Alignment getDataset()
641   {
642     return dataset;
643   }
644
645   public boolean padGaps()
646   {
647     boolean modified = false;
648
649     //Remove excess gaps from the end of alignment
650     int maxLength = -1;
651
652     SequenceI current;
653     for (int i = 0; i < sequences.size(); i++)
654     {
655       current = getSequenceAt(i);
656       for (int j = current.getLength(); j > maxLength; j--)
657       {
658         if (j > maxLength && !jalview.util.Comparison.isGap(
659             current.getCharAt(j)))
660         {
661           maxLength = j;
662           break;
663         }
664       }
665     }
666
667     maxLength++;
668
669     int cLength;
670     for (int i = 0; i < sequences.size();
671          i++)
672     {
673       current = getSequenceAt(i);
674       cLength = current.getLength();
675
676       if (cLength < maxLength)
677       {
678         current.insertCharAt(cLength,
679                              maxLength - cLength, gapCharacter);
680         modified = true;
681       }
682       else if (current.getLength() > maxLength)
683       {
684         current.deleteChars(maxLength, current.getLength());
685       }
686     }
687     return modified;
688   }
689
690   public HiddenSequences getHiddenSequences()
691   {
692     return hiddenSequences;
693   }
694
695   public CigarArray getCompactAlignment()
696   {
697     SeqCigar alseqs[] = new SeqCigar[sequences.size()];
698     for (int i = 0; i < sequences.size(); i++)
699     {
700       alseqs[i] = new SeqCigar( (SequenceI) sequences.elementAt(i));
701     }
702     CigarArray cal = new CigarArray(alseqs);
703     cal.addOperation(CigarArray.M, getWidth());
704     return cal;
705   }
706
707   public void setProperty(Object key, Object value)
708   {
709     if(alignmentProperties==null)
710       alignmentProperties = new Hashtable();
711
712     alignmentProperties.put(key,value);
713   }
714
715   public Object getProperty(Object key)
716   {
717     if(alignmentProperties!=null)
718       return alignmentProperties.get(key);
719     else
720       return null;
721   }
722
723
724 }