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) throws IOException
101 * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
102 * resulting object model to generate Jalview-style sequences, with secondary
103 * structure annotation added where available (i.e. where it has been computed
104 * by Jmol using DSSP).
106 * @see jalview.io.AlignFile#parse()
109 public void parse() throws IOException
111 setChains(new Vector<PDBChain>());
112 Viewer jmolModel = getJmolData();
113 jmolModel.openReader(getDataName(), getDataName(), getReader());
114 waitForScript(jmolModel);
117 * Convert one or more Jmol Model objects to Jalview sequences
119 if (jmolModel.ms.mc > 0)
121 // ideally we do this
124 // setStructureFileType(jmolModel.evalString("show _fileType"));
125 // } catch (Exception q)
129 // instead, we distinguish .cif from non-.cif by filename
130 setStructureFileType(
131 getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
132 ? PDBEntry.Type.MMCIF.toString()
135 transformJmolModelToJalview(jmolModel.ms);
140 * create a headless jmol instance for dataprocessing
144 private Viewer getJmolData()
151 * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
152 * see http://wiki.jmol.org/index.php/Jmol_Application
155 viewer = JalviewJmolBinding.getJmolData(this);
156 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
157 viewer.setBooleanProperty("defaultStructureDSSP", true);
158 } catch (ClassCastException x)
160 throw new Error(MessageManager.formatMessage(
161 "error.jmol_version_not_compatible_with_jalview_version",
163 { JmolViewer.getJmolVersion() }), x);
169 public static Regex getNewAlphafoldValidator()
171 Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
172 validator.setIgnoreCase(true);
176 PDBEntry.Type jmolFiletype = null;
179 * resolve a jmol filetype string and update the jmolFiletype field
182 * @param jmolIdentifiedFileType
183 * @return true if filetype was identified as MMCIF, PDB
185 public boolean updateFileType(String jmolIdentifiedFileType)
187 if (jmolIdentifiedFileType == null
188 || jmolIdentifiedFileType.trim().equals(""))
192 if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType))
194 jmolFiletype = PDBEntry.Type.MMCIF;
197 if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
199 jmolFiletype = PDBEntry.Type.PDB;
205 public void transformJmolModelToJalview(ModelSet ms) throws IOException
209 Regex alphaFold = getNewAlphafoldValidator();
211 List<SequenceI> rna = new ArrayList<SequenceI>();
212 List<SequenceI> prot = new ArrayList<SequenceI>();
214 String pdbId = (String) ms.getInfo(0, "title");
215 boolean isMMCIF = false;
216 String jmolFileType_String = (String) ms.getInfo(0, "fileType");
217 if (updateFileType(jmolFileType_String))
219 setStructureFileType(jmolFiletype.toString());
222 isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
226 setId(safeName(getDataName()));
227 setPDBIdAvailable(false);
232 setPDBIdAvailable(true);
233 setAlphafoldModel(alphaFold.search(pdbId) && isMMCIF);
235 List<Atom> significantAtoms = convertSignificantAtoms(ms);
236 for (Atom tmpatom : significantAtoms)
238 if (tmpatom.resNumIns.trim().equals(lastID))
240 // phosphorylated protein - seen both CA and P..
243 tmpchain = findChain(tmpatom.chain);
244 if (tmpchain != null)
246 tmpchain.atoms.addElement(tmpatom);
250 AnnotationRowBuilder builder = null;
251 if (isAlphafoldModel()
252 || getTemperatureFactorType() == StructureImportSettings.TFType.PLDDT)
254 builder = new AlphaFoldAnnotationRowBuilder();
257 tmpchain = new PDBChain(getId(), tmpatom.chain, builder);
258 getChains().add(tmpchain);
259 tmpchain.atoms.addElement(tmpatom);
261 lastID = tmpatom.resNumIns.trim();
263 if (isParseImmediately())
265 // configure parsing settings from the static singleton
272 for (PDBChain chain : getChains())
274 SequenceI chainseq = postProcessChain(chain);
284 // look at local setting for adding secondary tructure
285 if (predictSecondaryStructure)
287 createAnnotation(chainseq, chain, ms.at);
290 // if Alphafold, fetch the PAE matrix if doesn't already have one
291 if (isAlphafoldModel() && !hasPAEMatrix())
295 Console.info("retrieving pAE for " + pdbId);
296 File paeFile = EBIAlfaFold.fetchAlphaFoldPAE(pdbId, null);
297 this.setPAEMatrix(paeFile.getAbsolutePath());
298 } catch (Throwable t)
300 Console.error("Couldn't get the pAE for " + pdbId, t);
303 // add a PAEMatrix if set (either by above or otherwise)
306 Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
307 EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
308 null, false, false, null);
310 if (al.getAlignmentAnnotation() != null)
312 for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
314 annotations.add(alann);
318 } catch (OutOfMemoryError er)
321 "OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
322 throw new IOException(MessageManager
323 .getString("exception.outofmemory_loading_mmcif_file"));
327 private List<Atom> convertSignificantAtoms(ModelSet ms)
329 List<Atom> significantAtoms = new ArrayList<Atom>();
330 HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
331 org.jmol.modelset.Atom prevAtom = null;
332 for (org.jmol.modelset.Atom atom : ms.at)
334 if (atom.getAtomName().equalsIgnoreCase("CA")
335 || atom.getAtomName().equalsIgnoreCase("P"))
337 if (!atomValidated(atom, prevAtom, chainTerMap))
341 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
342 curAtom.atomIndex = atom.getIndex();
343 curAtom.chain = atom.getChainIDStr();
344 curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
345 : atom.group.getInsertionCode();
346 curAtom.name = atom.getAtomName();
347 curAtom.number = atom.getAtomNumber();
348 curAtom.resName = atom.getGroup3(true);
349 curAtom.resNumber = atom.getResno();
350 curAtom.occupancy = ms.occupancies != null
351 ? ms.occupancies[atom.getIndex()]
352 : Float.valueOf(atom.getOccupancy100());
353 String fmt = new Format("%4i").form(curAtom.resNumber);
354 curAtom.resNumIns = (fmt + curAtom.insCode);
355 curAtom.tfactor = atom.getBfactor100() / 100f;
357 // significantAtoms.add(curAtom);
358 // ignore atoms from subsequent models
359 if (!significantAtoms.contains(curAtom))
361 significantAtoms.add(curAtom);
366 return significantAtoms;
369 private boolean atomValidated(org.jmol.modelset.Atom curAtom,
370 org.jmol.modelset.Atom prevAtom,
371 HashMap<String, org.jmol.modelset.Atom> chainTerMap)
373 // System.out.println("Atom: " + curAtom.getAtomNumber()
374 // + " Last atom index " + curAtom.group.lastAtomIndex);
375 if (chainTerMap == null || prevAtom == null)
379 String curAtomChId = curAtom.getChainIDStr();
380 String prevAtomChId = prevAtom.getChainIDStr();
381 // new chain encoutered
382 if (!prevAtomChId.equals(curAtomChId))
384 // On chain switch add previous chain termination to xTerMap if not exists
385 if (!chainTerMap.containsKey(prevAtomChId))
387 chainTerMap.put(prevAtomChId, prevAtom);
389 // if current atom belongs to an already terminated chain and the resNum
390 // diff < 5 then mark as valid and update termination Atom
391 if (chainTerMap.containsKey(curAtomChId))
393 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
397 if ((curAtom.getResno()
398 - chainTerMap.get(curAtomChId).getResno()) < 5)
400 chainTerMap.put(curAtomChId, curAtom);
406 // atom with previously terminated chain encountered
407 else if (chainTerMap.containsKey(curAtomChId))
409 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
413 if ((curAtom.getResno()
414 - chainTerMap.get(curAtomChId).getResno()) < 5)
416 chainTerMap.put(curAtomChId, curAtom);
421 // HETATM with resNum jump > 2
422 return !(curAtom.isHetero()
423 && ((curAtom.getResno() - prevAtom.getResno()) > 2));
426 private void createAnnotation(SequenceI sequence, PDBChain chain,
427 org.jmol.modelset.Atom[] jmolAtoms)
429 char[] secstr = new char[sequence.getLength()];
430 char[] secstrcode = new char[sequence.getLength()];
432 // Ensure Residue size equals Seq size
433 if (chain.residues.size() != sequence.getLength())
438 for (Residue residue : chain.residues)
440 Atom repAtom = residue.getAtoms().get(0);
441 STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
442 .getProteinStructureSubType();
443 setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
447 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
448 secstrcode, chain.id, sequence.getStart());
452 * Helper method that adds an AlignmentAnnotation for secondary structure to
453 * the sequence, provided at least one secondary structure assignment has been
464 protected void addSecondaryStructureAnnotation(String modelTitle,
465 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
468 int length = sq.getLength();
469 boolean ssFound = false;
470 Annotation asecstr[] = new Annotation[length + firstResNum - 1];
471 for (int p = 0; p < length; p++)
473 if (secstr[p] >= 'A' && secstr[p] <= 'z')
477 asecstr[p] = new Annotation(null, null, secstrcode[p], Float.NaN);
479 } catch (Exception e)
481 // e.printStackTrace();
488 String mt = modelTitle == null ? getDataName() : modelTitle;
490 AlignmentAnnotation ann = new AlignmentAnnotation(
491 "Secondary Structure", "Secondary Structure for " + mt,
493 ann.belowAlignment = true;
495 ann.autoCalculated = false;
496 ann.setCalcId(getClass().getName());
497 ann.adjustForAlignment();
498 ann.validateRangeAndDisplay();
499 annotations.add(ann);
500 sq.addAlignmentAnnotation(ann);
504 private void waitForScript(Viewer jmd)
506 while (jmd.isScriptExecuting())
512 } catch (InterruptedException x)
519 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
520 * secondary structure arrays at the given sequence position
522 * @param proteinStructureSubType
527 protected void setSecondaryStructure(STR proteinStructureSubType, int pos,
528 char[] secstr, char[] secstrcode)
530 switch (proteinStructureSubType)
549 switch (proteinStructureSubType)
555 secstrcode[pos] = 'H';
558 secstrcode[pos] = 'E';
566 * Convert any non-standard peptide codes to their standard code table
567 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
569 * @param threeLetterCode
573 protected void replaceNonCanonicalResidue(String threeLetterCode,
576 String canonical = ResidueProperties
577 .getCanonicalAminoAcid(threeLetterCode);
578 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
580 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
585 * Not implemented - returns null
588 public String print(SequenceI[] seqs, boolean jvSuffix)
597 public void setCallbackFunction(String callbackType,
598 String callbackFunction)
603 public void notifyCallback(CBK cbType, Object[] data)
605 String strInfo = (data == null || data[1] == null ? null
606 : data[1].toString());
610 sendConsoleEcho(strInfo);
613 notifyScriptTermination((String) data[2],
614 ((Integer) data[3]).intValue());
617 String mystatus = (String) data[3];
618 if (mystatus.indexOf("Picked") >= 0
619 || mystatus.indexOf("Sequence") >= 0)
622 sendConsoleMessage(strInfo);
624 else if (mystatus.indexOf("Completed") >= 0)
626 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
627 strInfo.length() - 1));
631 sendConsoleMessage(data == null ? null : strInfo);
634 sendConsoleMessage(strInfo);
641 String lastConsoleEcho = "";
643 private void sendConsoleEcho(String string)
645 lastConsoleEcho += string;
646 lastConsoleEcho += "\n";
649 String lastConsoleMessage = "";
651 private void sendConsoleMessage(String string)
653 lastConsoleMessage += string;
654 lastConsoleMessage += "\n";
657 int lastScriptTermination = -1;
659 String lastScriptMessage = "";
661 private void notifyScriptTermination(String string, int intValue)
663 lastScriptMessage += string;
664 lastScriptMessage += "\n";
665 lastScriptTermination = intValue;
669 public boolean notifyEnabled(CBK callbackPick)
671 switch (callbackPick)
685 * Not implemented - returns null
688 public String eval(String strEval)
694 * Not implemented - returns null
697 public float[][] functionXY(String functionName, int x, int y)
703 * Not implemented - returns null
706 public float[][][] functionXYZ(String functionName, int nx, int ny,
713 * Not implemented - returns null
716 public String createImage(String fileName, String imageType,
717 Object text_or_bytes, int quality)
723 * Not implemented - returns null
726 public Map<String, Object> getRegistryInfo()
735 public void showUrl(String url)
740 * Not implemented - returns null
743 public int[] resizeInnerPanel(String data)
749 public Map<String, Object> getJSpecViewProperty(String arg0)
754 public boolean isPredictSecondaryStructure()
756 return predictSecondaryStructure;
759 public void setPredictSecondaryStructure(
760 boolean predictSecondaryStructure)
762 this.predictSecondaryStructure = predictSecondaryStructure;
765 public boolean isVisibleChainAnnotation()
767 return visibleChainAnnotation;
770 public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
772 this.visibleChainAnnotation = visibleChainAnnotation;