fileFormat enum wip changes
[jalview.git] / src / jalview / ext / jmol / JmolParser.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 jalview.ext.jmol;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Annotation;
25 import jalview.datamodel.DBRefSource;
26 import jalview.datamodel.SequenceI;
27 import jalview.io.FileParse;
28 import jalview.io.DataSourceType;
29 import jalview.io.StructureFile;
30 import jalview.schemes.ResidueProperties;
31 import jalview.structure.StructureImportSettings;
32 import jalview.util.MessageManager;
33
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Vector;
39
40 import javajs.awt.Dimension;
41
42 import org.jmol.api.JmolStatusListener;
43 import org.jmol.api.JmolViewer;
44 import org.jmol.c.CBK;
45 import org.jmol.c.STR;
46 import org.jmol.modelset.ModelSet;
47 import org.jmol.viewer.Viewer;
48
49 import MCview.Atom;
50 import MCview.PDBChain;
51 import MCview.Residue;
52
53 /**
54  * Import and process files with Jmol for file like PDB, mmCIF
55  * 
56  * @author jprocter
57  * 
58  */
59 public class JmolParser extends StructureFile implements JmolStatusListener
60 {
61   Viewer viewer = null;
62
63   public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
64           boolean externalSecStr, String inFile, DataSourceType sourceType)
65           throws IOException
66   {
67     super(inFile, sourceType);
68   }
69
70   public JmolParser(boolean addAlignmentAnnotations, boolean predictSecStr,
71           boolean externalSecStr, FileParse fp) throws IOException
72   {
73     super(fp);
74   }
75
76   public JmolParser()
77   {
78   }
79
80   /**
81    * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
82    * resulting object model to generate Jalview-style sequences, with secondary
83    * structure annotation added where available (i.e. where it has been computed
84    * by Jmol using DSSP).
85    * 
86    * @see jalview.io.AlignFile#parse()
87    */
88   @Override
89   public void parse() throws IOException
90   {
91     String dataName = getDataName();
92     if (dataName.endsWith(".cif"))
93     {
94       setDbRefType(DBRefSource.MMCIF);
95     }
96     else
97     {
98       setDbRefType(DBRefSource.PDB);
99     }
100     setChains(new Vector<PDBChain>());
101     Viewer jmolModel = getJmolData();
102     jmolModel.openReader(getDataName(), getDataName(), getReader());
103     waitForScript(jmolModel);
104
105     /*
106      * Convert one or more Jmol Model objects to Jalview sequences
107      */
108     if (jmolModel.ms.mc > 0)
109     {
110       transformJmolModelToJalview(jmolModel.ms);
111     }
112   }
113
114   /**
115    * create a headless jmol instance for dataprocessing
116    * 
117    * @return
118    */
119   private Viewer getJmolData()
120   {
121     if (viewer == null)
122     {
123       try
124       {
125         viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
126                 null, "-x -o -n", this);
127         // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
128         viewer.setBooleanProperty("defaultStructureDSSP", true);
129       } catch (ClassCastException x)
130       {
131         throw new Error(MessageManager.formatMessage(
132                 "error.jmol_version_not_compatible_with_jalview_version",
133                 new String[] { JmolViewer.getJmolVersion() }), x);
134       }
135     }
136     return viewer;
137   }
138
139   public void transformJmolModelToJalview(ModelSet ms) throws IOException
140   {
141     try
142     {
143       String lastID = "";
144       List<SequenceI> rna = new ArrayList<SequenceI>();
145       List<SequenceI> prot = new ArrayList<SequenceI>();
146       PDBChain tmpchain;
147       String pdbId = (String) ms.getInfo(0, "title");
148       setId(pdbId);
149       List<Atom> significantAtoms = convertSignificantAtoms(ms);
150       for (Atom tmpatom : significantAtoms)
151       {
152         try
153         {
154           tmpchain = findChain(tmpatom.chain);
155           if (tmpatom.resNumIns.trim().equals(lastID))
156           {
157             // phosphorylated protein - seen both CA and P..
158             continue;
159           }
160           tmpchain.atoms.addElement(tmpatom);
161         } catch (Exception e)
162         {
163           tmpchain = new PDBChain(pdbId, tmpatom.chain);
164           getChains().add(tmpchain);
165           tmpchain.atoms.addElement(tmpatom);
166         }
167         lastID = tmpatom.resNumIns.trim();
168       }
169       xferSettings();
170
171       makeResidueList();
172       makeCaBondList();
173
174       if (getId() == null)
175       {
176         setId(inFile.getName());
177       }
178       for (PDBChain chain : getChains())
179       {
180         SequenceI chainseq = postProcessChain(chain);
181         if (isRNA(chainseq))
182         {
183           rna.add(chainseq);
184         }
185         else
186         {
187           prot.add(chainseq);
188         }
189
190         if (StructureImportSettings.isPredictSecondaryStructure())
191         {
192           createAnnotation(chainseq, chain, ms.at);
193         }
194       }
195     } catch (OutOfMemoryError er)
196     {
197       System.out
198               .println("OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
199       throw new IOException(
200               MessageManager
201                       .getString("exception.outofmemory_loading_mmcif_file"));
202     }
203   }
204
205   private List<Atom> convertSignificantAtoms(ModelSet ms)
206   {
207     List<Atom> significantAtoms = new ArrayList<Atom>();
208     for (org.jmol.modelset.Atom atom : ms.at)
209     {
210       // System.out.println("Seq Id : " + atom.getSeqID());
211       // System.out.println("To String : " + atom.toString());
212       if (!StructureImportSettings.isProcessHETATMs() && atom.isHetero())
213       {
214         continue;
215       }
216       if (atom.getAtomName().equalsIgnoreCase("CA")
217               || atom.getAtomName().equalsIgnoreCase("P"))
218       {
219         Atom curAtom = new Atom(atom.x, atom.y, atom.z);
220         curAtom.atomIndex = atom.getIndex();
221         curAtom.chain = atom.getChainIDStr();
222         curAtom.insCode = atom.group.getInsertionCode();
223         curAtom.name = atom.getAtomName();
224         curAtom.number = atom.getAtomNumber();
225         curAtom.resName = atom.getGroup3(true);
226         curAtom.resNumber = atom.getResno();
227         curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom
228                 .getIndex()] : Float.valueOf(atom.getOccupancy100());
229         curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode;
230         curAtom.tfactor = atom.getBfactor100() / 100f;
231         curAtom.type = 0;
232         significantAtoms.add(curAtom);
233       }
234     }
235     return significantAtoms;
236   }
237
238   private void createAnnotation(SequenceI sequence, PDBChain chain,
239           org.jmol.modelset.Atom[] jmolAtoms)
240   {
241     char[] secstr = new char[sequence.getLength()];
242     char[] secstrcode = new char[sequence.getLength()];
243
244     // Ensure Residue size equals Seq size
245     if (chain.residues.size() != sequence.getLength())
246     {
247       return;
248     }
249     int annotIndex = 0;
250     for (Residue residue : chain.residues)
251     {
252       Atom repAtom = residue.getAtoms().get(0);
253       STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
254               .getProteinStructureSubType();
255       setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
256               secstrcode);
257       ++annotIndex;
258     }
259     addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
260             secstrcode, chain.id, sequence.getStart());
261   }
262
263   /**
264    * Helper method that adds an AlignmentAnnotation for secondary structure to
265    * the sequence, provided at least one secondary structure prediction has been
266    * made
267    * 
268    * @param modelTitle
269    * @param seq
270    * @param secstr
271    * @param secstrcode
272    * @param chainId
273    * @param firstResNum
274    * @return
275    */
276   protected void addSecondaryStructureAnnotation(String modelTitle,
277           SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
278           int firstResNum)
279   {
280     char[] seq = sq.getSequence();
281     boolean ssFound = false;
282     Annotation asecstr[] = new Annotation[seq.length + firstResNum - 1];
283     for (int p = 0; p < seq.length; p++)
284     {
285       if (secstr[p] >= 'A' && secstr[p] <= 'z')
286       {
287         try
288         {
289         asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
290                 secstrcode[p], Float.NaN);
291         ssFound = true;
292         } catch (Exception e)
293         {
294           // e.printStackTrace();
295         }
296       }
297     }
298
299     if (ssFound)
300     {
301       String mt = modelTitle == null ? getDataName() : modelTitle;
302       mt += chainId;
303       AlignmentAnnotation ann = new AlignmentAnnotation(
304               "Secondary Structure", "Secondary Structure for " + mt,
305               asecstr);
306       ann.belowAlignment = true;
307       ann.visible = true;
308       ann.autoCalculated = false;
309       ann.setCalcId(getClass().getName());
310       ann.adjustForAlignment();
311       ann.validateRangeAndDisplay();
312       annotations.add(ann);
313       sq.addAlignmentAnnotation(ann);
314     }
315   }
316
317   private void waitForScript(Viewer jmd)
318   {
319     while (jmd.isScriptExecuting())
320     {
321       try
322       {
323         Thread.sleep(50);
324
325       } catch (InterruptedException x)
326       {
327       }
328     }
329   }
330
331   /**
332    * Convert Jmol's secondary structure code to Jalview's, and stored it in the
333    * secondary structure arrays at the given sequence position
334    * 
335    * @param proteinStructureSubType
336    * @param pos
337    * @param secstr
338    * @param secstrcode
339    */
340   protected void setSecondaryStructure(STR proteinStructureSubType,
341           int pos, char[] secstr, char[] secstrcode)
342   {
343     switch (proteinStructureSubType)
344     {
345     case HELIX310:
346       secstr[pos] = '3';
347       break;
348     case HELIX:
349     case HELIXALPHA:
350       secstr[pos] = 'H';
351       break;
352     case HELIXPI:
353       secstr[pos] = 'P';
354       break;
355     case SHEET:
356       secstr[pos] = 'E';
357       break;
358     default:
359       secstr[pos] = 0;
360     }
361
362     switch (proteinStructureSubType)
363     {
364     case HELIX310:
365     case HELIXALPHA:
366     case HELIXPI:
367     case HELIX:
368       secstrcode[pos] = 'H';
369       break;
370     case SHEET:
371       secstrcode[pos] = 'E';
372       break;
373     default:
374       secstrcode[pos] = 0;
375     }
376   }
377
378   /**
379    * Convert any non-standard peptide codes to their standard code table
380    * equivalent. (Initial version only does Selenomethionine MSE->MET.)
381    * 
382    * @param threeLetterCode
383    * @param seq
384    * @param pos
385    */
386   protected void replaceNonCanonicalResidue(String threeLetterCode,
387           char[] seq, int pos)
388   {
389     String canonical = ResidueProperties
390             .getCanonicalAminoAcid(threeLetterCode);
391     if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
392     {
393       seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
394     }
395   }
396
397   /**
398    * Not implemented - returns null
399    */
400   @Override
401   public String print()
402   {
403     return null;
404   }
405
406   /**
407    * Not implemented
408    */
409   @Override
410   public void setCallbackFunction(String callbackType,
411           String callbackFunction)
412   {
413   }
414
415   @Override
416   public void notifyCallback(CBK cbType, Object[] data)
417   {
418     String strInfo = (data == null || data[1] == null ? null : data[1]
419             .toString());
420     switch (cbType)
421     {
422     case ECHO:
423       sendConsoleEcho(strInfo);
424       break;
425     case SCRIPT:
426       notifyScriptTermination((String) data[2],
427               ((Integer) data[3]).intValue());
428       break;
429     case MEASURE:
430       String mystatus = (String) data[3];
431       if (mystatus.indexOf("Picked") >= 0
432               || mystatus.indexOf("Sequence") >= 0)
433       {
434         // Picking mode
435         sendConsoleMessage(strInfo);
436       }
437       else if (mystatus.indexOf("Completed") >= 0)
438       {
439         sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
440                 strInfo.length() - 1));
441       }
442       break;
443     case MESSAGE:
444       sendConsoleMessage(data == null ? null : strInfo);
445       break;
446     case PICK:
447       sendConsoleMessage(strInfo);
448       break;
449     default:
450       break;
451     }
452   }
453
454   String lastConsoleEcho = "";
455
456   private void sendConsoleEcho(String string)
457   {
458     lastConsoleEcho += string;
459     lastConsoleEcho += "\n";
460   }
461
462   String lastConsoleMessage = "";
463
464   private void sendConsoleMessage(String string)
465   {
466     lastConsoleMessage += string;
467     lastConsoleMessage += "\n";
468   }
469
470   int lastScriptTermination = -1;
471
472   String lastScriptMessage = "";
473
474   private void notifyScriptTermination(String string, int intValue)
475   {
476     lastScriptMessage += string;
477     lastScriptMessage += "\n";
478     lastScriptTermination = intValue;
479   }
480
481   @Override
482   public boolean notifyEnabled(CBK callbackPick)
483   {
484     switch (callbackPick)
485     {
486     case MESSAGE:
487     case SCRIPT:
488     case ECHO:
489     case LOADSTRUCT:
490     case ERROR:
491       return true;
492     default:
493       return false;
494     }
495   }
496
497   /**
498    * Not implemented - returns null
499    */
500   @Override
501   public String eval(String strEval)
502   {
503     return null;
504   }
505
506   /**
507    * Not implemented - returns null
508    */
509   @Override
510   public float[][] functionXY(String functionName, int x, int y)
511   {
512     return null;
513   }
514
515   /**
516    * Not implemented - returns null
517    */
518   @Override
519   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
520   {
521     return null;
522   }
523
524   /**
525    * Not implemented - returns null
526    */
527   @Override
528   public String createImage(String fileName, String imageType,
529           Object text_or_bytes, int quality)
530   {
531     return null;
532   }
533
534   /**
535    * Not implemented - returns null
536    */
537   @Override
538   public Map<String, Object> getRegistryInfo()
539   {
540     return null;
541   }
542
543   /**
544    * Not implemented
545    */
546   @Override
547   public void showUrl(String url)
548   {
549   }
550
551   /**
552    * Not implemented - returns null
553    */
554   @Override
555   public Dimension resizeInnerPanel(String data)
556   {
557     return null;
558   }
559
560   @Override
561   public Map<String, Object> getJSpecViewProperty(String arg0)
562   {
563     return null;
564   }
565
566   public boolean isPredictSecondaryStructure()
567   {
568     return predictSecondaryStructure;
569   }
570
571   public void setPredictSecondaryStructure(boolean predictSecondaryStructure)
572   {
573     this.predictSecondaryStructure = predictSecondaryStructure;
574   }
575
576   public boolean isVisibleChainAnnotation()
577   {
578     return visibleChainAnnotation;
579   }
580
581   public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
582   {
583     this.visibleChainAnnotation = visibleChainAnnotation;
584   }
585
586 }