940e27b889ff44b758f8522bec700fac03f0fbde
[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.setProperty(new Hashtable());
196         if (chains.elementAt(i).id != null)
197         {
198           // entry.getProperty().put("CHAIN", chains.elementAt(i).id);
199           entry.setChainCode(String.valueOf(chains.elementAt(i).id));
200         }
201         if (inFile != null)
202         {
203           entry.setFile(inFile.getAbsolutePath());
204         }
205         else
206         {
207           // TODO: decide if we should dump the datasource to disk
208           entry.setFile(getDataName());
209         }
210         dataset.addPDBId(entry);
211         SequenceI chainseq = dataset.deriveSequence(); // PDBChain objects
212         // maintain reference to
213         // dataset
214         seqs.addElement(chainseq);
215         if (isRNA(chainseq) == true)
216         {
217           rna.add(chainseq);
218         }
219         else
220         {
221           prot.add(chainseq);
222         }
223
224         AlignmentAnnotation[] chainannot = chainseq.getAnnotation();
225
226         if (chainannot != null && VisibleChainAnnotation)
227         {
228           for (int ai = 0; ai < chainannot.length; ai++)
229           {
230             chainannot[ai].visible = VisibleChainAnnotation;
231             annotations.addElement(chainannot[ai]);
232           }
233         }
234       }
235       if (processSecondaryStructure)
236       {
237         if (externalSecondaryStructure && rna.size() > 0)
238         {
239           try
240           {
241             processPdbFileWithAnnotate3d(rna);
242           } catch (Exception x)
243           {
244             System.err
245                     .println("Exceptions when dealing with RNA in pdb file");
246             x.printStackTrace();
247
248           }
249         }
250         ;
251         if (prot.size() > 0)
252         {
253           try
254           {
255             processPdbFileWithJmol(prot);
256           } catch (Exception x)
257           {
258             System.err
259                     .println("Exceptions from Jmol when processing data in pdb file");
260             x.printStackTrace();
261
262           }
263         }
264       }
265     } catch (OutOfMemoryError er)
266     {
267       System.out.println("OUT OF MEMORY LOADING PDB FILE");
268       throw new IOException(
269               MessageManager
270                       .getString("exception.outofmemory_loading_pdb_file"));
271     } catch (NumberFormatException ex)
272     {
273       if (line != null)
274       {
275         System.err.println("Couldn't read number from line:");
276         System.err.println(line);
277       }
278     }
279     markCalcIds();
280   }
281
282   private static String calcIdPrefix = "JalviewPDB";
283
284   public static boolean isCalcIdHandled(String calcId)
285   {
286     return calcId != null && (calcIdPrefix.equals(calcId));
287   }
288
289   public static boolean isCalcIdForFile(AlignmentAnnotation alan,
290           String pdbFile)
291   {
292     return alan.getCalcId() != null
293             && calcIdPrefix.equals(alan.getCalcId())
294             && pdbFile.equals(alan.getProperty("PDBID"));
295   }
296
297   public static String relocateCalcId(String calcId,
298           Hashtable<String, String> alreadyLoadedPDB) throws Exception
299   {
300     int s = calcIdPrefix.length(), end = calcId.indexOf(calcIdPrefix, s);
301     String between = calcId.substring(s, end - 1);
302     return calcIdPrefix + alreadyLoadedPDB.get(between) + ":"
303             + calcId.substring(end);
304   }
305
306   private void markCalcIds()
307   {
308     for (SequenceI sq : seqs)
309     {
310       if (sq.getAnnotation() != null)
311       {
312         for (AlignmentAnnotation aa : sq.getAnnotation())
313         {
314           String oldId = aa.getCalcId();
315           if (oldId == null)
316           {
317             oldId = "";
318           }
319           aa.setCalcId(calcIdPrefix);
320           aa.setProperty("PDBID", id);
321           aa.setProperty("oldCalcId", oldId);
322         }
323       }
324     }
325   }
326
327   private void processPdbFileWithJmol(ArrayList<SequenceI> prot)
328           throws Exception
329   {
330     try
331     {
332       Class cl = Class.forName("jalview.ext.jmol.PDBFileWithJmol");
333       if (cl != null)
334       {
335         Object jmf = cl.getConstructor(new Class[]
336         { FileParse.class }).newInstance(new Object[]
337         { new FileParse(getDataName(), type) });
338         Alignment al = new Alignment((SequenceI[]) cl.getMethod(
339                 "getSeqsAsArray", new Class[]
340                 {}).invoke(jmf));
341         cl.getMethod("addAnnotations", new Class[]
342         { Alignment.class }).invoke(jmf, al);
343         for (SequenceI sq : al.getSequences())
344         {
345           if (sq.getDatasetSequence() != null)
346           {
347             sq.getDatasetSequence().getPDBId().clear();
348           }
349           else
350           {
351             sq.getPDBId().clear();
352           }
353         }
354         replaceAndUpdateChains(prot, al, AlignSeq.PEP, false);
355       }
356     } catch (ClassNotFoundException q)
357     {
358     }
359   }
360
361   private void replaceAndUpdateChains(ArrayList<SequenceI> prot,
362           AlignmentI al, String pep, boolean b)
363   {
364     List<List<? extends Object>> replaced = AlignSeq
365             .replaceMatchingSeqsWith(seqs, annotations, prot, al, pep,
366                     false);
367     for (PDBChain ch : chains)
368     {
369       int p = 0;
370       for (SequenceI sq : (List<SequenceI>) replaced.get(0))
371       {
372         p++;
373         if (sq == ch.sequence || sq.getDatasetSequence() == ch.sequence)
374         {
375           p = -p;
376           break;
377         }
378       }
379       if (p < 0)
380       {
381         p = -p - 1;
382         // set shadow entry for chains
383         ch.shadow = (SequenceI) replaced.get(1).get(p);
384         ch.shadowMap = ((AlignSeq) replaced.get(2).get(p))
385                 .getMappingFromS1(false);
386       }
387     }
388   }
389
390   private void processPdbFileWithAnnotate3d(ArrayList<SequenceI> rna)
391           throws Exception
392   {
393     // System.out.println("this is a PDB format and RNA sequence");
394     // note: we use reflection here so that the applet can compile and run
395     // without the HTTPClient bits and pieces needed for accessing Annotate3D
396     // web service
397     try
398     {
399       Class cl = Class.forName("jalview.ws.jws1.Annotate3D");
400       if (cl != null)
401       {
402         // TODO: use the PDB ID of the structure if one is available, to save
403         // bandwidth and avoid uploading the whole structure to the service
404         Object annotate3d = cl.getConstructor(new Class[]
405         {}).newInstance(new Object[]
406         {});
407         AlignmentI al = ((AlignmentI) cl.getMethod("getRNAMLFor",
408                 new Class[]
409                 { FileParse.class }).invoke(annotate3d, new Object[]
410         { new FileParse(getDataName(), type) }));
411         for (SequenceI sq : al.getSequences())
412         {
413           if (sq.getDatasetSequence() != null)
414           {
415             if (sq.getDatasetSequence().getPDBId() != null)
416             {
417               sq.getDatasetSequence().getPDBId().clear();
418             }
419           }
420           else
421           {
422             if (sq.getPDBId() != null)
423             {
424               sq.getPDBId().clear();
425             }
426           }
427         }
428         replaceAndUpdateChains(rna, al, AlignSeq.DNA, false);
429       }
430     } catch (ClassNotFoundException x)
431     {
432       // ignore classnotfounds - occurs in applet
433     }
434     ;
435   }
436
437   /**
438    * make a friendly ID string.
439    * 
440    * @param dataName
441    * @return truncated dataName to after last '/'
442    */
443   private String safeName(String dataName)
444   {
445     int p = 0;
446     while ((p = dataName.indexOf("/")) > -1 && p < dataName.length())
447     {
448       dataName = dataName.substring(p + 1);
449     }
450     return dataName;
451   }
452
453   public void makeResidueList()
454   {
455     for (int i = 0; i < chains.size(); i++)
456     {
457       chains.elementAt(i).makeResidueList(VisibleChainAnnotation);
458     }
459   }
460
461   public void makeCaBondList()
462   {
463     for (int i = 0; i < chains.size(); i++)
464     {
465       chains.elementAt(i).makeCaBondList();
466     }
467   }
468
469   public PDBChain findChain(String id)
470   {
471     for (int i = 0; i < chains.size(); i++)
472     {
473       if (chains.elementAt(i).id.equals(id))
474       {
475         return chains.elementAt(i);
476       }
477     }
478
479     return null;
480   }
481
482   public void setChargeColours()
483   {
484     for (int i = 0; i < chains.size(); i++)
485     {
486       chains.elementAt(i).setChargeColours();
487     }
488   }
489
490   public void setColours(jalview.schemes.ColourSchemeI cs)
491   {
492     for (int i = 0; i < chains.size(); i++)
493     {
494       chains.elementAt(i).setChainColours(cs);
495     }
496   }
497
498   public void setChainColours()
499   {
500     for (int i = 0; i < chains.size(); i++)
501     {
502       chains.elementAt(i).setChainColours(
503               Color.getHSBColor(1.0f / i, .4f, 1.0f));
504     }
505   }
506
507   public boolean isRNA(SequenceI seqs)
508   {
509     for (int i = 0; i < seqs.getLength(); i++)
510     {
511       if ((seqs.getCharAt(i) != 'A') && (seqs.getCharAt(i) != 'C')
512               && (seqs.getCharAt(i) != 'G') && (seqs.getCharAt(i) != 'U'))
513       {
514         return false;
515       }
516     }
517
518     return true;
519
520   }
521 }