JAL-1333 JAL-1533 todo to get Chimera superposing properly we need to specify a set...
[jalview.git] / src / jalview / ext / jmol / PDBFileWithJmol.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 jalview.ext.jmol;
22
23 import java.io.IOException;
24 import java.util.Hashtable;
25 import java.util.Map;
26
27 import org.jmol.api.JmolStatusListener;
28 import org.jmol.api.JmolViewer;
29 import org.jmol.constant.EnumCallback;
30 import org.jmol.modelset.Group;
31 import org.jmol.modelset.Model;
32 import org.jmol.modelset.ModelSet;
33 import org.jmol.modelset.Polymer;
34 import org.jmol.modelsetbio.BioPolymer;
35 import org.jmol.viewer.Viewer;
36 import org.openscience.jmol.app.JmolApp;
37
38 import jalview.datamodel.AlignmentAnnotation;
39 import jalview.datamodel.Annotation;
40 import jalview.datamodel.PDBEntry;
41 import jalview.datamodel.Sequence;
42 import jalview.datamodel.SequenceI;
43 import jalview.io.AlignFile;
44 import jalview.io.FileParse;
45
46 /**
47  * Import and process PDB files with Jmol
48  * 
49  * @author jprocter
50  * 
51  */
52 public class PDBFileWithJmol extends AlignFile implements
53         JmolStatusListener
54 {
55
56   JmolApp jmolApp = null;
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     // TODO Auto-generated constructor stub
73   }
74
75   /**
76    * create a headless jmol instance for dataprocessing
77    * 
78    * @return
79    */
80   private Viewer getJmolData()
81   {
82     if (viewer == null)
83     { // note that -o -n -x are all implied
84       jmolApp = new JmolApp();
85       jmolApp.isDataOnly = true;
86       jmolApp.haveConsole = false;
87       jmolApp.haveDisplay = false;
88       jmolApp.exitUponCompletion = true;
89       try
90       {
91         viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
92                 null, jmolApp.commandOptions, this);
93         viewer.setScreenDimension(jmolApp.startupWidth,
94                 jmolApp.startupHeight);
95         jmolApp.startViewer(viewer, null);
96       } catch (ClassCastException x)
97       {
98         throw new Error(
99                 "Jmol version "
100                         + JmolViewer.getJmolVersion()
101                         + " is not compatible with this version of Jalview. Report this problem at issues.jalview.org",
102                 x);
103       }
104     }
105     return viewer;
106   }
107
108   private void waitForScript(Viewer jmd)
109   {
110     while (jmd.isScriptExecuting())
111     {
112       try
113       {
114         Thread.sleep(50);
115
116       } catch (InterruptedException x)
117       {
118       }
119     }
120   }
121
122   /*
123    * (non-Javadoc)
124    * 
125    * @see jalview.io.AlignFile#parse()
126    */
127   @Override
128   public void parse() throws IOException
129   {
130     Viewer jmd = getJmolData();
131     jmd.openReader(getDataName(), getDataName(), getReader());
132     waitForScript(jmd);
133     if (jmd.getModelCount() > 0)
134     {
135       ModelSet ms = jmd.getModelSet();
136       String structs = ms.calculateStructures(null, true, false, true);
137       // System.out.println("Structs\n"+structs);
138       for (Model model : ms.getModels())
139       {
140         for (int _bp = 0, _bpc = model.getBioPolymerCount(); _bp < _bpc; _bp++)
141         {
142           Polymer bp = model.getBioPolymer(_bp);
143           if (bp instanceof BioPolymer)
144           {
145             BioPolymer biopoly = (BioPolymer) bp;
146             char _lastChainId = 0;
147             int[] groups = biopoly.getLeadAtomIndices();
148             Group[] bpgrp = biopoly.getGroups();
149             char seq[] = new char[groups.length], secstr[] = new char[groups.length], secstrcode[] = new char[groups.length];
150             int groupc = 0, len = 0, firstrnum = 1, lastrnum = 0;
151             do
152             {
153               if (groupc >= groups.length
154                       || ms.atoms[groups[groupc]].getChainID() != _lastChainId)
155               {
156                 if (len > 0)
157                 {
158                   char newseq[] = new char[len];
159                   System.arraycopy(seq, 0, newseq, 0, len);
160                   Annotation asecstr[] = new Annotation[len+firstrnum-1];
161                   for (int p = 0; p < len; p++)
162                   {
163                     if (secstr[p] >= 'A' && secstr[p] <= 'z')
164                     {
165                       asecstr[p] = new Annotation("" + secstr[p], null,
166                               secstrcode[p], Float.NaN);
167                     }
168                   }
169                   SequenceI sq = new Sequence("" + getDataName() + "|"
170                           + model.getModelTitle() + "|" + _lastChainId,
171                           newseq, firstrnum, lastrnum);
172                   PDBEntry pdbe = new PDBEntry();
173                   pdbe.setFile(getDataName());
174                   pdbe.setId(getDataName());
175                   sq.addPDBId(pdbe);
176                   pdbe.setProperty(new Hashtable());
177                   pdbe.getProperty().put("CHAIN", "" + _lastChainId);
178                   // JAL-1533
179                   // Need to put the number of models for this polymer somewhere for Chimera/others to grab
180                   //                  pdbe.getProperty().put("PDBMODELS", biopoly.)
181                   seqs.add(sq);
182                   if (!(biopoly.isDna() || biopoly.isRna()))
183                   {
184                     AlignmentAnnotation ann = new AlignmentAnnotation(
185                             "Secondary Structure",
186                             "Secondary Structure from PDB File", asecstr);
187                     ann.belowAlignment=true;
188                     ann.visible=true;
189                     ann.autoCalculated=false;
190                     ann.setCalcId(getClass().getName());
191                     sq.addAlignmentAnnotation(ann);
192                     ann.adjustForAlignment();
193                     ann.validateRangeAndDisplay();
194                     annotations.add(ann);
195                   }
196                 }
197                 len = 0;
198                 firstrnum = 1;
199                 lastrnum = 0;
200               }
201               if (groupc < groups.length)
202               {
203                 if (len == 0)
204                 {
205                   firstrnum = bpgrp[groupc].getResno();
206                   _lastChainId = bpgrp[groupc].getChainID();
207                 }
208                 else
209                 {
210                   lastrnum = bpgrp[groupc].getResno();
211                 }
212                 seq[len] = bpgrp[groupc].getGroup1();
213                 switch (bpgrp[groupc].getProteinStructureSubType())
214                 {
215                 case HELIX_310:
216                   if (secstr[len] == 0)
217                   {
218                     secstr[len] = '3';
219                   }
220                 case HELIX_ALPHA:
221                   if (secstr[len] == 0)
222                   {
223                     secstr[len] = 'H';
224                   }
225                 case HELIX_PI:
226                   if (secstr[len] == 0)
227                   {
228                     secstr[len] = 'P';
229                   }
230                 case HELIX:
231                   if (secstr[len] == 0)
232                   {
233                     secstr[len] = 'H';
234                   }
235                   secstrcode[len] = 'H';
236                   break;
237                 case SHEET:
238                   secstr[len] = 'E';
239                   secstrcode[len] = 'E';
240                   break;
241                 default:
242                   secstr[len] = 0;
243                   secstrcode[len] = 0;
244                 }
245                 len++;
246               }
247             } while (groupc++ < groups.length);
248
249           }
250         }
251       }
252
253       /*
254        * lastScriptTermination = -9465; String dsspOut =
255        * jmd.evalString("calculate STRUCTURE"); if (dsspOut.equals("pending")) {
256        * while (lastScriptTermination == -9465) { try { Thread.sleep(50); }
257        * catch (Exception x) { } ; } } System.out.println(lastConsoleEcho);
258        */
259     }
260   }
261
262   /*
263    * (non-Javadoc)
264    * 
265    * @see jalview.io.AlignFile#print()
266    */
267   @Override
268   public String print()
269   {
270     // TODO Auto-generated method stub
271     return null;
272   }
273
274   @Override
275   public void setCallbackFunction(String callbackType,
276           String callbackFunction)
277   {
278     // TODO Auto-generated method stub
279
280   }
281
282   /*
283    * @Override public void notifyCallback(EnumCallback type, Object[] data) {
284    * try { switch (type) { case ERROR: case SCRIPT:
285    * notifyScriptTermination((String) data[2], ((Integer) data[3]).intValue());
286    * break; case MESSAGE: sendConsoleMessage((data == null) ? ((String) null) :
287    * (String) data[1]); break; case LOADSTRUCT: notifyFileLoaded((String)
288    * data[1], (String) data[2], (String) data[3], (String) data[4], ((Integer)
289    * data[5]).intValue());
290    * 
291    * break; default: // System.err.println("Unhandled callback " + type + " " //
292    * + data[1].toString()); break; } } catch (Exception e) {
293    * System.err.println("Squashed Jmol callback handler error:");
294    * e.printStackTrace(); } }
295    */
296   public void notifyCallback(EnumCallback type, Object[] data)
297   {
298     String strInfo = (data == null || data[1] == null ? null : data[1]
299             .toString());
300     switch (type)
301     {
302     case ECHO:
303       sendConsoleEcho(strInfo);
304       break;
305     case SCRIPT:
306       notifyScriptTermination((String) data[2],
307               ((Integer) data[3]).intValue());
308       break;
309     case MEASURE:
310       String mystatus = (String) data[3];
311       if (mystatus.indexOf("Picked") >= 0
312               || mystatus.indexOf("Sequence") >= 0) // picking mode
313         sendConsoleMessage(strInfo);
314       else if (mystatus.indexOf("Completed") >= 0)
315         sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
316                 strInfo.length() - 1));
317       break;
318     case MESSAGE:
319       sendConsoleMessage(data == null ? null : strInfo);
320       break;
321     case PICK:
322       sendConsoleMessage(strInfo);
323       break;
324     default:
325       break;
326     }
327   }
328
329   private void notifyFileLoaded(String string, String string2,
330           String string3, String string4, int intValue)
331   {
332     // TODO Auto-generated method stub
333
334   }
335
336   String lastConsoleEcho = "";
337
338   private void sendConsoleEcho(String string)
339   {
340     lastConsoleEcho += string;
341     lastConsoleEcho += "\n";
342   }
343
344   String lastConsoleMessage = "";
345
346   private void sendConsoleMessage(String string)
347   {
348     lastConsoleMessage += string;
349     lastConsoleMessage += "\n";
350   }
351
352   int lastScriptTermination = -1;
353
354   String lastScriptMessage = "";
355
356   private void notifyScriptTermination(String string, int intValue)
357   {
358     lastScriptMessage += string;
359     lastScriptMessage += "\n";
360     lastScriptTermination = intValue;
361   }
362
363   @Override
364   public boolean notifyEnabled(EnumCallback callbackPick)
365   {
366     switch (callbackPick)
367     {
368     case MESSAGE:
369     case SCRIPT:
370     case ECHO:
371     case LOADSTRUCT:
372     case ERROR:
373       return true;
374     case MEASURE:
375     case PICK:
376     case HOVER:
377     case RESIZE:
378     case SYNC:
379     case CLICK:
380     case ANIMFRAME:
381     case MINIMIZATION:
382     }
383     return false;
384   }
385
386   @Override
387   public String eval(String strEval)
388   {
389     // TODO Auto-generated method stub
390     return null;
391   }
392
393   @Override
394   public float[][] functionXY(String functionName, int x, int y)
395   {
396     // TODO Auto-generated method stub
397     return null;
398   }
399
400   @Override
401   public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
402   {
403     // TODO Auto-generated method stub
404     return null;
405   }
406
407   @Override
408   public String createImage(String fileName, String type,
409           Object text_or_bytes, int quality)
410   {
411     // TODO Auto-generated method stub
412     return null;
413   }
414
415   @Override
416   public Map<String, Object> getRegistryInfo()
417   {
418     // TODO Auto-generated method stub
419     return null;
420   }
421
422   @Override
423   public void showUrl(String url)
424   {
425     // TODO Auto-generated method stub
426
427   }
428
429   @Override
430   public void resizeInnerPanel(String data)
431   {
432     // TODO Auto-generated method stub
433
434   }
435
436 }