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