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