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