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