JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / jalview / io / ClustalFile.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.Sequence;
25 import jalview.datamodel.SequenceI;
26 import jalview.util.Format;
27
28 import java.io.IOException;
29 import java.util.Hashtable;
30 import java.util.StringTokenizer;
31 import java.util.Vector;
32
33 public class ClustalFile extends AlignFile
34 {
35
36   public ClustalFile()
37   {
38   }
39
40   public ClustalFile(String inFile, String type) throws IOException
41   {
42     super(inFile, type);
43   }
44
45   public ClustalFile(FileParse source) throws IOException
46   {
47     super(source);
48   }
49
50   @Override
51   public void initData()
52   {
53     super.initData();
54   }
55
56   @Override
57   public void parse() throws IOException
58   {
59     int i = 0;
60     boolean flag = false;
61     boolean rna = false;
62     boolean top = false;
63     StringBuffer pssecstr = new StringBuffer(), consstr = new StringBuffer();
64     Vector headers = new Vector();
65     Hashtable seqhash = new Hashtable();
66     StringBuffer tempseq;
67     String line, id;
68     StringTokenizer str;
69
70     try
71     {
72       while ((line = nextLine()) != null)
73       {
74         if (line.length() == 0)
75         {
76           top = true;
77         }
78         if (line.indexOf(" ") != 0)
79         {
80           str = new StringTokenizer(line, " ");
81
82           if (str.hasMoreTokens())
83           {
84             id = str.nextToken();
85
86             if (id.equalsIgnoreCase("CLUSTAL"))
87             {
88               flag = true;
89             }
90             else
91             {
92               if (flag)
93               {
94                 if (seqhash.containsKey(id))
95                 {
96                   tempseq = (StringBuffer) seqhash.get(id);
97                 }
98                 else
99                 {
100                   tempseq = new StringBuffer();
101                   seqhash.put(id, tempseq);
102                 }
103
104                 if (!(headers.contains(id)))
105                 {
106                   headers.addElement(id);
107                 }
108
109                 if (str.hasMoreTokens())
110                 {
111                   tempseq.append(str.nextToken());
112                 }
113                 top = false;
114               }
115             }
116           }
117           else
118           {
119             flag = true;
120           }
121         }
122         else
123         {
124           if (line.matches("\\s+(-|\\.|\\(|\\[|\\]|\\))+"))
125           {
126             if (top)
127             {
128               pssecstr.append(line.trim());
129             }
130             else
131             {
132               consstr.append(line.trim());
133             }
134           }
135         }
136       }
137     } catch (IOException e)
138     {
139       System.err.println("Exception parsing clustal file " + e);
140       e.printStackTrace();
141     }
142
143     if (flag)
144     {
145       this.noSeqs = headers.size();
146
147       // Add sequences to the hash
148       for (i = 0; i < headers.size(); i++)
149       {
150         if (seqhash.get(headers.elementAt(i)) != null)
151         {
152           if (maxLength < seqhash.get(headers.elementAt(i)).toString()
153                   .length())
154           {
155             maxLength = seqhash.get(headers.elementAt(i)).toString()
156                     .length();
157           }
158
159           Sequence newSeq = parseId(headers.elementAt(i).toString());
160           newSeq.setSequence(seqhash.get(headers.elementAt(i).toString())
161                   .toString());
162
163           seqs.addElement(newSeq);
164         }
165         else
166         {
167           System.err
168                   .println("Clustal File Reader: Can't find sequence for "
169                           + headers.elementAt(i));
170         }
171       }
172       AlignmentAnnotation lastssa = null;
173       if (pssecstr.length() == maxLength)
174       {
175         Vector ss = new Vector();
176         AlignmentAnnotation ssa = lastssa = StockholmFile
177                 .parseAnnotationRow(ss, "secondary structure",
178                         pssecstr.toString());
179         ssa.label = "Secondary Structure";
180         annotations.addElement(ssa);
181       }
182       if (consstr.length() == maxLength)
183       {
184         Vector ss = new Vector();
185         AlignmentAnnotation ssa = StockholmFile.parseAnnotationRow(ss,
186                 "secondary structure", consstr.toString());
187         ssa.label = "Consensus Secondary Structure";
188         if (lastssa == null
189                 || !lastssa.getRNAStruc().equals(
190                         ssa.getRNAStruc().replace('-', '.')))
191         {
192           annotations.addElement(ssa);
193         }
194       }
195     }
196   }
197
198   @Override
199   public String print()
200   {
201     return print(getSeqsAsArray());
202     // TODO: locaRNA style aln output
203   }
204
205   public String print(SequenceI[] s)
206   {
207     StringBuffer out = new StringBuffer("CLUSTAL" + newline + newline);
208
209     int max = 0;
210     int maxid = 0;
211
212     int i = 0;
213
214     while ((i < s.length) && (s[i] != null))
215     {
216       String tmp = printId(s[i]);
217
218       if (s[i].getSequence().length > max)
219       {
220         max = s[i].getSequence().length;
221       }
222
223       if (tmp.length() > maxid)
224       {
225         maxid = tmp.length();
226       }
227
228       i++;
229     }
230
231     if (maxid < 15)
232     {
233       maxid = 15;
234     }
235
236     maxid++;
237
238     int len = 60;
239     int nochunks = (max / len) + (max % len > 0 ? 1 : 0);
240
241     for (i = 0; i < nochunks; i++)
242     {
243       int j = 0;
244
245       while ((j < s.length) && (s[j] != null))
246       {
247         out.append(new Format("%-" + maxid + "s").form(printId(s[j]) + " "));
248
249         int start = i * len;
250         int end = start + len;
251
252         if ((end < s[j].getSequence().length)
253                 && (start < s[j].getSequence().length))
254         {
255           out.append(s[j].getSequenceAsString(start, end));
256         }
257         else
258         {
259           if (start < s[j].getSequence().length)
260           {
261             out.append(s[j].getSequenceAsString().substring(start));
262           }
263         }
264
265         out.append(newline);
266         j++;
267       }
268
269       out.append(newline);
270     }
271
272     return out.toString();
273   }
274 }