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