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