seq size cant be less than 2 for tree
[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         jalview.datamodel.DBRefEntry [] dbr = seq.getDatasetSequence().getDBRef();\r
228         int i, j;\r
229         for (i = 0, j = dbr.length; i < j; i++)\r
230         {\r
231           if (dbr[i] != null)\r
232           {\r
233             // JBPNote PDB dbRefEntry needs properties to propagate onto ModellerField\r
234             // JBPNote Need to get info from the user about whether the sequence is the one being modelled, or if it is a template.\r
235             if (dbr[i].getSource().equals(jalview.datamodel.DBRefSource.PDB))\r
236             {\r
237               fields.put(Fields[LOCALID], dbr[i].getAccessionId());\r
238               t = 2;\r
239               break;\r
240             }\r
241           }\r
242         }\r
243       }\r
244       fields.put(Fields[TYPE], seqTypes[t]);\r
245     }\r
246 \r
247   }\r
248 \r
249   /**\r
250    * Indicate if fields parsed to a modeller-like colon-separated value line\r
251    * @return boolean\r
252    */\r
253   boolean isModellerFieldset()\r
254   {\r
255     return (fields.containsKey(Fields[TYPE]));\r
256   }\r
257 \r
258   String getDescriptionLine()\r
259   {\r
260     String desc = "";\r
261     int lastfield = Fields.length - 1;\r
262 \r
263     if (isModellerFieldset())\r
264     {\r
265       String value;\r
266       // try to write a minimal modeller field set, so..\r
267 \r
268       // find the last valid field in the entry\r
269 \r
270       for (; lastfield > 6; lastfield--)\r
271       {\r
272         if (fields.containsKey(Fields[lastfield]))\r
273         {\r
274           break;\r
275         }\r
276       }\r
277 \r
278       for (int i = 0; i < lastfield; i++)\r
279       {\r
280         value = (String) fields.get(Fields[i]);\r
281         if (value != null && value.length() > 0)\r
282         {\r
283           desc += ( (String) fields.get(Fields[i])) + ":";\r
284         }\r
285         else\r
286         {\r
287           desc += Padding[i] + ":";\r
288         }\r
289       }\r
290     }\r
291     // just return the last field if no others were defined.\r
292     if (fields.containsKey(Fields[lastfield]))\r
293     {\r
294       desc += (String) fields.get(Fields[lastfield]);\r
295     }\r
296     else\r
297     {\r
298       desc += ".";\r
299     }\r
300     return desc;\r
301   }\r
302 \r
303   int getStartNum()\r
304   {\r
305     int start = 0;\r
306     resCode val = getStartCode();\r
307     if (val.val != null)\r
308     {\r
309       return val.val.intValue();\r
310     }\r
311     return start;\r
312   }\r
313 \r
314   resCode getStartCode()\r
315   {\r
316     if (isModellerFieldset() && fields.containsKey(Fields[START] + "num"))\r
317     {\r
318       return (resCode) fields.get(Fields[START] + "num");\r
319     }\r
320     return null;\r
321   }\r
322 \r
323   resCode getEndCode()\r
324   {\r
325     if (isModellerFieldset() && fields.containsKey(Fields[END] + "num"))\r
326     {\r
327       return (resCode) fields.get(Fields[END] + "num");\r
328     }\r
329     return null;\r
330   }\r
331 \r
332   int getEndNum()\r
333   {\r
334     int end = 0;\r
335     resCode val = getEndCode();\r
336     if (val.val != null)\r
337     {\r
338       return val.val.intValue();\r
339     }\r
340     return end;\r
341   }\r
342 \r
343   /**\r
344    * returns true if sequence object was modifed with a valid modellerField set\r
345    * @param newSeq SequenceI\r
346    * @return boolean\r
347    */\r
348   boolean updateSequenceI(SequenceI newSeq)\r
349   {\r
350     if (isModellerFieldset())\r
351     {\r
352       if (getStartCode().val != null)\r
353       {\r
354         newSeq.setStart(getStartNum());\r
355       }\r
356       else\r
357       {\r
358         newSeq.setStart(1);\r
359       }\r
360       if (getEndCode().val != null)\r
361       {\r
362         newSeq.setEnd(getEndNum());\r
363       }\r
364       else\r
365       {\r
366         newSeq.setEnd(newSeq.getStart() + newSeq.getLength());\r
367       }\r
368       return true;\r
369     }\r
370     return false;\r
371   }\r
372 }\r
373 \r