merge
[jalview.git] / src / jalview / ext / jmol / PDBFileWithJmol.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.PDBEntry;
26 import jalview.datamodel.Sequence;
27 import jalview.datamodel.SequenceI;
28 import jalview.io.AlignFile;
29 import jalview.io.FileParse;
30 import jalview.schemes.ResidueProperties;
31 import jalview.util.MessageManager;
32
33 import java.io.IOException;
34 import java.util.Hashtable;
35 import java.util.Map;
36
37 import javajs.awt.Dimension;
38
39 import org.jmol.api.JmolStatusListener;
40 import org.jmol.api.JmolViewer;
41 import org.jmol.c.CBK;
42 import org.jmol.modelset.Group;
43 import org.jmol.modelset.Model;
44 import org.jmol.modelset.ModelSet;
45 import org.jmol.modelsetbio.BioModel;
46 import org.jmol.modelsetbio.BioPolymer;
47 import org.jmol.viewer.Viewer;
48
49 /**
50  * Import and process PDB files with Jmol
51  * 
52  * @author jprocter
53  * 
54  */
55 public class PDBFileWithJmol extends AlignFile implements
56         JmolStatusListener
57 {
58   Viewer viewer = null;
59
60   public PDBFileWithJmol(String inFile, String type) throws IOException
61   {
62     super(inFile, type);
63   }
64
65   public PDBFileWithJmol(FileParse fp) throws IOException
66   {
67     super(fp);
68   }
69
70   public PDBFileWithJmol()
71   {
72   }
73
74   /**
75    * create a headless jmol instance for dataprocessing
76    * 
77    * @return
78    */
79   private Viewer getJmolData()
80   {
81     if (viewer == null)
82     {
83       try
84       {
85         viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
86                 null, "-x -o -n", this);
87       } catch (ClassCastException x)
88       {
89         throw new Error(MessageManager.formatMessage("error.jmol_version_not_compatible_with_jalview_version", new String[]{JmolViewer.getJmolVersion()}),
90                 x);
91       }
92     }
93     return viewer;
94   }
95
96   private void waitForScript(Viewer jmd)
97   {
98     while (jmd.isScriptExecuting())
99     {
100       try
101       {
102         Thread.sleep(50);
103
104       } catch (InterruptedException x)
105       {
106       }
107     }
108   }
109
110   /*
111    * (non-Javadoc)
112    * 
113    * @see jalview.io.AlignFile#parse()
114    */
115   @Override
116   public void parse() throws IOException
117   {
118     Viewer jmd = getJmolData();
119     jmd.openReader(getDataName(), getDataName(), getReader());
120     waitForScript(jmd);
121
122     if (jmd.ms.mc > 0)
123     {
124       ModelSet ms = jmd.ms;
125       // Jmol 14.2 added third argument doReport = false
126       ms.calculateStructures(null, true, false, false, true);
127       // System.out.println("Structs\n"+structs);
128       Group group = null;
129       int modelIndex = -1;
130       for (Model model : ms.am)
131       {
132         modelIndex++;
133         for (BioPolymer bp : ((BioModel) model).bioPolymers)
134         {
135           int lastChainId = 0; // int value of character e.g. 65 for A
136           String lastChainIdAlpha = "";
137
138           int[] groups = bp.getLeadAtomIndices();
139           char seq[] = new char[groups.length], secstr[] = new char[groups.length], secstrcode[] = new char[groups.length];
140           int groupc = 0, len = 0, firstrnum = 1, lastrnum = 0;
141
142           do
143           {
144             if (groupc >= groups.length
145                     || ms.at[groups[groupc]].group.chain.chainID != lastChainId)
146             {
147               /*
148                * on change of chain (or at end), construct the sequence and
149                * secondary structure annotation for the last chain
150                */
151               if (len > 0)
152               {
153                 boolean isNa = bp.isNucleic();
154                 // normalise sequence from Jmol to jalview
155                 int[] cinds = isNa ? ResidueProperties.nucleotideIndex
156                         : ResidueProperties.aaIndex;
157                 int nonGap = isNa ? ResidueProperties.maxNucleotideIndex
158                         : ResidueProperties.maxProteinIndex;
159                 char ngc = 'X';
160                 char newseq[] = new char[len];
161                 Annotation asecstr[] = new Annotation[len + firstrnum - 1];
162                 for (int p = 0; p < len; p++)
163                 {
164                   newseq[p] = cinds[seq[p]] == nonGap ? ngc : seq[p];
165                   if (secstr[p] >= 'A' && secstr[p] <= 'z')
166                   {
167                     try
168                     {
169                       asecstr[p] = new Annotation("" + secstr[p], null,
170                               secstrcode[p], Float.NaN);
171                     } catch (ArrayIndexOutOfBoundsException e)
172                     {
173                       // skip - patch for JAL-1836
174                     }
175                   }
176                 }
177                 String modelTitle = (String) ms
178                         .getInfo(modelIndex, "title");
179                 SequenceI sq = new Sequence("" + getDataName() + "|"
180                         + modelTitle + "|" + lastChainIdAlpha, newseq,
181                         firstrnum, lastrnum);
182                 PDBEntry pdbe = new PDBEntry();
183                 pdbe.setFile(getDataName());
184                 pdbe.setId(getDataName());
185                 pdbe.setProperty(new Hashtable());
186                 // pdbe.getProperty().put("CHAIN", "" + _lastChainId);
187                 pdbe.setChainCode(lastChainIdAlpha);
188                 sq.addPDBId(pdbe);
189                 // JAL-1533
190                 // Need to put the number of models for this polymer somewhere
191                 // for Chimera/others to grab
192                 // pdbe.getProperty().put("PDBMODELS", biopoly.)
193                 seqs.add(sq);
194                 if (!isNa)
195                 {
196                   String mt = modelTitle == null ? getDataName()
197                           : modelTitle;
198                   if (lastChainId >= ' ')
199                   {
200                     mt += lastChainIdAlpha;
201                   }
202                   AlignmentAnnotation ann = new AlignmentAnnotation(
203                           "Secondary Structure", "Secondary Structure for "
204                                   + mt, asecstr);
205                   ann.belowAlignment = true;
206                   ann.visible = true;
207                   ann.autoCalculated = false;
208                   ann.setCalcId(getClass().getName());
209                   sq.addAlignmentAnnotation(ann);
210                   ann.adjustForAlignment();
211                   ann.validateRangeAndDisplay();
212                   annotations.add(ann);
213                 }
214               }
215               len = 0;
216               firstrnum = 1;
217               lastrnum = 0;
218             }
219             if (groupc < groups.length)
220             {
221               group = ms.at[groups[groupc]].group;
222               if (len == 0)
223               {
224                 firstrnum = group.getResno();
225                 lastChainId = group.chain.chainID;
226                 lastChainIdAlpha = group.chain.getIDStr();
227               }
228               else
229               {
230                 lastrnum = group.getResno();
231               }
232               seq[len] = group.getGroup1();
233
234               /*
235                * JAL-1828 replace a modified amino acid with its standard
236                * equivalent (e.g. MSE with MET->M) to maximise sequence matching
237                */
238               String threeLetterCode = group.getGroup3();
239               String canonical = ResidueProperties.getCanonicalAminoAcid(threeLetterCode);
240               if (canonical != null
241                       && !canonical.equalsIgnoreCase(threeLetterCode))
242               {
243                 seq[len] = ResidueProperties
244                         .getSingleCharacterCode(canonical);
245               }
246               switch (group.getProteinStructureSubType())
247               {
248               case HELIX310:
249                 if (secstr[len] == 0)
250                 {
251                   secstr[len] = '3';
252                 }
253               case HELIXALPHA:
254                 if (secstr[len] == 0)
255                 {
256                   secstr[len] = 'H';
257                 }
258               case HELIXPI:
259                 if (secstr[len] == 0)
260                 {
261                   secstr[len] = 'P';
262                 }
263               case HELIX:
264                 if (secstr[len] == 0)
265                 {
266                   secstr[len] = 'H';
267                 }
268                 secstrcode[len] = 'H';
269                 break;
270               case SHEET:
271                 secstr[len] = 'E';
272                 secstrcode[len] = 'E';
273                 break;
274               default:
275                 secstr[len] = 0;
276                 secstrcode[len] = 0;
277               }
278               len++;
279             }
280           } while (groupc++ < groups.length);
281         }
282       }
283
284       /*
285        * lastScriptTermination = -9465; String dsspOut =
286        * jmd.evalString("calculate STRUCTURE"); if (dsspOut.equals("pending")) {
287        * while (lastScriptTermination == -9465) { try { Thread.sleep(50); }
288        * catch (Exception x) { } ; } } System.out.println(lastConsoleEcho);
289        */
290     }
291   }
292
293   /*
294    * (non-Javadoc)
295    * 
296    * @see jalview.io.AlignFile#print()
297    */
298   @Override
299   public String print()
300   {
301     // TODO Auto-generated method stub
302     return null;
303   }
304
305   @Override
306   public void setCallbackFunction(String callbackType,
307           String callbackFunction)
308   {
309     // TODO Auto-generated method stub
310
311   }
312
313   /*
314    * @Override public void notifyCallback(EnumCallback type, Object[] data) {
315    * try { switch (type) { case ERROR: case SCRIPT:
316    * notifyScriptTermination((String) data[2], ((Integer) data[3]).intValue());
317    * break; case MESSAGE: sendConsoleMessage((data == null) ? ((String) null) :
318    * (String) data[1]); break; case LOADSTRUCT: notifyFileLoaded((String)
319    * data[1], (String) data[2], (String) data[3], (String) data[4], ((Integer)
320    * data[5]).intValue());
321    * 
322    * break; default: // System.err.println("Unhandled callback " + type + " " //
323    * + data[1].toString()); break; } } catch (Exception e) {
324    * System.err.println("Squashed Jmol callback handler error:");
325    * e.printStackTrace(); } }
326    */
327   public void notifyCallback(CBK type, Object[] data)
328   {
329     String strInfo = (data == null || data[1] == null ? null : data[1]
330             .toString());
331     switch (type)
332     {
333     case ECHO:
334       sendConsoleEcho(strInfo);
335       break;
336     case SCRIPT:
337       notifyScriptTermination((String) data[2],
338               ((Integer) data[3]).intValue());
339       break;
340     case MEASURE:
341       String mystatus = (String) data[3];
342       if (mystatus.indexOf("Picked") >= 0
343               || mystatus.indexOf("Sequence") >= 0)
344       {
345         // Picking mode
346         sendConsoleMessage(strInfo);
347       }
348       else if (mystatus.indexOf("Completed") >= 0)
349       {
350         sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
351                 strInfo.length() - 1));
352       }
353       break;
354     case MESSAGE:
355       sendConsoleMessage(data == null ? null : strInfo);
356       break;
357     case PICK:
358       sendConsoleMessage(strInfo);
359       break;
360     default:
361       break;
362     }
363   }
364
365   String lastConsoleEcho = "";
366
367   private void sendConsoleEcho(String string)
368   {
369     lastConsoleEcho += string;
370     lastConsoleEcho += "\n";
371   }
372
373   String lastConsoleMessage = "";
374
375   private void sendConsoleMessage(String string)
376   {
377     lastConsoleMessage += string;
378     lastConsoleMessage += "\n";
379   }
380
381   int lastScriptTermination = -1;
382
383   String lastScriptMessage = "";
384
385   private void notifyScriptTermination(String string, int intValue)
386   {
387     lastScriptMessage += string;
388     lastScriptMessage += "\n";
389     lastScriptTermination = intValue;
390   }
391
392   @Override
393   public boolean notifyEnabled(CBK callbackPick)
394   {
395     switch (callbackPick)
396     {
397     case MESSAGE:
398     case SCRIPT:
399     case ECHO:
400     case LOADSTRUCT:
401     case ERROR:
402       return true;
403     default:
404       return false;
405     }
406   }
407
408   @Override
409   public String eval(String strEval)
410   {
411     // TODO Auto-generated method stub
412     return null;
413   }
414
415   @Override
416   public float[][] functionXY(String functionName, int x, int y)
417   {
418     // TODO Auto-generated method stub
419     return null;
420   }
421
422   @Override
423   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
424   {
425     // TODO Auto-generated method stub
426     return null;
427   }
428
429   @Override
430   public String createImage(String fileName, String type,
431           Object text_or_bytes, int quality)
432   {
433     // TODO Auto-generated method stub
434     return null;
435   }
436
437   @Override
438   public Map<String, Object> getRegistryInfo()
439   {
440     // TODO Auto-generated method stub
441     return null;
442   }
443
444   @Override
445   public void showUrl(String url)
446   {
447     // TODO Auto-generated method stub
448
449   }
450
451   @Override
452   public Dimension resizeInnerPanel(String data)
453   {
454     return null;
455   }
456
457   @Override
458   public Map<String, Object> getJSpecViewProperty(String arg0)
459   {
460     return null;
461   }
462
463 }