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