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