Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.ext.jmol

File JmolParser.java

 

Coverage histogram

../../../img/srcFileCovDistChart7.png
27% of files have more coverage

Code metrics

68
213
33
1
677
493
97
0.46
6.45
33
2.94

Classes

Class Line # Actions
JmolParser 58 213 97
0.6273885462.7%
 

Contributing tests

This file is covered by 20 tests. .

Source view

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