FileParse object can be re-used to read different files concatenated together
[jalview.git] / src / jalview / analysis / SeqsetUtils.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.analysis;
20
21 import java.util.*;
22
23 import jalview.datamodel.*;
24
25 /**
26  * <p>Title: </p>
27  *
28  * <p>Description: </p>
29  *
30  * <p>Copyright: Copyright (c) 2004</p>
31  *
32  * <p>Company: Dundee University</p>
33  *
34  * @author not attributable
35  * @version 1.0
36  */
37 public class SeqsetUtils
38 {
39
40   /**
41    * Store essential properties of a sequence in a hashtable for later recovery
42    *  Keys are Name, Start, End, SeqFeatures, PdbId
43    * @param seq SequenceI
44    * @return Hashtable
45    */
46   public static Hashtable SeqCharacterHash(SequenceI seq)
47   {
48     Hashtable sqinfo = new Hashtable();
49     sqinfo.put("Name", seq.getName());
50     sqinfo.put("Start", new Integer(seq.getStart()));
51     sqinfo.put("End", new Integer(seq.getEnd()));
52     if (seq.getDescription() != null)
53     {
54       sqinfo.put("Description", seq.getDescription());
55     }
56     Vector sfeat = new Vector();
57     jalview.datamodel.SequenceFeature[] sfarray = seq.getSequenceFeatures();
58     if (sfarray != null && sfarray.length > 0)
59     {
60       for (int i = 0; i < sfarray.length; i++)
61       {
62         sfeat.add(sfarray[i]);
63       }
64     }
65     sqinfo.put("SeqFeatures", sfeat);
66     sqinfo.put("PdbId",
67                (seq.getPDBId() != null) ? seq.getPDBId() : new Vector());
68     sqinfo.put("datasetSequence",
69                (seq.getDatasetSequence() != null) ? seq.getDatasetSequence() :
70                new Sequence("THISISAPLACEHOLDER", ""));
71     return sqinfo;
72   }
73
74   /**
75    * Recover essential properties of a sequence from a hashtable
76    * TODO: replace these methods with something more elegant.
77    * @param sq SequenceI
78    * @param sqinfo Hashtable
79    * @return boolean true if name was not updated from sqinfo Name entry
80    */
81   public static boolean SeqCharacterUnhash(SequenceI sq, Hashtable sqinfo)
82   {
83     boolean namePresent = true;
84     if (sqinfo == null)
85     {
86       return false;
87     }
88     String oldname = (String) sqinfo.get("Name");
89     Integer start = (Integer) sqinfo.get("Start");
90     Integer end = (Integer) sqinfo.get("End");
91     Vector sfeatures = (Vector) sqinfo.get(
92         "SeqFeatures");
93     Vector pdbid = (Vector) sqinfo.get("PdbId");
94     String description = (String) sqinfo.get("Description");
95     Sequence seqds = (Sequence) sqinfo.get("datasetSequence");
96     if (oldname == null)
97     {
98       namePresent = false;
99     }
100     else
101     {
102       sq.setName(oldname);
103     }
104     if (pdbid != null && pdbid.size() > 0)
105     {
106       sq.setPDBId(pdbid);
107     }
108
109     if ( (start != null) && (end != null))
110     {
111       sq.setStart(start.intValue());
112       sq.setEnd(end.intValue());
113     }
114
115     if ( (sfeatures != null) && (sfeatures.size() > 0))
116     {
117       SequenceFeature[] sfarray = (SequenceFeature[]) sfeatures.toArray();
118       sq.setSequenceFeatures(sfarray);
119     }
120     if (description != null)
121     {
122       sq.setDescription(description);
123     }
124     if ( (seqds != null) &&
125         ! (seqds.getName().equals("THISISAPLACEHOLDER") &&
126            seqds.getLength() == 0))
127     {
128       sq.setDatasetSequence(seqds);
129     }
130
131     return namePresent;
132   }
133
134   /**
135    * Form of the unique name used in uniquify for the i'th sequence in an ordered vector of sequences.
136    * @param i int
137    * @return String
138    */
139   public static String unique_name(int i)
140   {
141     return new String("Sequence" + i);
142   }
143
144   /**
145    * Generates a hash of SeqCharacterHash properties for each sequence
146    * in a sequence set, and optionally renames the sequences to an
147    * unambiguous 'safe' name.
148    * @param sequences SequenceI[]
149    * @param write_names boolean set this to rename each of the sequences to its unique_name(index) name
150    * @return Hashtable to be passed to @see deuniquify to recover original names (and properties) for renamed sequences
151    */
152   public static Hashtable uniquify(SequenceI[] sequences, boolean write_names)
153   {
154     // Generate a safely named sequence set and a hash to recover the sequence names
155     Hashtable map = new Hashtable();
156     //String[] un_names = new String[sequences.length];
157
158     for (int i = 0; i < sequences.length; i++)
159     {
160       String safename = unique_name(i);
161       map.put(safename, SeqCharacterHash(sequences[i]));
162
163       if (write_names)
164       {
165         sequences[i].setName(safename);
166       }
167     }
168
169     return map;
170   }
171
172   /**
173    * recover unsafe sequence names and original properties for a sequence
174    * set using a map generated by @see uniquify(sequences,true)
175    * @param map Hashtable
176    * @param sequences SequenceI[]
177    * @return boolean
178    */
179   public static boolean deuniquify(Hashtable map, SequenceI[] sequences)
180   {
181     jalview.analysis.SequenceIdMatcher matcher = new SequenceIdMatcher(
182         sequences);
183     SequenceI msq = null;
184     Enumeration keys = map.keys();
185     Vector unmatched = new Vector();
186     for (int i = 0, j = sequences.length; i < j; i++)
187     {
188       unmatched.add(sequences[i]);
189     }
190     while (keys.hasMoreElements())
191     {
192       Object key = keys.nextElement();
193       if (key instanceof String)
194       {
195         if ( (msq = matcher.findIdMatch( (String) key)) != null)
196         {
197           Hashtable sqinfo = (Hashtable) map.get(key);
198           unmatched.remove(msq);
199           SeqCharacterUnhash(msq, sqinfo);
200         }
201         else
202         {
203           System.err.println("Can't find '" + ( (String) key) +
204                              "' in uniquified alignment");
205         }
206       }
207     }
208     if (unmatched.size() > 0)
209     {
210       System.err.println("Did not find matches for :");
211       for (Enumeration i = unmatched.elements(); i.hasMoreElements();
212            System.out.println( ( (SequenceI) i.nextElement()).getName()))
213       {
214         ;
215       }
216       return false;
217     }
218
219     return true;
220   }
221
222   /**
223    * returns a subset of the sequenceI seuqences,
224    * including only those that contain at least one residue.
225    * @param sequences SequenceI[]
226    * @return SequenceI[]
227    */
228   public static SequenceI[] getNonEmptySequenceSet(SequenceI[] sequences)
229   {
230     // Identify first row of alignment with residues for prediction
231     boolean ungapped[] = new boolean[sequences.length];
232     int msflen = 0;
233     for (int i = 0, j = sequences.length; i < j; i++)
234     {
235       String tempseq = jalview.analysis.AlignSeq.extractGaps(
236           jalview.util.Comparison.GapChars,
237           sequences[i].getSequenceAsString());
238
239       if (tempseq.length() == 0)
240       {
241         ungapped[i] = false;
242       }
243       else
244       {
245         ungapped[i] = true;
246         msflen++;
247       }
248     }
249     if (msflen == 0)
250     {
251       return null; // no minimal set
252     }
253     // compose minimal set
254     SequenceI[] mset = new SequenceI[msflen];
255     for (int i = 0, j = sequences.length, k = 0; i < j; i++)
256     {
257       if (ungapped[i])
258       {
259         mset[k++] = sequences[i];
260       }
261     }
262     ungapped = null;
263     return mset;
264   }
265 }