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