JAL-1745, JAL-1717 improvement to ensure cached PDB entries are unique within a seque...
[jalview.git] / src / MCview / PDBfile.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package MCview;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.datamodel.Alignment;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.AlignmentI;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.SequenceI;
29 import jalview.io.FileParse;
30 import jalview.util.MessageManager;
31
32 import java.awt.Color;
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.Hashtable;
36 import java.util.List;
37 import java.util.Vector;
38
39 public class PDBfile extends jalview.io.AlignFile
40 {
41   public Vector<PDBChain> chains;
42
43   public String id;
44
45   /**
46    * set to true to add chain alignment annotation as visible annotation.
47    */
48   boolean VisibleChainAnnotation = false;
49
50   boolean processSecondaryStructure = true;
51
52   boolean externalSecondaryStructure = false;
53
54   public PDBfile(boolean visibleChainAnnotation,
55           boolean processSecondaryStructure, boolean externalSecStr)
56   {
57     super();
58     VisibleChainAnnotation = visibleChainAnnotation;
59     this.processSecondaryStructure = processSecondaryStructure;
60     this.externalSecondaryStructure = externalSecStr;
61   }
62
63   public PDBfile(boolean visibleChainAnnotation,
64           boolean processSecondaryStructure, boolean externalSecStr,
65           String file, String protocol) throws IOException
66   {
67     super(false, file, protocol);
68     VisibleChainAnnotation = visibleChainAnnotation;
69     this.processSecondaryStructure = processSecondaryStructure;
70     this.externalSecondaryStructure = externalSecStr;
71     doParse();
72   }
73
74   public PDBfile(boolean visibleChainAnnotation,
75           boolean processSecondaryStructure, boolean externalSecStr,
76           FileParse source) throws IOException
77   {
78     super(false, source);
79     VisibleChainAnnotation = visibleChainAnnotation;
80     this.processSecondaryStructure = processSecondaryStructure;
81     this.externalSecondaryStructure = externalSecStr;
82     doParse();
83   }
84
85   public String print()
86   {
87     return null;
88   }
89
90   public void parse() throws IOException
91   {
92     // TODO set the filename sensibly - try using data source name.
93     id = safeName(getDataName());
94
95     chains = new Vector();
96     ArrayList<SequenceI> rna = new ArrayList<SequenceI>(), prot = new ArrayList<SequenceI>();
97     PDBChain tmpchain;
98     String line = null;
99     boolean modelFlag = false;
100     boolean terFlag = false;
101     String lastID = "";
102
103     int index = 0;
104     String atomnam = null;
105     try
106     {
107       while ((line = nextLine()) != null)
108       {
109         if (line.indexOf("HEADER") == 0)
110         {
111           if (line.length() > 62)
112           {
113             String tid;
114             if (line.length() > 67)
115             {
116               tid = line.substring(62, 67).trim();
117             }
118             else
119             {
120               tid = line.substring(62).trim();
121             }
122             if (tid.length() > 0)
123             {
124               id = tid;
125             }
126             continue;
127           }
128         }
129         // Were we to do anything with SEQRES - we start it here
130         if (line.indexOf("SEQRES") == 0)
131         {
132         }
133
134         if (line.indexOf("MODEL") == 0)
135         {
136           modelFlag = true;
137         }
138
139         if (line.indexOf("TER") == 0)
140         {
141           terFlag = true;
142         }
143
144         if (modelFlag && line.indexOf("ENDMDL") == 0)
145         {
146           break;
147         }
148         if (line.indexOf("ATOM") == 0
149                 || (line.indexOf("HETATM") == 0 && !terFlag))
150         {
151           terFlag = false;
152
153           // Jalview is only interested in CA bonds????
154           atomnam = line.substring(12, 15).trim();
155           if (!atomnam.equals("CA") && !atomnam.equals("P"))
156           {
157             continue;
158           }
159
160           Atom tmpatom = new Atom(line);
161           tmpchain = findChain(tmpatom.chain);
162           if (tmpchain != null)
163           {
164             if (tmpatom.resNumIns.trim().equals(lastID))
165             {
166               // phosphorylated protein - seen both CA and P..
167               continue;
168             }
169             tmpchain.atoms.addElement(tmpatom);
170           }
171           else
172           {
173             tmpchain = new PDBChain(id, tmpatom.chain);
174             chains.addElement(tmpchain);
175             tmpchain.atoms.addElement(tmpatom);
176           }
177           lastID = tmpatom.resNumIns.trim();
178         }
179         index++;
180       }
181
182       makeResidueList();
183       makeCaBondList();
184
185       if (id == null)
186       {
187         id = inFile.getName();
188       }
189       for (int i = 0; i < chains.size(); i++)
190       {
191         SequenceI dataset = chains.elementAt(i).sequence;
192         dataset.setName(id + "|" + dataset.getName());
193         PDBEntry entry = new PDBEntry();
194         entry.setId(id);
195         entry.setType(PDBEntry.Type.PDB);
196         entry.setProperty(new Hashtable());
197         if (chains.elementAt(i).id != null)
198         {
199           // entry.getProperty().put("CHAIN", chains.elementAt(i).id);
200           entry.setChainCode(String.valueOf(chains.elementAt(i).id));
201         }
202         if (inFile != null)
203         {
204           entry.setFile(inFile.getAbsolutePath());
205         }
206         else
207         {
208           // TODO: decide if we should dump the datasource to disk
209           entry.setFile(getDataName());
210         }
211         dataset.addPDBId(entry);
212         SequenceI chainseq = dataset.deriveSequence(); // PDBChain objects
213         // maintain reference to
214         // dataset
215         seqs.addElement(chainseq);
216         if (isRNA(chainseq) == true)
217         {
218           rna.add(chainseq);
219         }
220         else
221         {
222           prot.add(chainseq);
223         }
224
225         AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
226
227         if (chainannot != null && VisibleChainAnnotation)
228         {
229           for (int ai = 0; ai < chainannot.length; ai++)
230           {
231             chainannot[ai].visible = VisibleChainAnnotation;
232             annotations.addElement(chainannot[ai]);
233           }
234         }
235       }
236       if (processSecondaryStructure)
237       {
238         if (externalSecondaryStructure && rna.size() > 0)
239         {
240           try
241           {
242             processPdbFileWithAnnotate3d(rna);
243           } catch (Exception x)
244           {
245             System.err
246                     .println("Exceptions when dealing with RNA in pdb file");
247             x.printStackTrace();
248
249           }
250         }
251         ;
252         if (prot.size() > 0)
253         {
254           try
255           {
256             processPdbFileWithJmol(prot);
257           } catch (Exception x)
258           {
259             System.err
260                     .println("Exceptions from Jmol when processing data in pdb file");
261             x.printStackTrace();
262
263           }
264         }
265       }
266     } catch (OutOfMemoryError er)
267     {
268       System.out.println("OUT OF MEMORY LOADING PDB FILE");
269       throw new IOException(
270               MessageManager
271                       .getString("exception.outofmemory_loading_pdb_file"));
272     } catch (NumberFormatException ex)
273     {
274       if (line != null)
275       {
276         System.err.println("Couldn't read number from line:");
277         System.err.println(line);
278       }
279     }
280     markCalcIds();
281   }
282
283   private static String calcIdPrefix = "JalviewPDB";
284
285   public static boolean isCalcIdHandled(String calcId)
286   {
287     return calcId != null && (calcIdPrefix.equals(calcId));
288   }
289
290   public static boolean isCalcIdForFile(AlignmentAnnotation alan,
291           String pdbFile)
292   {
293     return alan.getCalcId() != null
294             && calcIdPrefix.equals(alan.getCalcId())
295             && pdbFile.equals(alan.getProperty("PDBID"));
296   }
297
298   public static String relocateCalcId(String calcId,
299           Hashtable<String, String> alreadyLoadedPDB) throws Exception
300   {
301     int s = calcIdPrefix.length(), end = calcId.indexOf(calcIdPrefix, s);
302     String between = calcId.substring(s, end - 1);
303     return calcIdPrefix + alreadyLoadedPDB.get(between) + ":"
304             + calcId.substring(end);
305   }
306
307   private void markCalcIds()
308   {
309     for (SequenceI sq : seqs)
310     {
311       if (sq.getAnnotation() != null)
312       {
313         for (AlignmentAnnotation aa : sq.getAnnotation())
314         {
315           String oldId = aa.getCalcId();
316           if (oldId == null)
317           {
318             oldId = "";
319           }
320           aa.setCalcId(calcIdPrefix);
321           aa.setProperty("PDBID", id);
322           aa.setProperty("oldCalcId", oldId);
323         }
324       }
325     }
326   }
327
328   private void processPdbFileWithJmol(ArrayList<SequenceI> prot)
329           throws Exception
330   {
331     try
332     {
333       Class cl = Class.forName("jalview.ext.jmol.PDBFileWithJmol");
334       if (cl != null)
335       {
336         Object jmf = cl.getConstructor(new Class[]
337         { FileParse.class }).newInstance(new Object[]
338         { new FileParse(getDataName(), type) });
339         Alignment al = new Alignment((SequenceI[]) cl.getMethod(
340                 "getSeqsAsArray", new Class[]
341                 {}).invoke(jmf));
342         cl.getMethod("addAnnotations", new Class[]
343         { Alignment.class }).invoke(jmf, al);
344         for (SequenceI sq : al.getSequences())
345         {
346           if (sq.getDatasetSequence() != null)
347           {
348             sq.getDatasetSequence().getPDBId().clear();
349           }
350           else
351           {
352             sq.getPDBId().clear();
353           }
354         }
355         replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
356       }
357     } catch (ClassNotFoundException q)
358     {
359     }
360   }
361
362   private void replaceAndUpdateChains(ArrayList<SequenceI> prot,
363           AlignmentI al, String pep, boolean b)
364   {
365     List<List<? extends Object>> replaced = AlignSeq
366             .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
367                     false);
368     for (PDBChain ch : chains)
369     {
370       int p = 0;
371       for (SequenceI sq : (List<SequenceI>) replaced.get(0))
372       {
373         p++;
374         if (sq == ch.sequence || sq.getDatasetSequence() == ch.sequence)
375         {
376           p = -p;
377           break;
378         }
379       }
380       if (p < 0)
381       {
382         p = -p - 1;
383         // set shadow entry for chains
384         ch.shadow = (SequenceI) replaced.get(1).get(p);
385         ch.shadowMap = ((AlignSeq) replaced.get(2).get(p))
386                 .getMappingFromS1(false);
387       }
388     }
389   }
390
391   private void processPdbFileWithAnnotate3d(ArrayList<SequenceI> rna)
392           throws Exception
393   {
394     // System.out.println("this is a PDB format and RNA sequence");
395     // note: we use reflection here so that the applet can compile and run
396     // without the HTTPClient bits and pieces needed for accessing Annotate3D
397     // web service
398     try
399     {
400       Class cl = Class.forName("jalview.ws.jws1.Annotate3D");
401       if (cl != null)
402       {
403         // TODO: use the PDB ID of the structure if one is available, to save
404         // bandwidth and avoid uploading the whole structure to the service
405         Object annotate3d = cl.getConstructor(new Class[]
406         {}).newInstance(new Object[]
407         {});
408         AlignmentI al = ((AlignmentI) cl.getMethod("getRNAMLFor",
409                 new Class[]
410                 { FileParse.class }).invoke(annotate3d, new Object[]
411         { new FileParse(getDataName(), type) }));
412         for (SequenceI sq : al.getSequences())
413         {
414           if (sq.getDatasetSequence() != null)
415           {
416             if (sq.getDatasetSequence().getPDBId() != null)
417             {
418               sq.getDatasetSequence().getPDBId().clear();
419             }
420           }
421           else
422           {
423             if (sq.getPDBId() != null)
424             {
425               sq.getPDBId().clear();
426             }
427           }
428         }
429         replaceAndUpdateChains(rna, al, AlignSeq.DNA, false);
430       }
431     } catch (ClassNotFoundException x)
432     {
433       // ignore classnotfounds - occurs in applet
434     }
435     ;
436   }
437
438   /**
439    * make a friendly ID string.
440    * 
441    * @param dataName
442    * @return truncated dataName to after last '/'
443    */
444   private String safeName(String dataName)
445   {
446     int p = 0;
447     while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
448     {
449       dataName = dataName.substring(p + 1);
450     }
451     return dataName;
452   }
453
454   public void makeResidueList()
455   {
456     for (int i = 0; i < chains.size(); i++)
457     {
458       chains.elementAt(i).makeResidueList(VisibleChainAnnotation);
459     }
460   }
461
462   public void makeCaBondList()
463   {
464     for (int i = 0; i < chains.size(); i++)
465     {
466       chains.elementAt(i).makeCaBondList();
467     }
468   }
469
470   public PDBChain findChain(String id)
471   {
472     for (int i = 0; i < chains.size(); i++)
473     {
474       if (chains.elementAt(i).id.equals(id))
475       {
476         return chains.elementAt(i);
477       }
478     }
479
480     return null;
481   }
482
483   public void setChargeColours()
484   {
485     for (int i = 0; i < chains.size(); i++)
486     {
487       chains.elementAt(i).setChargeColours();
488     }
489   }
490
491   public void setColours(jalview.schemes.ColourSchemeI cs)
492   {
493     for (int i = 0; i < chains.size(); i++)
494     {
495       chains.elementAt(i).setChainColours(cs);
496     }
497   }
498
499   public void setChainColours()
500   {
501     for (int i = 0; i < chains.size(); i++)
502     {
503       chains.elementAt(i).setChainColours(
504               Color.getHSBColor(1.0f / i, .4f, 1.0f));
505     }
506   }
507
508   public boolean isRNA(SequenceI seqs)
509   {
510     for (int i = 0; i < seqs.getLength(); i++)
511     {
512       if ((seqs.getCharAt(i) != 'A') && (seqs.getCharAt(i) != 'C')
513               && (seqs.getCharAt(i) != 'G') && (seqs.getCharAt(i) != 'U'))
514       {
515         return false;
516       }
517     }
518
519     return true;
520
521   }
522 }