Merge branch 'develop' into features/filetypeEnum
[jalview.git] / src / jalview / io / AlignFile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.AlignmentI;
25 import jalview.datamodel.Sequence;
26 import jalview.datamodel.SequenceGroup;
27 import jalview.datamodel.SequenceI;
28 import jalview.util.MessageManager;
29
30 import java.io.IOException;
31 import java.util.ArrayList;
32 import java.util.Enumeration;
33 import java.util.Hashtable;
34 import java.util.List;
35 import java.util.Vector;
36
37 /**
38  * DOCUMENT ME!
39  * 
40  * @author $author$
41  * @version $Revision$
42  */
43 public abstract class AlignFile extends FileParse implements AlignmentFileI
44 {
45   int noSeqs = 0;
46
47   int maxLength = 0;
48
49   /**
50    * Sequences to be added to form a new alignment. TODO: remove vector in this
51    * class
52    */
53   protected Vector<SequenceI> seqs;
54
55   /**
56    * annotation to be added to generated alignment object
57    */
58   protected Vector<AlignmentAnnotation> annotations;
59
60   /**
61    * SequenceGroups to be added to the alignment object
62    */
63   protected List<SequenceGroup> seqGroups;
64
65   /**
66    * Properties to be added to generated alignment object
67    */
68   protected Hashtable properties;
69
70   long start;
71
72   long end;
73
74   private boolean parseCalled;
75
76   /**
77    * Creates a new AlignFile object.
78    */
79   public AlignFile()
80   {
81     // Shouldn't we init data structures (JBPNote: not sure - initData is for
82     // initialising the structures used for reading from a datasource, and the
83     // bare constructor hasn't got any datasource)
84     initData();
85   }
86
87   public AlignFile(SequenceI[] seqs)
88   {
89     this();
90     setSeqs(seqs);
91   }
92
93   /**
94    * Constructor which parses the data from a file of some specified type.
95    * 
96    * @param dataObject
97    *          Filename, URL or Pasted String to read from.
98    * @param sourceType
99    *          What type of file to read from (File, URL, Pasted String)
100    */
101   public AlignFile(String dataObject, DataSourceType sourceType)
102           throws IOException
103   {
104     this(true, dataObject, sourceType);
105   }
106
107   /**
108    * Constructor which (optionally delays) parsing of data from a file of some
109    * specified type.
110    * 
111    * @param parseImmediately
112    *          if false, need to call 'doParse()' to begin parsing data
113    * @param dataObject
114    *          Filename, URL or Pasted String to read from.
115    * @param sourceType
116    *          What type of file to read from (File, URL)
117    * @throws IOException
118    */
119   public AlignFile(boolean parseImmediately, String dataObject,
120           DataSourceType sourceType)
121           throws IOException
122   {
123     super(dataObject, sourceType);
124     initData();
125     if (parseImmediately)
126     {
127       doParse();
128     }
129   }
130
131   /**
132    * Attempt to read from the position where some other parsing process left
133    * off.
134    * 
135    * @param source
136    * @throws IOException
137    */
138   public AlignFile(FileParse source) throws IOException
139   {
140     this(true, source);
141   }
142
143   /**
144    * Construct a new parser to read from the position where some other parsing
145    * process left
146    * 
147    * @param parseImmediately
148    *          if false, need to call 'doParse()' to begin parsing data
149    * @param source
150    */
151   public AlignFile(boolean parseImmediately, FileParse source)
152           throws IOException
153   {
154     super(source);
155     initData();
156     if (parseImmediately)
157     {
158       doParse();
159     }
160   }
161
162   /**
163    * called if parsing was delayed till after parser was constructed
164    * 
165    * @throws IOException
166    */
167   public void doParse() throws IOException
168   {
169     if (parseCalled)
170     {
171       throw new IOException(
172               "Implementation error: Parser called twice for same data.\n"
173                       + "Need to call initData() again before parsing can be reattempted.");
174     }
175     parseCalled = true;
176     parse();
177     // sets the index of each sequence in the alignment
178     for (int i = 0, c = seqs.size(); i < c; i++)
179     {
180       seqs.get(i).setIndex(i);
181     }
182   }
183
184   /**
185    * Return the seqs Vector
186    */
187   public Vector<SequenceI> getSeqs()
188   {
189     return seqs;
190   }
191
192   public List<SequenceGroup> getSeqGroups()
193   {
194     return seqGroups;
195   }
196
197   /**
198    * Return the Sequences in the seqs Vector as an array of Sequences
199    */
200   @Override
201   public SequenceI[] getSeqsAsArray()
202   {
203     SequenceI[] s = new SequenceI[seqs.size()];
204
205     for (int i = 0; i < seqs.size(); i++)
206     {
207       s[i] = seqs.elementAt(i);
208     }
209
210     return s;
211   }
212
213   /**
214    * called by AppletFormatAdapter to generate an annotated alignment, rather
215    * than bare sequences.
216    * 
217    * @param al
218    */
219   @Override
220   public void addAnnotations(AlignmentI al)
221   {
222     addProperties(al);
223     for (int i = 0; i < annotations.size(); i++)
224     {
225       // detect if annotations.elementAt(i) rna secondary structure
226       // if so then do:
227       /*
228        * SequenceFeature[] pairArray =
229        * Rna.GetBasePairsFromAlignmentAnnotation(annotations.elementAt(i));
230        * Rna.HelixMap(pairArray);
231        */
232       AlignmentAnnotation an = annotations.elementAt(i);
233       an.validateRangeAndDisplay();
234       al.addAnnotation(an);
235     }
236
237   }
238
239   /**
240    * register sequence groups on the alignment for **output**
241    * 
242    * @param al
243    */
244   public void addSeqGroups(AlignmentI al)
245   {
246     this.seqGroups = al.getGroups();
247
248   }
249
250   /**
251    * Add any additional information extracted from the file to the alignment
252    * properties.
253    * 
254    * @note implicitly called by addAnnotations()
255    * @param al
256    */
257   public void addProperties(AlignmentI al)
258   {
259     if (properties != null && properties.size() > 0)
260     {
261       Enumeration keys = properties.keys();
262       Enumeration vals = properties.elements();
263       while (keys.hasMoreElements())
264       {
265         al.setProperty(keys.nextElement(), vals.nextElement());
266       }
267     }
268   }
269
270   /**
271    * Store a non-null key-value pair in a hashtable used to set alignment
272    * properties note: null keys will raise an error, null values will result in
273    * the key/value pair being silently ignored.
274    * 
275    * @param key
276    *          - non-null key object
277    * @param value
278    *          - non-null value
279    */
280   protected void setAlignmentProperty(Object key, Object value)
281   {
282     if (key == null)
283     {
284       throw new Error(
285               MessageManager
286                       .getString("error.implementation_error_cannot_have_null_alignment"));
287     }
288     if (value == null)
289     {
290       return; // null properties are ignored.
291     }
292     if (properties == null)
293     {
294       properties = new Hashtable();
295     }
296     properties.put(key, value);
297   }
298
299   protected Object getAlignmentProperty(Object key)
300   {
301     if (properties != null && key != null)
302     {
303       return properties.get(key);
304     }
305     return null;
306   }
307
308   /**
309    * Initialise objects to store sequence data in.
310    */
311   protected void initData()
312   {
313     seqs = new Vector<SequenceI>();
314     annotations = new Vector<AlignmentAnnotation>();
315     seqGroups = new ArrayList<SequenceGroup>();
316     parseCalled = false;
317   }
318
319   /**
320    * DOCUMENT ME!
321    * 
322    * @param s
323    *          DOCUMENT ME!
324    */
325   @Override
326   public void setSeqs(SequenceI[] s)
327   {
328     seqs = new Vector<SequenceI>();
329
330     for (int i = 0; i < s.length; i++)
331     {
332       seqs.addElement(s[i]);
333     }
334   }
335
336   /**
337    * This method must be implemented to parse the contents of the file.
338    */
339   public abstract void parse() throws IOException;
340
341   /**
342    * A general parser for ids.
343    * 
344    * @String id Id to be parsed
345    */
346   Sequence parseId(String id)
347   {
348     Sequence seq = null;
349     id = id.trim();
350     int space = id.indexOf(" ");
351     if (space > -1)
352     {
353       seq = new Sequence(id.substring(0, space), "");
354       String desc = id.substring(space + 1);
355       seq.setDescription(desc);
356
357       /*
358        * it is tempting to parse Ensembl style gene description e.g.
359        * chromosome:GRCh38:7:140696688:140721955:1 and set the
360        * start position of the sequence, but this causes much confusion
361        * for reverse strand feature locations
362        */
363     }
364     else
365     {
366       seq = new Sequence(id, "");
367     }
368
369     return seq;
370   }
371
372   /**
373    * Creates the output id. Adds prefix Uniprot format source|id and optionally
374    * suffix Jalview /start-end
375    * 
376    * @param jvsuffix
377    * 
378    * @String id Id to be parsed
379    */
380   String printId(SequenceI seq, boolean jvsuffix)
381   {
382     return seq.getDisplayId(jvsuffix);
383   }
384
385   String printId(SequenceI seq)
386   {
387     return printId(seq, true);
388   }
389
390   /**
391    * vector of String[] treeName, newickString pairs
392    */
393   Vector<String[]> newickStrings = null;
394
395   protected void addNewickTree(String treeName, String newickString)
396   {
397     if (newickStrings == null)
398     {
399       newickStrings = new Vector<String[]>();
400     }
401     newickStrings.addElement(new String[] { treeName, newickString });
402   }
403
404   protected int getTreeCount()
405   {
406     return newickStrings == null ? 0 : newickStrings.size();
407   }
408
409   @Override
410   public void addGroups(AlignmentI al)
411   {
412
413     for (SequenceGroup sg : getSeqGroups())
414     {
415       al.addGroup(sg);
416     }
417   }
418
419 }