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);
96 public JmolParser(FileParse fp) throws IOException
106 * Calls the Jmol library to parse the PDB/mmCIF file, and then inspects the
107 * resulting object model to generate Jalview-style sequences, with secondary
108 * structure annotation added where available (i.e. where it has been computed
109 * by Jmol using DSSP).
111 * @see jalview.io.AlignFile#parse()
114 public void parse() throws IOException
120 public void parse(boolean doXferSettings) throws IOException
122 setChains(new Vector<PDBChain>());
123 Viewer jmolModel = getJmolData();
124 jmolModel.openReader(getDataName(), getDataName(), getReader());
125 waitForScript(jmolModel);
128 * Convert one or more Jmol Model objects to Jalview sequences
130 if (jmolModel.ms.mc > 0)
132 // ideally we do this
135 // setStructureFileType(jmolModel.evalString("show _fileType"));
136 // } catch (Exception q)
140 // instead, we distinguish .cif from non-.cif by filename
141 setStructureFileType(
142 getDataName().toLowerCase(Locale.ROOT).endsWith(".cif")
143 ? PDBEntry.Type.MMCIF.toString()
146 transformJmolModelToJalview(jmolModel.ms, doXferSettings);
151 * create a headless jmol instance for dataprocessing
155 private Viewer getJmolData()
162 * params -o (output to sysout) -n (nodisplay) -x (exit when finished)
163 * see http://wiki.jmol.org/index.php/Jmol_Application
166 viewer = JalviewJmolBinding.getJmolData(this);
167 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
168 viewer.setBooleanProperty("defaultStructureDSSP", true);
169 } catch (ClassCastException x)
171 throw new Error(MessageManager.formatMessage(
172 "error.jmol_version_not_compatible_with_jalview_version",
174 { JmolViewer.getJmolVersion() }), x);
180 public static Regex getNewAlphafoldValidator()
182 Regex validator = new Regex("(AF-[A-Z]+[0-9]+[A-Z0-9]+-F1)");
183 validator.setIgnoreCase(true);
187 PDBEntry.Type jmolFiletype = null;
190 * resolve a jmol filetype string and update the jmolFiletype field
193 * @param jmolIdentifiedFileType
194 * @return true if filetype was identified as MMCIF, PDB
196 public boolean updateFileType(String jmolIdentifiedFileType)
198 if (jmolIdentifiedFileType == null
199 || jmolIdentifiedFileType.trim().equals(""))
203 if ("mmcif".equalsIgnoreCase(jmolIdentifiedFileType))
205 jmolFiletype = PDBEntry.Type.MMCIF;
208 if ("pdb".equalsIgnoreCase(jmolIdentifiedFileType))
210 jmolFiletype = PDBEntry.Type.PDB;
216 public void transformJmolModelToJalview(ModelSet ms,
217 boolean localDoXferSettings) throws IOException
221 Regex alphaFold = getNewAlphafoldValidator();
223 List<SequenceI> rna = new ArrayList<SequenceI>();
224 List<SequenceI> prot = new ArrayList<SequenceI>();
226 String pdbId = (String) ms.getInfo(0, "title");
227 boolean isMMCIF = false;
228 String jmolFileType_String = (String) ms.getInfo(0, "fileType");
229 if (updateFileType(jmolFileType_String))
231 setStructureFileType(jmolFiletype.toString());
234 isMMCIF = PDBEntry.Type.MMCIF.equals(jmolFiletype);
238 setId(safeName(getDataName()));
239 setPDBIdAvailable(false);
244 setPDBIdAvailable(true);
245 setAlphafoldModel(alphaFold.search(pdbId) && isMMCIF);
247 List<Atom> significantAtoms = convertSignificantAtoms(ms);
248 for (Atom tmpatom : significantAtoms)
250 if (tmpatom.resNumIns.trim().equals(lastID))
252 // phosphorylated protein - seen both CA and P..
255 tmpchain = findChain(tmpatom.chain);
256 if (tmpchain != null)
258 tmpchain.atoms.addElement(tmpatom);
262 AnnotationRowBuilder builder = null;
263 if (isAlphafoldModel()
264 || getTemperatureFactorType() == StructureImportSettings.TFType.PLDDT)
266 builder = new AlphaFoldAnnotationRowBuilder();
269 tmpchain = new PDBChain(getId(), tmpatom.chain, builder);
270 getChains().add(tmpchain);
271 tmpchain.atoms.addElement(tmpatom);
273 lastID = tmpatom.resNumIns.trim();
275 if (isParseImmediately() && localDoXferSettings)
277 // configure parsing settings from the static singleton
284 for (PDBChain chain : getChains())
286 SequenceI chainseq = postProcessChain(chain);
296 // look at local setting for adding secondary tructure
297 if (predictSecondaryStructure)
299 createAnnotation(chainseq, chain, ms.at);
302 // if Alphafold, fetch the PAE matrix if doesn't already have one
303 if (isAlphafoldModel() && !hasPAEMatrix())
307 Console.info("Retrieving PAE for " + pdbId);
308 File paeFile = EBIAlfaFold.fetchAlphaFoldPAE(pdbId, null);
309 this.setPAEMatrix(paeFile.getAbsolutePath());
310 } catch (Throwable t)
312 Console.error("Couldn't get the pAE for " + pdbId, t);
315 // add a PAEMatrix if set (either by above or otherwise)
318 Alignment al = new Alignment(prot.toArray(new SequenceI[0]));
319 EBIAlfaFold.addAlphaFoldPAE(al, new File(this.getPAEMatrix()), 0,
320 null, false, false, null);
322 if (al.getAlignmentAnnotation() != null)
324 for (AlignmentAnnotation alann : al.getAlignmentAnnotation())
326 annotations.add(alann);
330 } catch (OutOfMemoryError er)
333 "OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
334 throw new IOException(MessageManager
335 .getString("exception.outofmemory_loading_mmcif_file"));
339 private List<Atom> convertSignificantAtoms(ModelSet ms)
341 List<Atom> significantAtoms = new ArrayList<Atom>();
342 HashMap<String, org.jmol.modelset.Atom> chainTerMap = new HashMap<String, org.jmol.modelset.Atom>();
343 org.jmol.modelset.Atom prevAtom = null;
344 for (org.jmol.modelset.Atom atom : ms.at)
346 if (atom.getAtomName().equalsIgnoreCase("CA")
347 || atom.getAtomName().equalsIgnoreCase("P"))
349 if (!atomValidated(atom, prevAtom, chainTerMap))
353 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
354 curAtom.atomIndex = atom.getIndex();
355 curAtom.chain = atom.getChainIDStr();
356 curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' '
357 : atom.group.getInsertionCode();
358 curAtom.name = atom.getAtomName();
359 curAtom.number = atom.getAtomNumber();
360 curAtom.resName = atom.getGroup3(true);
361 curAtom.resNumber = atom.getResno();
362 curAtom.occupancy = ms.occupancies != null
363 ? ms.occupancies[atom.getIndex()]
364 : Float.valueOf(atom.getOccupancy100());
365 String fmt = new Format("%4i").form(curAtom.resNumber);
366 curAtom.resNumIns = (fmt + curAtom.insCode);
367 curAtom.tfactor = atom.getBfactor100() / 100f;
369 // significantAtoms.add(curAtom);
370 // ignore atoms from subsequent models
371 if (!significantAtoms.contains(curAtom))
373 significantAtoms.add(curAtom);
378 return significantAtoms;
381 private boolean atomValidated(org.jmol.modelset.Atom curAtom,
382 org.jmol.modelset.Atom prevAtom,
383 HashMap<String, org.jmol.modelset.Atom> chainTerMap)
385 // System.out.println("Atom: " + curAtom.getAtomNumber()
386 // + " Last atom index " + curAtom.group.lastAtomIndex);
387 if (chainTerMap == null || prevAtom == null)
391 String curAtomChId = curAtom.getChainIDStr();
392 String prevAtomChId = prevAtom.getChainIDStr();
393 // new chain encoutered
394 if (!prevAtomChId.equals(curAtomChId))
396 // On chain switch add previous chain termination to xTerMap if not exists
397 if (!chainTerMap.containsKey(prevAtomChId))
399 chainTerMap.put(prevAtomChId, prevAtom);
401 // if current atom belongs to an already terminated chain and the resNum
402 // diff < 5 then mark as valid and update termination Atom
403 if (chainTerMap.containsKey(curAtomChId))
405 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
409 if ((curAtom.getResno()
410 - chainTerMap.get(curAtomChId).getResno()) < 5)
412 chainTerMap.put(curAtomChId, curAtom);
418 // atom with previously terminated chain encountered
419 else if (chainTerMap.containsKey(curAtomChId))
421 if (curAtom.getResno() < chainTerMap.get(curAtomChId).getResno())
425 if ((curAtom.getResno()
426 - chainTerMap.get(curAtomChId).getResno()) < 5)
428 chainTerMap.put(curAtomChId, curAtom);
433 // HETATM with resNum jump > 2
434 return !(curAtom.isHetero()
435 && ((curAtom.getResno() - prevAtom.getResno()) > 2));
438 private void createAnnotation(SequenceI sequence, PDBChain chain,
439 org.jmol.modelset.Atom[] jmolAtoms)
441 char[] secstr = new char[sequence.getLength()];
442 char[] secstrcode = new char[sequence.getLength()];
444 // Ensure Residue size equals Seq size
445 if (chain.residues.size() != sequence.getLength())
450 for (Residue residue : chain.residues)
452 Atom repAtom = residue.getAtoms().get(0);
453 STR proteinStructureSubType = jmolAtoms[repAtom.atomIndex].group
454 .getProteinStructureSubType();
455 setSecondaryStructure(proteinStructureSubType, annotIndex, secstr,
459 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
460 secstrcode, chain.id, sequence.getStart());
464 * Helper method that adds an AlignmentAnnotation for secondary structure to
465 * the sequence, provided at least one secondary structure assignment has been
476 protected void addSecondaryStructureAnnotation(String modelTitle,
477 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
480 int length = sq.getLength();
481 boolean ssFound = false;
482 Annotation asecstr[] = new Annotation[length + firstResNum - 1];
483 for (int p = 0; p < length; p++)
485 if (secstr[p] >= 'A' && secstr[p] <= 'z')
489 asecstr[p] = new Annotation(null, null, secstrcode[p], Float.NaN);
491 } catch (Exception e)
493 // e.printStackTrace();
500 String mt = modelTitle == null ? getDataName() : modelTitle;
502 AlignmentAnnotation ann = new AlignmentAnnotation(
503 "Secondary Structure", "Secondary Structure for " + mt,
505 ann.belowAlignment = true;
507 ann.autoCalculated = false;
508 ann.setCalcId(getClass().getName());
509 ann.adjustForAlignment();
510 ann.validateRangeAndDisplay();
511 annotations.add(ann);
512 sq.addAlignmentAnnotation(ann);
516 private void waitForScript(Viewer jmd)
518 while (jmd.isScriptExecuting())
524 } catch (InterruptedException x)
531 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
532 * secondary structure arrays at the given sequence position
534 * @param proteinStructureSubType
539 protected void setSecondaryStructure(STR proteinStructureSubType, int pos,
540 char[] secstr, char[] secstrcode)
542 switch (proteinStructureSubType)
561 switch (proteinStructureSubType)
567 secstrcode[pos] = 'H';
570 secstrcode[pos] = 'E';
578 * Convert any non-standard peptide codes to their standard code table
579 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
581 * @param threeLetterCode
585 protected void replaceNonCanonicalResidue(String threeLetterCode,
588 String canonical = ResidueProperties
589 .getCanonicalAminoAcid(threeLetterCode);
590 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
592 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
597 * Not implemented - returns null
600 public String print(SequenceI[] seqs, boolean jvSuffix)
609 public void setCallbackFunction(String callbackType,
610 String callbackFunction)
615 public void notifyCallback(CBK cbType, Object[] data)
617 String strInfo = (data == null || data[1] == null ? null
618 : data[1].toString());
622 sendConsoleEcho(strInfo);
625 notifyScriptTermination((String) data[2],
626 ((Integer) data[3]).intValue());
629 String mystatus = (String) data[3];
630 if (mystatus.indexOf("Picked") >= 0
631 || mystatus.indexOf("Sequence") >= 0)
634 sendConsoleMessage(strInfo);
636 else if (mystatus.indexOf("Completed") >= 0)
638 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
639 strInfo.length() - 1));
643 sendConsoleMessage(data == null ? null : strInfo);
646 sendConsoleMessage(strInfo);
653 String lastConsoleEcho = "";
655 private void sendConsoleEcho(String string)
657 lastConsoleEcho += string;
658 lastConsoleEcho += "\n";
661 String lastConsoleMessage = "";
663 private void sendConsoleMessage(String string)
665 lastConsoleMessage += string;
666 lastConsoleMessage += "\n";
669 int lastScriptTermination = -1;
671 String lastScriptMessage = "";
673 private void notifyScriptTermination(String string, int intValue)
675 lastScriptMessage += string;
676 lastScriptMessage += "\n";
677 lastScriptTermination = intValue;
681 public boolean notifyEnabled(CBK callbackPick)
683 switch (callbackPick)
697 * Not implemented - returns null
700 public String eval(String strEval)
706 * Not implemented - returns null
709 public float[][] functionXY(String functionName, int x, int y)
715 * Not implemented - returns null
718 public float[][][] functionXYZ(String functionName, int nx, int ny,
725 * Not implemented - returns null
728 public String createImage(String fileName, String imageType,
729 Object text_or_bytes, int quality)
735 * Not implemented - returns null
738 public Map<String, Object> getRegistryInfo()
747 public void showUrl(String url)
752 * Not implemented - returns null
755 public int[] resizeInnerPanel(String data)
761 public Map<String, Object> getJSpecViewProperty(String arg0)
766 public boolean isPredictSecondaryStructure()
768 return predictSecondaryStructure;
771 public void setPredictSecondaryStructure(
772 boolean predictSecondaryStructure)
774 this.predictSecondaryStructure = predictSecondaryStructure;
777 public boolean isVisibleChainAnnotation()
779 return visibleChainAnnotation;
782 public void setVisibleChainAnnotation(boolean visibleChainAnnotation)
784 this.visibleChainAnnotation = visibleChainAnnotation;