2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.ext.jmol;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Locale;
30 import java.util.Vector;
32 import org.jmol.api.JmolStatusListener;
33 import org.jmol.api.JmolViewer;
34 import org.jmol.c.CBK;
35 import org.jmol.c.STR;
36 import org.jmol.modelset.ModelSet;
37 import org.jmol.viewer.Viewer;
39 import com.stevesoft.pat.Regex;
41 import jalview.bin.Console;
42 import jalview.datamodel.Alignment;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.PDBEntry;
46 import jalview.datamodel.SequenceI;
47 import jalview.datamodel.annotations.AlphaFoldAnnotationRowBuilder;
48 import jalview.datamodel.annotations.AnnotationRowBuilder;
49 import jalview.io.DataSourceType;
50 import jalview.io.FileParse;
51 import jalview.io.StructureFile;
52 import jalview.schemes.ResidueProperties;
53 import jalview.structure.StructureImportSettings;
54 import jalview.util.Format;
55 import jalview.util.MessageManager;
56 import jalview.ws.dbsources.EBIAlfaFold;
58 import mc_view.PDBChain;
59 import mc_view.Residue;
62 * Import and process files with Jmol for file like PDB, mmCIF
67 public class JmolParser extends StructureFile implements JmolStatusListener
71 public JmolParser(boolean immediate, Object inFile,
72 DataSourceType sourceType) throws IOException
74 // BH 2018 File or String for filename
75 super(immediate, inFile, sourceType);
79 public JmolParser(Object inFile, DataSourceType sourceType)
82 this(inFile, sourceType, null);
85 public JmolParser(Object inFile, DataSourceType sourceType,
86 StructureImportSettings.TFType tempfacType) throws IOException
88 super(inFile, sourceType, tempfacType);
91 public JmolParser(FileParse fp, boolean doXferSettings) throws IOException
93 super(fp, doXferSettings);
95 public JmolParser(FileParse fp) throws IOException
105 * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
106 * resulting object model to generate Jalview-style sequences, with secondary
107 * structure annotation added where available (i.e. where it has been computed
108 * by Jmol using DSSP).
110 * @see jalview.io.AlignFile#parse()
113 public void parse() throws IOException
119 public void parse(boolean doXferSettings) throws IOException
121 setChains(new Vector<PDBChain>());
122 Viewer jmolModel = getJmolData();
123 jmolModel.openReader(getDataName(), getDataName(), getReader());
124 waitForScript(jmolModel);
127 * Convert one or more Jmol Model objects to Jalview sequences
129 if (jmolModel.ms.mc > 0)
131 // ideally we do this
134 // setStructureFileType(jmolModel.evalString("show _fileType"));
135 // } catch (Exception q)
139 // instead, we distinguish .cif from non-.cif by filename
140 setStructureFileType(
141 getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
142 ? PDBEntry.Type.MMCIF.toString()
145 transformJmolModelToJalview(jmolModel.ms, doXferSettings);
150 * create a headless jmol instance for dataprocessing
154 private Viewer getJmolData()
161 * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
162 * see http://wiki.jmol.org/index.php/Jmol_Application
165 viewer = JalviewJmolBinding.getJmolData(this);
166 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
167 viewer.setBooleanProperty("defaultStructureDSSP", true);
168 } catch (ClassCastException x)
170 throw new Error(MessageManager.formatMessage(
171 "error.jmol_version_not_compatible_with_jalview_version",
173 { JmolViewer.getJmolVersion() }), x);
179 public static Regex getNewAlphafoldValidator()
181 Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
182 validator.setIgnoreCase(true);
186 PDBEntry.Type jmolFiletype = null;
189 * resolve a jmol filetype string and update the jmolFiletype field
192 * @param jmolIdentifiedFileType
193 * @return true if filetype was identified as MMCIF, PDB
195 public boolean updateFileType(String jmolIdentifiedFileType)
197 if (jmolIdentifiedFileType == null
198 || jmolIdentifiedFileType.trim().equals(""))
202 if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType))
204 jmolFiletype = PDBEntry.Type.MMCIF;
207 if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
209 jmolFiletype = PDBEntry.Type.PDB;
215 public void transformJmolModelToJalview(ModelSet ms,
216 boolean localDoXferSettings) throws IOException
220 Regex alphaFold = getNewAlphafoldValidator();
222 List<SequenceI> rna = new ArrayList<SequenceI>();
223 List<SequenceI> prot = new ArrayList<SequenceI>();
225 String pdbId = (String) ms.getInfo(0, "title");
226 boolean isMMCIF = false;
227 String jmolFileType_String = (String) ms.getInfo(0, "fileType");
228 if (updateFileType(jmolFileType_String))
230 setStructureFileType(jmolFiletype.toString());
233 isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
237 setId(safeName(getDataName()));
238 setPDBIdAvailable(false);
243 setPDBIdAvailable(true);
244 setAlphafoldModel(alphaFold.search(pdbId) && isMMCIF);
246 List<Atom> significantAtoms = convertSignificantAtoms(ms);
247 for (Atom tmpatom : significantAtoms)
249 if (tmpatom.resNumIns.trim().equals(lastID))
251 // phosphorylated protein - seen both CA and P..
254 tmpchain = findChain(tmpatom.chain);
255 if (tmpchain != null)
257 tmpchain.atoms.addElement(tmpatom);
261 AnnotationRowBuilder builder = null;
262 if (isAlphafoldModel()
263 || getTemperatureFactorType() == StructureImportSettings.TFType.PLDDT)
265 builder = new AlphaFoldAnnotationRowBuilder();
268 tmpchain = new PDBChain(getId(), tmpatom.chain, builder);
269 getChains().add(tmpchain);
270 tmpchain.atoms.addElement(tmpatom);
272 lastID = tmpatom.resNumIns.trim();
274 if (isParseImmediately() && localDoXferSettings)
276 // configure parsing settings from the static singleton
283 for (PDBChain chain : getChains())
285 SequenceI chainseq = postProcessChain(chain);
295 // look at local setting for adding secondary tructure
296 if (predictSecondaryStructure)
298 createAnnotation(chainseq, chain, ms.at);
301 // if Alphafold, fetch the PAE matrix if doesn't already have one
302 if (isAlphafoldModel() && !hasPAEMatrix())
306 Console.info("Retrieving PAE for " + pdbId);
307 File paeFile = EBIAlfaFold.fetchAlphaFoldPAE(pdbId, null);
308 this.setPAEMatrix(paeFile.getAbsolutePath());
309 } catch (Throwable t)
311 Console.error("Couldn't get the pAE for " + pdbId, t);
314 // add a PAEMatrix if set (either by above or otherwise)
319 Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
320 EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
321 null, false, false, null);
323 if (al.getAlignmentAnnotation() != null)
325 for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
327 annotations.add(alann);
330 } catch (Throwable ff)
332 Console.error("Couldn't import PAE Matrix from " + getPAEMatrix(),
334 warningMessage += "Couldn't import PAE Matrix"
335 + getNewlineString() + ff.getLocalizedMessage()
336 + getNewlineString();
339 } catch (OutOfMemoryError er)
342 "OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
343 throw new IOException(MessageManager
344 .getString("exception.outofmemory_loading_mmcif_file"));
348 private List<Atom> convertSignificantAtoms(ModelSet ms)
350 List<Atom> significantAtoms = new ArrayList<Atom>();
351 HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
352 org.jmol.modelset.Atom prevAtom = null;
353 for (org.jmol.modelset.Atom atom : ms.at)
355 if (atom.getAtomName().equalsIgnoreCase("CA")
356 || atom.getAtomName().equalsIgnoreCase("P"))
358 if (!atomValidated(atom, prevAtom, chainTerMap))
362 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
363 curAtom.atomIndex = atom.getIndex();
364 curAtom.chain = atom.getChainIDStr();
365 curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
366 : atom.group.getInsertionCode();
367 curAtom.name = atom.getAtomName();
368 curAtom.number = atom.getAtomNumber();
369 curAtom.resName = atom.getGroup3(true);
370 curAtom.resNumber = atom.getResno();
371 curAtom.occupancy = ms.occupancies != null
372 ? ms.occupancies[atom.getIndex()]
373 : Float.valueOf(atom.getOccupancy100());
374 String fmt = new Format("%4i").form(curAtom.resNumber);
375 curAtom.resNumIns = (fmt + curAtom.insCode);
376 curAtom.tfactor = atom.getBfactor100() / 100f;
378 // significantAtoms.add(curAtom);
379 // ignore atoms from subsequent models
380 if (!significantAtoms.contains(curAtom))
382 significantAtoms.add(curAtom);
387 return significantAtoms;
390 private boolean atomValidated(org.jmol.modelset.Atom curAtom,
391 org.jmol.modelset.Atom prevAtom,
392 HashMap<String, org.jmol.modelset.Atom> chainTerMap)
394 // System.out.println("Atom: " + curAtom.getAtomNumber()
395 // + " Last atom index " + curAtom.group.lastAtomIndex);
396 if (chainTerMap == null || prevAtom == null)
400 String curAtomChId = curAtom.getChainIDStr();
401 String prevAtomChId = prevAtom.getChainIDStr();
402 // new chain encoutered
403 if (!prevAtomChId.equals(curAtomChId))
405 // On chain switch add previous chain termination to xTerMap if not exists
406 if (!chainTerMap.containsKey(prevAtomChId))
408 chainTerMap.put(prevAtomChId, prevAtom);
410 // if current atom belongs to an already terminated chain and the resNum
411 // diff < 5 then mark as valid and update termination Atom
412 if (chainTerMap.containsKey(curAtomChId))
414 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
418 if ((curAtom.getResno()
419 - chainTerMap.get(curAtomChId).getResno()) < 5)
421 chainTerMap.put(curAtomChId, curAtom);
427 // atom with previously terminated chain encountered
428 else if (chainTerMap.containsKey(curAtomChId))
430 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
434 if ((curAtom.getResno()
435 - chainTerMap.get(curAtomChId).getResno()) < 5)
437 chainTerMap.put(curAtomChId, curAtom);
442 // HETATM with resNum jump > 2
443 return !(curAtom.isHetero()
444 && ((curAtom.getResno() - prevAtom.getResno()) > 2));
447 private void createAnnotation(SequenceI sequence, PDBChain chain,
448 org.jmol.modelset.Atom[] jmolAtoms)
450 char[] secstr = new char[sequence.getLength()];
451 char[] secstrcode = new char[sequence.getLength()];
453 // Ensure Residue size equals Seq size
454 if (chain.residues.size() != sequence.getLength())
459 for (Residue residue : chain.residues)
461 Atom repAtom = residue.getAtoms().get(0);
462 STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
463 .getProteinStructureSubType();
464 setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
468 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
469 secstrcode, chain.id, sequence.getStart());
473 * Helper method that adds an AlignmentAnnotation for secondary structure to
474 * the sequence, provided at least one secondary structure assignment has been
485 protected void addSecondaryStructureAnnotation(String modelTitle,
486 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
489 int length = sq.getLength();
490 boolean ssFound = false;
491 Annotation asecstr[] = new Annotation[length + (firstResNum-sq.getStart())];
492 for (int p = 0; p < length; p++)
494 if (secstr[p] >= 'A' && secstr[p] <= 'z')
498 asecstr[p] = new Annotation(null, null, secstrcode[p], Float.NaN);
500 } catch (Exception e)
502 // e.printStackTrace();
509 String mt = modelTitle == null ? getDataName() : modelTitle;
511 AlignmentAnnotation ann = new AlignmentAnnotation(
512 "Secondary Structure", "Secondary Structure for " + mt,
514 ann.belowAlignment = true;
516 ann.autoCalculated = false;
517 ann.setCalcId(getClass().getName());
518 ann.adjustForAlignment();
519 ann.validateRangeAndDisplay();
520 annotations.add(ann);
521 sq.addAlignmentAnnotation(ann);
525 private void waitForScript(Viewer jmd)
527 while (jmd.isScriptExecuting())
533 } catch (InterruptedException x)
540 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
541 * secondary structure arrays at the given sequence position
543 * @param proteinStructureSubType
548 protected void setSecondaryStructure(STR proteinStructureSubType, int pos,
549 char[] secstr, char[] secstrcode)
551 switch (proteinStructureSubType)
570 switch (proteinStructureSubType)
576 secstrcode[pos] = 'H';
579 secstrcode[pos] = 'E';
587 * Convert any non-standard peptide codes to their standard code table
588 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
590 * @param threeLetterCode
594 protected void replaceNonCanonicalResidue(String threeLetterCode,
597 String canonical = ResidueProperties
598 .getCanonicalAminoAcid(threeLetterCode);
599 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
601 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
606 * Not implemented - returns null
609 public String print(SequenceI[] seqs, boolean jvSuffix)
618 public void setCallbackFunction(String callbackType,
619 String callbackFunction)
624 public void notifyCallback(CBK cbType, Object[] data)
626 String strInfo = (data == null || data[1] == null ? null
627 : data[1].toString());
631 sendConsoleEcho(strInfo);
634 notifyScriptTermination((String) data[2],
635 ((Integer) data[3]).intValue());
638 String mystatus = (String) data[3];
639 if (mystatus.indexOf("Picked") >= 0
640 || mystatus.indexOf("Sequence") >= 0)
643 sendConsoleMessage(strInfo);
645 else if (mystatus.indexOf("Completed") >= 0)
647 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
648 strInfo.length() - 1));
652 sendConsoleMessage(data == null ? null : strInfo);
655 sendConsoleMessage(strInfo);
662 String lastConsoleEcho = "";
664 private void sendConsoleEcho(String string)
666 lastConsoleEcho += string;
667 lastConsoleEcho += "\n";
670 String lastConsoleMessage = "";
672 private void sendConsoleMessage(String string)
674 lastConsoleMessage += string;
675 lastConsoleMessage += "\n";
678 int lastScriptTermination = -1;
680 String lastScriptMessage = "";
682 private void notifyScriptTermination(String string, int intValue)
684 lastScriptMessage += string;
685 lastScriptMessage += "\n";
686 lastScriptTermination = intValue;
690 public boolean notifyEnabled(CBK callbackPick)
692 switch (callbackPick)
706 * Not implemented - returns null
709 public String eval(String strEval)
715 * Not implemented - returns null
718 public float[][] functionXY(String functionName, int x, int y)
724 * Not implemented - returns null
727 public float[][][] functionXYZ(String functionName, int nx, int ny,
734 * Not implemented - returns null
737 public String createImage(String fileName, String imageType,
738 Object text_or_bytes, int quality)
744 * Not implemented - returns null
747 public Map<String, Object> getRegistryInfo()
756 public void showUrl(String url)
761 * Not implemented - returns null
764 public int[] resizeInnerPanel(String data)
770 public Map<String, Object> getJSpecViewProperty(String arg0)
775 public boolean isPredictSecondaryStructure()
777 return predictSecondaryStructure;
780 public void setPredictSecondaryStructure(
781 boolean predictSecondaryStructure)
783 this.predictSecondaryStructure = predictSecondaryStructure;
786 public boolean isVisibleChainAnnotation()
788 return visibleChainAnnotation;
791 public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
793 this.visibleChainAnnotation = visibleChainAnnotation;