fa6ef6d013ac861d592f08959574827cff4fe46f
[jalview.git] / src / jalview / io / ModellerDescription.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.io;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 public class ModellerDescription\r
24 {\r
25   /**\r
26    * Translates between a String containing a set of colon-separated values\r
27    * on a single line, and sequence start/end and other properties.\r
28    * See PIRFile IO for its use.\r
29    */\r
30   final String[] seqTypes =\r
31       {\r
32       "sequence", "structure", "structureX", "structureN"};\r
33   final String[] Fields =\r
34       {\r
35       "objectType", "objectId",\r
36       "startField", "startCode",\r
37       "endField", "endCode",\r
38       "description1", "description2",\r
39       "resolutionField", "tailField"};\r
40   final int TYPE = 0;\r
41   final int LOCALID = 1;\r
42   final int START = 2;\r
43   final int START_CHAIN = 3;\r
44   final int END = 4;\r
45   final int END_CHAIN = 5;\r
46   final int DESCRIPTION1 = 6;\r
47   final int DESCRIPTION2 = 7;\r
48   final int RESOLUTION = 8;\r
49   final int TAIL = 9;\r
50 \r
51   /**\r
52    * 0 is free text or empty\r
53    * 1 is something that parses to an integer, or \@\r
54    */\r
55   final int Types[] =\r
56       {\r
57       0, 0, 1, 0, 1, 0, 0, 0, 0, 0\r
58   };\r
59   final char Padding[] =\r
60       {\r
61       ' ', ' ', ' ', '.', ' ', '.', '.', '.', '.', '.'\r
62   };\r
63 \r
64   java.util.Hashtable fields = new java.util.Hashtable();\r
65   ModellerDescription()\r
66   {\r
67     fields.put(Fields[TAIL], "");\r
68   }\r
69 \r
70   class resCode\r
71   {\r
72     Integer val;\r
73     String field;\r
74     resCode(String f, Integer v)\r
75     {\r
76       val = v;\r
77       field = f;\r
78     }\r
79 \r
80     resCode(int v)\r
81     {\r
82       val = new Integer(v);\r
83       field = val.toString();\r
84     }\r
85   };\r
86 \r
87   private resCode validResidueCode(String field)\r
88   {\r
89     Integer val = null;\r
90     com.stevesoft.pat.Regex r = new com.stevesoft.pat.Regex(\r
91         "\\s*((([-0-9]+).?)|FIRST|LAST|@)");\r
92 \r
93     if (!r.search(field))\r
94     {\r
95       return null; // invalid\r
96     }\r
97     String value = r.stringMatched(3);\r
98     if (value == null)\r
99     {\r
100       value = r.stringMatched(1);\r
101     }\r
102     // jalview.bin.Cache.log.debug("from '" + field + "' matched '" + value +\r
103     //                             "'");\r
104     try\r
105     {\r
106       val = Integer.valueOf(value);\r
107       return new resCode(field, val); // successful numeric extraction\r
108     }\r
109     catch (Exception e)\r
110     {\r
111     }\r
112     return new resCode(field, null);\r
113   }\r
114 \r
115   private java.util.Hashtable parseDescription(String desc)\r
116   {\r
117     java.util.Hashtable fields = new java.util.Hashtable();\r
118     java.util.StringTokenizer st = new java.util.StringTokenizer(desc, ":");\r
119     String field;\r
120     int type = -1;\r
121     if (st.countTokens() > 0)\r
122     {\r
123       // parse colon-fields\r
124       int i = 0;\r
125       field = st.nextToken(":");\r
126       do\r
127       {\r
128         if (seqTypes[i].equalsIgnoreCase(field))\r
129         {\r
130           break;\r
131         }\r
132       }\r
133       while (++i < seqTypes.length);\r
134 \r
135       if (i < seqTypes.length)\r
136       {\r
137         // valid seqType for modeller\r
138         type = i;\r
139         i = 1; // continue parsing fields\r
140         while (i < TAIL && st.hasMoreTokens())\r
141         {\r
142           if ( (field = st.nextToken(":")) != null)\r
143           {\r
144             // validate residue field value\r
145             if (Types[i] == 1)\r
146             {\r
147               resCode val = validResidueCode(field);\r
148               if (val != null)\r
149               {\r
150                 fields.put(new String(Fields[i] + "num"), val);\r
151               }\r
152               else\r
153               {\r
154                 //      jalview.bin.Cache.log.debug(\r
155                 //           "Ignoring non-Modeller description: invalid integer-like field '" + field + "'");\r
156                 type = -1; /* invalid field! - throw the FieldSet away */\r
157               }\r
158               ;\r
159             }\r
160             fields.put(Fields[i++], field);\r
161           }\r
162         }\r
163         if (i == TAIL)\r
164         {\r
165           // slurp remaining fields\r
166           while (st.hasMoreTokens())\r
167           {\r
168             field += ":" + st.nextToken(":");\r
169           }\r
170           fields.put(Fields[TAIL], field);\r
171         }\r
172       }\r
173     }\r
174     if (type == -1)\r
175     {\r
176       // object is not a proper ModellerPIR object\r
177       fields = new java.util.Hashtable();\r
178       fields.put(Fields[TAIL], new String(desc));\r
179     }\r
180     else\r
181     {\r
182       fields.put(Fields[TYPE], seqTypes[type]);\r
183     }\r
184     return fields;\r
185   }\r
186 \r
187   ModellerDescription(String desc)\r
188   {\r
189     if (desc == null)\r
190     {\r
191       desc = "";\r
192     }\r
193     fields = parseDescription(desc);\r
194   }\r
195 \r
196   void setStartCode(int v)\r
197   {\r
198     resCode r;\r
199     fields.put(Fields[START] + "num", r = new resCode(v));\r
200     fields.put(Fields[START], r.field);\r
201   }\r
202 \r
203   void setEndCode(int v)\r
204   {\r
205     resCode r;\r
206     fields.put(Fields[END] + "num", r = new resCode(v));\r
207     fields.put(Fields[END], r.field);\r
208   }\r
209 \r
210   /**\r
211    * make a possibly updated modeller field line for the sequence object\r
212    * @param seq SequenceI\r
213    */\r
214   ModellerDescription(SequenceI seq)\r
215   {\r
216 \r
217     if (seq.getDescription() != null)\r
218     {\r
219       fields = parseDescription(seq.getDescription());\r
220     }\r
221 \r
222     if (isModellerFieldset())\r
223     {\r
224       // Set start and end before we update the type (in the case of a synthesized field set)\r
225       if (getStartNum() != seq.getStart() && getStartCode().val != null)\r
226       {\r
227         setStartCode(seq.getStart());\r
228       }\r
229 \r
230       if (getEndNum() != seq.getEnd() && getStartCode().val != null)\r
231       {\r
232         setEndCode(seq.getEnd());\r
233       }\r
234     }\r
235     else\r
236     {\r
237       // synthesize fields\r
238       setStartCode(seq.getStart());\r
239       setEndCode(seq.getEnd());\r
240       fields.put(Fields[LOCALID], seq.getName()); // this may be overwritten below...\r
241       // type - decide based on evidence of PDB database references - this also sets the local reference field\r
242       int t = 0; // sequence\r
243       if (seq.getDatasetSequence() != null &&\r
244           seq.getDatasetSequence().getDBRef() != null)\r
245       {\r
246         jalview.datamodel.DBRefEntry[] dbr = seq.getDatasetSequence().getDBRef();\r
247         int i, j;\r
248         for (i = 0, j = dbr.length; i < j; i++)\r
249         {\r
250           if (dbr[i] != null)\r
251           {\r
252             // JBPNote PDB dbRefEntry needs properties to propagate onto ModellerField\r
253             // JBPNote Need to get info from the user about whether the sequence is the one being modelled, or if it is a template.\r
254             if (dbr[i].getSource().equals(jalview.datamodel.DBRefSource.PDB))\r
255             {\r
256               fields.put(Fields[LOCALID], dbr[i].getAccessionId());\r
257               t = 2;\r
258               break;\r
259             }\r
260           }\r
261         }\r
262       }\r
263       fields.put(Fields[TYPE], seqTypes[t]);\r
264     }\r
265 \r
266   }\r
267 \r
268   /**\r
269    * Indicate if fields parsed to a modeller-like colon-separated value line\r
270    * @return boolean\r
271    */\r
272   boolean isModellerFieldset()\r
273   {\r
274     return (fields.containsKey(Fields[TYPE]));\r
275   }\r
276 \r
277   String getDescriptionLine()\r
278   {\r
279     String desc = "";\r
280     int lastfield = Fields.length - 1;\r
281 \r
282     if (isModellerFieldset())\r
283     {\r
284       String value;\r
285       // try to write a minimal modeller field set, so..\r
286 \r
287       // find the last valid field in the entry\r
288 \r
289       for (; lastfield > 6; lastfield--)\r
290       {\r
291         if (fields.containsKey(Fields[lastfield]))\r
292         {\r
293           break;\r
294         }\r
295       }\r
296 \r
297       for (int i = 0; i < lastfield; i++)\r
298       {\r
299         value = (String) fields.get(Fields[i]);\r
300         if (value != null && value.length() > 0)\r
301         {\r
302           desc += ( (String) fields.get(Fields[i])) + ":";\r
303         }\r
304         else\r
305         {\r
306           desc += Padding[i] + ":";\r
307         }\r
308       }\r
309     }\r
310     // just return the last field if no others were defined.\r
311     if (fields.containsKey(Fields[lastfield]))\r
312     {\r
313       desc += (String) fields.get(Fields[lastfield]);\r
314     }\r
315     else\r
316     {\r
317       desc += ".";\r
318     }\r
319     return desc;\r
320   }\r
321 \r
322   int getStartNum()\r
323   {\r
324     int start = 0;\r
325     resCode val = getStartCode();\r
326     if (val.val != null)\r
327     {\r
328       return val.val.intValue();\r
329     }\r
330     return start;\r
331   }\r
332 \r
333   resCode getStartCode()\r
334   {\r
335     if (isModellerFieldset() && fields.containsKey(Fields[START] + "num"))\r
336     {\r
337       return (resCode) fields.get(Fields[START] + "num");\r
338     }\r
339     return null;\r
340   }\r
341 \r
342   resCode getEndCode()\r
343   {\r
344     if (isModellerFieldset() && fields.containsKey(Fields[END] + "num"))\r
345     {\r
346       return (resCode) fields.get(Fields[END] + "num");\r
347     }\r
348     return null;\r
349   }\r
350 \r
351   int getEndNum()\r
352   {\r
353     int end = 0;\r
354     resCode val = getEndCode();\r
355     if (val.val != null)\r
356     {\r
357       return val.val.intValue();\r
358     }\r
359     return end;\r
360   }\r
361 \r
362   /**\r
363    * returns true if sequence object was modifed with a valid modellerField set\r
364    * @param newSeq SequenceI\r
365    * @return boolean\r
366    */\r
367   boolean updateSequenceI(SequenceI newSeq)\r
368   {\r
369     if (isModellerFieldset())\r
370     {\r
371       if (getStartCode().val != null)\r
372       {\r
373         newSeq.setStart(getStartNum());\r
374       }\r
375       else\r
376       {\r
377         newSeq.setStart(1);\r
378       }\r
379       if (getEndCode().val != null)\r
380       {\r
381         newSeq.setEnd(getEndNum());\r
382       }\r
383       else\r
384       {\r
385         newSeq.setEnd(newSeq.getStart() + newSeq.getLength());\r
386       }\r
387       return true;\r
388     }\r
389     return false;\r
390   }\r
391 }\r