file format enum wip changes
[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   boolean jvSuffix = true;
75
76   private boolean parseCalled;
77
78   /**
79    * Creates a new AlignFile object.
80    */
81   public AlignFile()
82   {
83     // Shouldn't we init data structures (JBPNote: not sure - initData is for
84     // initialising the structures used for reading from a datasource, and the
85     // bare constructor hasn't got any datasource)
86     initData();
87   }
88
89   /**
90    * Constructor which parses the data from a file of some specified type.
91    * 
92    * @param dataObject
93    *          Filename, URL or Pasted String to read from.
94    * @param sourceType
95    *          What type of file to read from (File, URL, Pasted String)
96    */
97   public AlignFile(String dataObject, DataSourceType sourceType)
98           throws IOException
99   {
100     this(true, dataObject, sourceType);
101   }
102
103   /**
104    * Constructor which (optionally delays) parsing of data from a file of some
105    * specified type.
106    * 
107    * @param parseImmediately
108    *          if false, need to call 'doParse()' to begin parsing data
109    * @param dataObject
110    *          Filename, URL or Pasted String to read from.
111    * @param sourceType
112    *          What type of file to read from (File, URL)
113    * @throws IOException
114    */
115   public AlignFile(boolean parseImmediately, String dataObject,
116           DataSourceType sourceType)
117           throws IOException
118   {
119     super(dataObject, sourceType);
120     initData();
121     if (parseImmediately)
122     {
123       doParse();
124     }
125   }
126
127   /**
128    * Attempt to read from the position where some other parsing process left
129    * off.
130    * 
131    * @param source
132    * @throws IOException
133    */
134   public AlignFile(FileParse source) throws IOException
135   {
136     this(true, source);
137   }
138
139   /**
140    * Construct a new parser to read from the position where some other parsing
141    * process left
142    * 
143    * @param parseImmediately
144    *          if false, need to call 'doParse()' to begin parsing data
145    * @param source
146    */
147   public AlignFile(boolean parseImmediately, FileParse source)
148           throws IOException
149   {
150     super(source);
151     initData();
152     if (parseImmediately)
153     {
154       doParse();
155     }
156   }
157
158   /**
159    * called if parsing was delayed till after parser was constructed
160    * 
161    * @throws IOException
162    */
163   public void doParse() throws IOException
164   {
165     if (parseCalled)
166     {
167       throw new IOException(
168               "Implementation error: Parser called twice for same data.\n"
169                       + "Need to call initData() again before parsing can be reattempted.");
170     }
171     parseCalled = true;
172     parse();
173     // sets the index of each sequence in the alignment
174     for (int i = 0, c = seqs.size(); i < c; i++)
175     {
176       seqs.get(i).setIndex(i);
177     }
178   }
179
180   /**
181    * Return the seqs Vector
182    */
183   public Vector<SequenceI> getSeqs()
184   {
185     return seqs;
186   }
187
188   public List<SequenceGroup> getSeqGroups()
189   {
190     return seqGroups;
191   }
192
193   /**
194    * Return the Sequences in the seqs Vector as an array of Sequences
195    */
196   @Override
197   public SequenceI[] getSeqsAsArray()
198   {
199     SequenceI[] s = new SequenceI[seqs.size()];
200
201     for (int i = 0; i < seqs.size(); i++)
202     {
203       s[i] = seqs.elementAt(i);
204     }
205
206     return s;
207   }
208
209   /**
210    * called by AppletFormatAdapter to generate an annotated alignment, rather
211    * than bare sequences.
212    * 
213    * @param al
214    */
215   @Override
216   public void addAnnotations(AlignmentI al)
217   {
218     addProperties(al);
219     for (int i = 0; i < annotations.size(); i++)
220     {
221       // detect if annotations.elementAt(i) rna secondary structure
222       // if so then do:
223       /*
224        * SequenceFeature[] pairArray =
225        * Rna.GetBasePairsFromAlignmentAnnotation(annotations.elementAt(i));
226        * Rna.HelixMap(pairArray);
227        */
228       AlignmentAnnotation an = annotations.elementAt(i);
229       an.validateRangeAndDisplay();
230       al.addAnnotation(an);
231     }
232
233   }
234
235   /**
236    * register sequence groups on the alignment for **output**
237    * 
238    * @param al
239    */
240   public void addSeqGroups(AlignmentI al)
241   {
242     this.seqGroups = al.getGroups();
243
244   }
245
246   /**
247    * Add any additional information extracted from the file to the alignment
248    * properties.
249    * 
250    * @note implicitly called by addAnnotations()
251    * @param al
252    */
253   public void addProperties(AlignmentI al)
254   {
255     if (properties != null && properties.size() > 0)
256     {
257       Enumeration keys = properties.keys();
258       Enumeration vals = properties.elements();
259       while (keys.hasMoreElements())
260       {
261         al.setProperty(keys.nextElement(), vals.nextElement());
262       }
263     }
264   }
265
266   /**
267    * Store a non-null key-value pair in a hashtable used to set alignment
268    * properties note: null keys will raise an error, null values will result in
269    * the key/value pair being silently ignored.
270    * 
271    * @param key
272    *          - non-null key object
273    * @param value
274    *          - non-null value
275    */
276   protected void setAlignmentProperty(Object key, Object value)
277   {
278     if (key == null)
279     {
280       throw new Error(
281               MessageManager
282                       .getString("error.implementation_error_cannot_have_null_alignment"));
283     }
284     if (value == null)
285     {
286       return; // null properties are ignored.
287     }
288     if (properties == null)
289     {
290       properties = new Hashtable();
291     }
292     properties.put(key, value);
293   }
294
295   protected Object getAlignmentProperty(Object key)
296   {
297     if (properties != null && key != null)
298     {
299       return properties.get(key);
300     }
301     return null;
302   }
303
304   /**
305    * Initialise objects to store sequence data in.
306    */
307   protected void initData()
308   {
309     seqs = new Vector<SequenceI>();
310     annotations = new Vector<AlignmentAnnotation>();
311     seqGroups = new ArrayList<SequenceGroup>();
312     parseCalled = false;
313   }
314
315   /**
316    * DOCUMENT ME!
317    * 
318    * @param s
319    *          DOCUMENT ME!
320    */
321   @Override
322   public void setSeqs(SequenceI[] s)
323   {
324     seqs = new Vector<SequenceI>();
325
326     for (int i = 0; i < s.length; i++)
327     {
328       seqs.addElement(s[i]);
329     }
330   }
331
332   /**
333    * This method must be implemented to parse the contents of the file.
334    */
335   public abstract void parse() throws IOException;
336
337   /**
338    * Print out in alignment file format the Sequences in the seqs Vector.
339    */
340   @Override
341   public abstract String print();
342
343   @Override
344   public void addJVSuffix(boolean b)
345   {
346     jvSuffix = b;
347   }
348
349   /**
350    * A general parser for ids.
351    * 
352    * @String id Id to be parsed
353    */
354   Sequence parseId(String id)
355   {
356     Sequence seq = null;
357     id = id.trim();
358     int space = id.indexOf(" ");
359     if (space > -1)
360     {
361       seq = new Sequence(id.substring(0, space), "");
362       String desc = id.substring(space + 1);
363       seq.setDescription(desc);
364
365       /*
366        * it is tempting to parse Ensembl style gene description e.g.
367        * chromosome:GRCh38:7:140696688:140721955:1 and set the
368        * start position of the sequence, but this causes much confusion
369        * for reverse strand feature locations
370        */
371     }
372     else
373     {
374       seq = new Sequence(id, "");
375     }
376
377     return seq;
378   }
379
380   /**
381    * Creates the output id. Adds prefix Uniprot format source|id And suffix
382    * Jalview /start-end
383    * 
384    * @String id Id to be parsed
385    */
386   String printId(SequenceI seq)
387   {
388     return seq.getDisplayId(jvSuffix);
389   }
390
391   /**
392    * vector of String[] treeName, newickString pairs
393    */
394   Vector<String[]> newickStrings = null;
395
396   protected void addNewickTree(String treeName, String newickString)
397   {
398     if (newickStrings == null)
399     {
400       newickStrings = new Vector<String[]>();
401     }
402     newickStrings.addElement(new String[] { treeName, newickString });
403   }
404
405   protected int getTreeCount()
406   {
407     return newickStrings == null ? 0 : newickStrings.size();
408   }
409
410   @Override
411   public void addGroups(AlignmentI al)
412   {
413
414     for (SequenceGroup sg : getSeqGroups())
415     {
416       al.addGroup(sg);
417     }
418   }
419
420 }