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;
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.Annotation;
25 import jalview.datamodel.DBRefEntry;
26 import jalview.datamodel.DBRefSource;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.Sequence;
29 import jalview.datamodel.SequenceI;
30 import jalview.io.FileParse;
31 import jalview.io.StructureFile;
32 import jalview.schemes.ResidueProperties;
33 import jalview.util.Comparison;
34 import jalview.util.MessageManager;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Hashtable;
39 import java.util.List;
41 import java.util.Vector;
43 import javajs.awt.Dimension;
45 import org.jmol.api.JmolStatusListener;
46 import org.jmol.api.JmolViewer;
47 import org.jmol.c.CBK;
48 import org.jmol.c.STR;
49 import org.jmol.modelset.Group;
50 import org.jmol.modelset.Model;
51 import org.jmol.modelset.ModelSet;
52 import org.jmol.modelsetbio.BioModel;
53 import org.jmol.modelsetbio.BioPolymer;
54 import org.jmol.modelsetbio.Monomer;
55 import org.jmol.viewer.Viewer;
58 import MCview.PDBChain;
59 import MCview.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 addAlignmentAnnotations,
72 boolean predictSecondaryStructure, boolean externalSecStr,
73 String inFile, String type) throws IOException
76 this.visibleChainAnnotation = addAlignmentAnnotations;
77 this.predictSecondaryStructure = predictSecondaryStructure;
78 this.externalSecondaryStructure = externalSecStr;
81 public JmolParser(boolean addAlignmentAnnotations,
82 boolean predictSecondaryStructure, boolean externalSecStr,
83 FileParse fp) throws IOException
86 this.visibleChainAnnotation = addAlignmentAnnotations;
87 this.predictSecondaryStructure = predictSecondaryStructure;
88 this.externalSecondaryStructure = externalSecStr;
91 public JmolParser(FileParse fp) throws IOException
96 public JmolParser(String inFile, String type) 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
117 setChains(new Vector<PDBChain>());
118 Viewer jmolModel = getJmolData();
119 jmolModel.openReader(getDataName(), getDataName(), getReader());
120 waitForScript(jmolModel);
123 * Convert one or more Jmol Model objects to Jalview sequences
125 if (jmolModel.ms.mc > 0)
127 parseBiopolymer(jmolModel.ms);
128 // transformJmolModelToJalview(jmolModel.ms);
133 * create a headless jmol instance for dataprocessing
137 private Viewer getJmolData()
143 viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
144 null, "-x -o -n", this);
145 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
146 viewer.setBooleanProperty("defaultStructureDSSP", true);
147 } catch (ClassCastException x)
149 throw new Error(MessageManager.formatMessage(
150 "error.jmol_version_not_compatible_with_jalview_version",
151 new String[] { JmolViewer.getJmolVersion() }), x);
157 public void transformJmolModelToJalview(ModelSet ms) throws IOException
162 List<SequenceI> rna = new ArrayList<SequenceI>();
163 List<SequenceI> prot = new ArrayList<SequenceI>();
165 String pdbId = (String) ms.getInfo(0, "title");
167 List<Atom> significantAtoms = convertSignificantAtoms(ms);
168 for (Atom tmpatom : significantAtoms)
172 tmpchain = findChain(tmpatom.chain);
173 if (tmpatom.resNumIns.trim().equals(lastID))
175 // phosphorylated protein - seen both CA and P..
178 tmpchain.atoms.addElement(tmpatom);
179 } catch (Exception e)
181 tmpchain = new PDBChain(pdbId, tmpatom.chain);
182 getChains().add(tmpchain);
183 tmpchain.atoms.addElement(tmpatom);
185 lastID = tmpatom.resNumIns.trim();
192 setId(inFile.getName());
194 for (PDBChain chain : getChains())
196 SequenceI chainseq = postProcessChain(chain);
197 createAnnotation(chainseq, chain, ms.at);
207 } catch (OutOfMemoryError er)
210 .println("OUT OF MEMORY LOADING TRANSFORMING JMOL MODEL TO JALVIEW MODEL");
211 throw new IOException(
213 .getString("exception.outofmemory_loading_mmcif_file"));
217 private List<Atom> convertSignificantAtoms(ModelSet ms)
219 List<Atom> significantAtoms = new ArrayList<Atom>();
220 for (org.jmol.modelset.Atom atom : ms.at)
222 if (atom.getAtomName().equalsIgnoreCase("CA")
223 || atom.getAtomName().equalsIgnoreCase("P"))
225 Atom curAtom = new Atom(atom.x, atom.y, atom.z);
226 curAtom.atomIndex = atom.getIndex();
227 curAtom.chain = atom.getChainIDStr();
228 curAtom.insCode = atom.group.getInsertionCode();
229 curAtom.name = atom.getAtomName();
230 curAtom.number = atom.getAtomNumber();
231 curAtom.resName = atom.getGroup3(true);
232 curAtom.resNumber = atom.getResno();
233 curAtom.ss = getSecondayStructure(atom.group
234 .getProteinStructureSubType());
235 curAtom.occupancy = ms.occupancies != null ? ms.occupancies[atom
236 .getIndex()] : Float.valueOf(atom.getOccupancy100());
237 curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode;
238 // curAtom.tfactor = atom.group.;
240 significantAtoms.add(curAtom);
243 return significantAtoms;
246 private void createAnnotation(SequenceI sequence, PDBChain chain,
247 org.jmol.modelset.Atom[] jmolAtoms)
249 char[] secstr = new char[sequence.getLength()];
250 char[] secstrcode = new char[sequence.getLength()];
251 for (Residue residue : chain.residues)
255 addSecondaryStructureAnnotation(chain.pdbid, sequence, secstr,
256 secstrcode, chain.id, sequence.getStart());
260 * Process the Jmol BioPolymer array and generate a Jalview sequence for each
261 * chain found (including any secondary structure annotation from DSSP)
264 * @throws IOException
266 public void parseBiopolymer(ModelSet ms) throws IOException
269 for (Model model : ms.am)
272 String modelTitle = (String) ms.getInfo(modelIndex, "title");
274 * Chains can span BioPolymers, so first make a flattened list, and then
275 * work out the lengths of chains present
277 List<Monomer> monomers = getMonomers(ms, (BioModel) model);
278 List<Integer> chainLengths = getChainLengths(monomers);
281 * now chop up the Monomer list to make Jalview Sequences
284 for (int length : chainLengths)
286 buildSequenceFromChain(monomers.subList(from, from + length),
294 * Returns a flattened list of Monomer (residues) in order, across all
295 * BioPolymers in the model. This simplifies assembling chains which span
296 * BioPolymers. The result omits any alternate residues reported for the same
297 * sequence position (RESNUM value).
303 protected List<Monomer> getMonomers(ModelSet ms, BioModel model)
305 List<Monomer> result = new ArrayList<Monomer>();
306 int lastResNo = Integer.MIN_VALUE;
308 for (BioPolymer bp : model.bioPolymers)
310 for (int groupLeadAtoms : bp.getLeadAtomIndices())
312 Group group = ms.at[groupLeadAtoms].group;
313 if (group instanceof Monomer)
316 * ignore alternate residue at same position example: 1ejg has
317 * residues A:LEU, B:ILE at RESNUM=25
319 int resNo = group.getResno();
320 if (lastResNo != resNo)
322 result.add((Monomer) group);
332 * Scans the list of Monomers (residue models), inspecting the chain id for
333 * each, and returns an array whose length is the number of chains, and values
334 * the length of each chain
339 protected List<Integer> getChainLengths(List<Monomer> monomers)
341 List<Integer> chainLengths = new ArrayList<Integer>();
342 int lastChainId = -1;
345 for (Monomer monomer : monomers)
347 int chainId = monomer.chain.chainID;
348 if (chainId != lastChainId && length > 0)
351 * change of chain - record the length of the last one
353 chainLengths.add(length);
356 lastChainId = chainId;
362 * record the length of the final chain
364 chainLengths.add(length);
371 * Helper method to construct a sequence for one chain and add it to the seqs
375 * a list of all monomers in the chain
378 protected void buildSequenceFromChain(List<Monomer> monomers,
381 final int length = monomers.size();
384 * arrays to hold sequence and secondary structure
386 char[] seq = new char[length];
387 char[] secstr = new char[length];
388 char[] secstrcode = new char[length];
391 * populate the sequence and secondary structure arrays
393 extractJmolChainData(monomers, seq, secstr, secstrcode);
396 * grab chain code and start position from first residue;
398 String chainId = monomers.get(0).chain.getIDStr();
399 int firstResNum = monomers.get(0).getResno();
402 // Jalview doesn't like residue < 1, so force this to 1
403 System.err.println("Converting chain " + chainId + " first RESNUM ("
404 + firstResNum + ") to 1");
409 * convert any non-gap unknown residues to 'X'
411 convertNonGapCharacters(seq);
414 * construct and add the Jalview sequence
416 String seqName = "" + modelTitle + "|" + chainId;
417 int start = firstResNum;
418 int end = firstResNum + length - 1;
420 SequenceI sq = new Sequence(seqName, seq, start, end);
422 addPdbid(sq, modelTitle, chainId);
424 addSourceDBref(sq, modelTitle, start, end);
429 * add secondary structure predictions (if any)
431 addSecondaryStructureAnnotation(modelTitle, sq, secstr, secstrcode,
432 chainId, firstResNum);
437 * Scans the list of (Jmol) Monomer objects, and adds the residue for each to
438 * the sequence array, and any converted secondary structure prediction to the
439 * secondary structure arrays
446 protected void extractJmolChainData(List<Monomer> monomers, char[] seq,
447 char[] secstr, char[] secstrcode)
450 for (Monomer monomer : monomers)
452 seq[pos] = monomer.getGroup1();
455 * JAL-1828 replace a modified amino acid with its standard equivalent
456 * (e.g. MSE with MET->M) to maximise sequence matching
458 replaceNonCanonicalResidue(monomer.getGroup3(), seq, pos);
461 * if Jmol has derived a secondary structure prediction for this position,
462 * convert it to Jalview equivalent and save it
464 setSecondaryStructure(monomer.getProteinStructureSubType(), pos,
471 * Replace any non-gap miscellaneous characters with 'X'
476 protected void convertNonGapCharacters(char[] seq)
478 boolean isNa = Comparison.areNucleotide(new char[][] { seq });
479 int[] cinds = isNa ? ResidueProperties.nucleotideIndex
480 : ResidueProperties.aaIndex;
481 int nonGap = isNa ? ResidueProperties.maxNucleotideIndex
482 : ResidueProperties.maxProteinIndex;
484 for (int p = 0; p < seq.length; p++)
486 if (cinds[seq[p]] == nonGap)
494 * Add a source db ref entry for the given sequence.
501 protected void addSourceDBref(SequenceI sq, String accessionId,
504 DBRefEntry sourceDBRef = new DBRefEntry();
505 sourceDBRef.setAccessionId(accessionId);
506 sourceDBRef.setSource(DBRefSource.MMCIF);
507 sourceDBRef.setStartRes(start);
508 sourceDBRef.setEndRes(end);
509 sq.setSourceDBRef(sourceDBRef);
510 sq.addDBRef(sourceDBRef);
514 * Add a PDBEntry giving the source of PDB data to the sequence
520 protected void addPdbid(SequenceI sq, String id, String chainId)
522 PDBEntry entry = new PDBEntry();
524 entry.setType(PDBEntry.Type.MMCIF);
525 entry.setProperty(new Hashtable());
528 // entry.getProperty().put("CHAIN", chains.elementAt(i).id);
529 entry.setChainCode(String.valueOf(chainId));
533 entry.setFile(inFile.getAbsolutePath());
537 // TODO: decide if we should dump the datasource to disk
538 entry.setFile(getDataName());
546 * Helper method that adds an AlignmentAnnotation for secondary structure to
547 * the sequence, provided at least one secondary structure prediction has been
558 protected void addSecondaryStructureAnnotation(String modelTitle,
559 SequenceI sq, char[] secstr, char[] secstrcode, String chainId,
562 char[] seq = sq.getSequence();
563 boolean ssFound = false;
564 Annotation asecstr[] = new Annotation[seq.length + firstResNum - 1];
565 for (int p = 0; p < seq.length; p++)
567 if (secstr[p] >= 'A' && secstr[p] <= 'z')
569 asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
570 secstrcode[p], Float.NaN);
577 String mt = modelTitle == null ? getDataName() : modelTitle;
579 AlignmentAnnotation ann = new AlignmentAnnotation(
580 "Secondary Structure", "Secondary Structure for " + mt,
582 ann.belowAlignment = true;
584 ann.autoCalculated = false;
585 ann.setCalcId(getClass().getName());
586 ann.adjustForAlignment();
587 ann.validateRangeAndDisplay();
588 annotations.add(ann);
589 sq.addAlignmentAnnotation(ann);
593 private void waitForScript(Viewer jmd)
595 while (jmd.isScriptExecuting())
601 } catch (InterruptedException x)
608 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
609 * secondary structure arrays at the given sequence position
611 * @param proteinStructureSubType
616 protected void setSecondaryStructure(STR proteinStructureSubType,
617 int pos, char[] secstr, char[] secstrcode)
619 switch (proteinStructureSubType)
638 switch (proteinStructureSubType)
644 secstrcode[pos] = 'H';
647 secstrcode[pos] = 'E';
654 private char getSecondayStructure(STR proteinStructureSubType)
656 switch (proteinStructureSubType)
673 * Convert any non-standard peptide codes to their standard code table
674 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
676 * @param threeLetterCode
680 protected void replaceNonCanonicalResidue(String threeLetterCode,
683 String canonical = ResidueProperties
684 .getCanonicalAminoAcid(threeLetterCode);
685 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
687 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
692 * Not implemented - returns null
695 public String print()
704 public void setCallbackFunction(String callbackType,
705 String callbackFunction)
710 public void notifyCallback(CBK cbType, Object[] data)
712 String strInfo = (data == null || data[1] == null ? null : data[1]
717 sendConsoleEcho(strInfo);
720 notifyScriptTermination((String) data[2],
721 ((Integer) data[3]).intValue());
724 String mystatus = (String) data[3];
725 if (mystatus.indexOf("Picked") >= 0
726 || mystatus.indexOf("Sequence") >= 0)
729 sendConsoleMessage(strInfo);
731 else if (mystatus.indexOf("Completed") >= 0)
733 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
734 strInfo.length() - 1));
738 sendConsoleMessage(data == null ? null : strInfo);
741 sendConsoleMessage(strInfo);
748 String lastConsoleEcho = "";
750 private void sendConsoleEcho(String string)
752 lastConsoleEcho += string;
753 lastConsoleEcho += "\n";
756 String lastConsoleMessage = "";
758 private void sendConsoleMessage(String string)
760 lastConsoleMessage += string;
761 lastConsoleMessage += "\n";
764 int lastScriptTermination = -1;
766 String lastScriptMessage = "";
768 private void notifyScriptTermination(String string, int intValue)
770 lastScriptMessage += string;
771 lastScriptMessage += "\n";
772 lastScriptTermination = intValue;
776 public boolean notifyEnabled(CBK callbackPick)
778 switch (callbackPick)
792 * Not implemented - returns null
795 public String eval(String strEval)
801 * Not implemented - returns null
804 public float[][] functionXY(String functionName, int x, int y)
810 * Not implemented - returns null
813 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
819 * Not implemented - returns null
822 public String createImage(String fileName, String imageType,
823 Object text_or_bytes, int quality)
829 * Not implemented - returns null
832 public Map<String, Object> getRegistryInfo()
841 public void showUrl(String url)
846 * Not implemented - returns null
849 public Dimension resizeInnerPanel(String data)
855 public Map<String, Object> getJSpecViewProperty(String arg0)
860 public boolean isPredictSecondaryStructure()
862 return predictSecondaryStructure;
865 public void setPredictSecondaryStructure(boolean predictSecondaryStructure)
867 this.predictSecondaryStructure = predictSecondaryStructure;