AnnotationId is hashcode of object
[jalview.git] / src / jalview / io / ModellerDescription.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 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.SequenceI;\r
22 import java.util.Vector;\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("\\s*((([-0-9]+).?)|FIRST|LAST|@)");\r
91 \r
92     if (!r.search(field))\r
93     {\r
94       return null; // invalid\r
95     }\r
96     String value = r.stringMatched(3);\r
97     if (value == null)\r
98     {\r
99       value = r.stringMatched(1);\r
100     }\r
101    // jalview.bin.Cache.log.debug("from '" + field + "' matched '" + value +\r
102    //                             "'");\r
103     try\r
104     {\r
105       val = Integer.valueOf(value);\r
106       return new resCode(field, val); // successful numeric extraction\r
107     }\r
108     catch (Exception e)\r
109     {\r
110     }\r
111     return new resCode(field, null);\r
112   }\r
113 \r
114   private java.util.Hashtable parseDescription(String desc)\r
115   {\r
116     java.util.Hashtable fields = new java.util.Hashtable();\r
117     java.util.StringTokenizer st = new java.util.StringTokenizer(desc, ":");\r
118     String field;\r
119     int type = -1;\r
120     if (st.countTokens() > 0)\r
121     {\r
122       // parse colon-fields\r
123       int i = 0;\r
124       field = st.nextToken(":");\r
125       do\r
126       {\r
127         if (seqTypes[i].equalsIgnoreCase(field) )\r
128         {\r
129           break;\r
130         }\r
131       }\r
132       while (++i < seqTypes.length);\r
133 \r
134       if (i < seqTypes.length)\r
135       {\r
136         // valid seqType for modeller\r
137         type = i;\r
138         i = 1; // continue parsing fields\r
139         while (i < TAIL && st.hasMoreTokens())\r
140         {\r
141           if ( (field = st.nextToken(":")) != null)\r
142           {\r
143             // validate residue field value\r
144             if (Types[i] == 1)\r
145             {\r
146               resCode val = validResidueCode(field);\r
147               if (val != null)\r
148               {\r
149                 fields.put(new String(Fields[i] + "num"), val);\r
150               }\r
151               else\r
152               {\r
153           //      jalview.bin.Cache.log.debug(\r
154          //           "Ignoring non-Modeller description: invalid integer-like field '" + field + "'");\r
155                 type = -1; /* invalid field! - throw the FieldSet away */\r
156               }\r
157               ;\r
158             }\r
159             fields.put(Fields[i++], field);\r
160           }\r
161         }\r
162         if (i == TAIL)\r
163         {\r
164           // slurp remaining fields\r
165           while (st.hasMoreTokens())\r
166           {\r
167             field += ":" + st.nextToken(":");\r
168           }\r
169           fields.put(Fields[TAIL], field);\r
170         }\r
171       }\r
172     }\r
173     if (type == -1)\r
174     {\r
175       // object is not a proper ModellerPIR object\r
176       fields = new java.util.Hashtable();\r
177       fields.put(Fields[TAIL], new String(desc));\r
178     }\r
179     else\r
180     {\r
181       fields.put(Fields[TYPE], seqTypes[type]);\r
182     }\r
183     return fields;\r
184   }\r
185 \r
186   ModellerDescription(String desc)\r
187   {\r
188     if (desc == null)\r
189     {\r
190       desc = "";\r
191     }\r
192     fields = parseDescription(desc);\r
193   }\r
194 \r
195   void setStartCode(int v)\r
196   {\r
197     resCode r;\r
198     fields.put(Fields[START] + "num", r = new resCode(v));\r
199     fields.put(Fields[START], r.field);\r
200   }\r
201 \r
202   void setEndCode(int v)\r
203   {\r
204     resCode r;\r
205     fields.put(Fields[END] + "num", r = new resCode(v));\r
206     fields.put(Fields[END], r.field);\r
207   }\r
208 \r
209   /**\r
210    * make a possibly updated modeller field line for the sequence object\r
211    * @param seq SequenceI\r
212    */\r
213   ModellerDescription(SequenceI seq)\r
214   {\r
215 \r
216     if (seq.getDescription() != null)\r
217     {\r
218       fields = parseDescription(seq.getDescription());\r
219     }\r
220 \r
221     if (isModellerFieldset())\r
222     {\r
223       // Set start and end before we update the type (in the case of a synthesized field set)\r
224       if (getStartNum() != seq.getStart() && getStartCode().val != null)\r
225       {\r
226         setStartCode(seq.getStart());\r
227       }\r
228 \r
229       if (getEndNum() != seq.getEnd() && getStartCode().val != null)\r
230       {\r
231         setEndCode(seq.getEnd());\r
232       }\r
233     }\r
234     else\r
235     {\r
236       // synthesize fields\r
237       setStartCode(seq.getStart());\r
238       setEndCode(seq.getEnd());\r
239       fields.put(Fields[LOCALID], seq.getName()); // this may be overwritten below...\r
240       // type - decide based on evidence of PDB database references - this also sets the local reference field\r
241       int t = 0; // sequence\r
242       if (seq.getDatasetSequence() != null &&\r
243           seq.getDatasetSequence().getDBRef() != null)\r
244       {\r
245         jalview.datamodel.DBRefEntry [] dbr = seq.getDatasetSequence().getDBRef();\r
246         int i, j;\r
247         for (i = 0, j = dbr.length; i < j; i++)\r
248         {\r
249           if (dbr[i] != null)\r
250           {\r
251             // JBPNote PDB dbRefEntry needs properties to propagate onto ModellerField\r
252             // JBPNote Need to get info from the user about whether the sequence is the one being modelled, or if it is a template.\r
253             if (dbr[i].getSource().equals(jalview.datamodel.DBRefSource.PDB))\r
254             {\r
255               fields.put(Fields[LOCALID], dbr[i].getAccessionId());\r
256               t = 2;\r
257               break;\r
258             }\r
259           }\r
260         }\r
261       }\r
262       fields.put(Fields[TYPE], seqTypes[t]);\r
263     }\r
264 \r
265   }\r
266 \r
267   /**\r
268    * Indicate if fields parsed to a modeller-like colon-separated value line\r
269    * @return boolean\r
270    */\r
271   boolean isModellerFieldset()\r
272   {\r
273     return (fields.containsKey(Fields[TYPE]));\r
274   }\r
275 \r
276   String getDescriptionLine()\r
277   {\r
278     String desc = "";\r
279     int lastfield = Fields.length - 1;\r
280 \r
281     if (isModellerFieldset())\r
282     {\r
283       String value;\r
284       // try to write a minimal modeller field set, so..\r
285 \r
286       // find the last valid field in the entry\r
287 \r
288       for (; lastfield > 6; lastfield--)\r
289       {\r
290         if (fields.containsKey(Fields[lastfield]))\r
291         {\r
292           break;\r
293         }\r
294       }\r
295 \r
296       for (int i = 0; i < lastfield; i++)\r
297       {\r
298         value = (String) fields.get(Fields[i]);\r
299         if (value != null && value.length() > 0)\r
300         {\r
301           desc += ( (String) fields.get(Fields[i])) + ":";\r
302         }\r
303         else\r
304         {\r
305           desc += Padding[i] + ":";\r
306         }\r
307       }\r
308     }\r
309     // just return the last field if no others were defined.\r
310     if (fields.containsKey(Fields[lastfield]))\r
311     {\r
312       desc += (String) fields.get(Fields[lastfield]);\r
313     }\r
314     else\r
315     {\r
316       desc += ".";\r
317     }\r
318     return desc;\r
319   }\r
320 \r
321   int getStartNum()\r
322   {\r
323     int start = 0;\r
324     resCode val = getStartCode();\r
325     if (val.val != null)\r
326     {\r
327       return val.val.intValue();\r
328     }\r
329     return start;\r
330   }\r
331 \r
332   resCode getStartCode()\r
333   {\r
334     if (isModellerFieldset() && fields.containsKey(Fields[START] + "num"))\r
335     {\r
336       return (resCode) fields.get(Fields[START] + "num");\r
337     }\r
338     return null;\r
339   }\r
340 \r
341   resCode getEndCode()\r
342   {\r
343     if (isModellerFieldset() && fields.containsKey(Fields[END] + "num"))\r
344     {\r
345       return (resCode) fields.get(Fields[END] + "num");\r
346     }\r
347     return null;\r
348   }\r
349 \r
350   int getEndNum()\r
351   {\r
352     int end = 0;\r
353     resCode val = getEndCode();\r
354     if (val.val != null)\r
355     {\r
356       return val.val.intValue();\r
357     }\r
358     return end;\r
359   }\r
360 \r
361   /**\r
362    * returns true if sequence object was modifed with a valid modellerField set\r
363    * @param newSeq SequenceI\r
364    * @return boolean\r
365    */\r
366   boolean updateSequenceI(SequenceI newSeq)\r
367   {\r
368     if (isModellerFieldset())\r
369     {\r
370       if (getStartCode().val != null)\r
371       {\r
372         newSeq.setStart(getStartNum());\r
373       }\r
374       else\r
375       {\r
376         newSeq.setStart(1);\r
377       }\r
378       if (getEndCode().val != null)\r
379       {\r
380         newSeq.setEnd(getEndNum());\r
381       }\r
382       else\r
383       {\r
384         newSeq.setEnd(newSeq.getStart() + newSeq.getLength());\r
385       }\r
386       return true;\r
387     }\r
388     return false;\r
389   }\r
390 }\r
391 \r