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.PDBEntry;
26 import jalview.datamodel.Sequence;
27 import jalview.datamodel.SequenceI;
28 import jalview.io.AlignFile;
29 import jalview.io.FileParse;
30 import jalview.schemes.ResidueProperties;
31 import jalview.util.Comparison;
32 import jalview.util.MessageManager;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Hashtable;
37 import java.util.List;
40 import javajs.awt.Dimension;
42 import org.jmol.api.JmolStatusListener;
43 import org.jmol.api.JmolViewer;
44 import org.jmol.c.CBK;
45 import org.jmol.c.STR;
46 import org.jmol.modelset.Group;
47 import org.jmol.modelset.Model;
48 import org.jmol.modelset.ModelSet;
49 import org.jmol.modelsetbio.BioModel;
50 import org.jmol.modelsetbio.BioPolymer;
51 import org.jmol.modelsetbio.Monomer;
52 import org.jmol.viewer.Viewer;
55 * Import and process PDB files with Jmol
60 public class PDBFileWithJmol extends AlignFile implements
65 public PDBFileWithJmol(String inFile, String type) throws IOException
70 public PDBFileWithJmol(FileParse fp) throws IOException
75 public PDBFileWithJmol()
80 * create a headless jmol instance for dataprocessing
84 private Viewer getJmolData()
90 viewer = (Viewer) JmolViewer.allocateViewer(null, null, null, null,
91 null, "-x -o -n", this);
92 // ensure the 'new' (DSSP) not 'old' (Ramachandran) SS method is used
93 viewer.setBooleanProperty("defaultStructureDSSP", true);
94 } catch (ClassCastException x)
96 throw new Error(MessageManager.formatMessage(
97 "error.jmol_version_not_compatible_with_jalview_version",
98 new String[] { JmolViewer.getJmolVersion() }), x);
104 private void waitForScript(Viewer jmd)
106 while (jmd.isScriptExecuting())
112 } catch (InterruptedException x)
119 * Convert Jmol's secondary structure code to Jalview's, and stored it in the
120 * secondary structure arrays at the given sequence position
122 * @param proteinStructureSubType
127 protected void setSecondaryStructure(STR proteinStructureSubType,
128 int pos, char[] secstr, char[] secstrcode)
130 switch (proteinStructureSubType)
149 switch (proteinStructureSubType)
155 secstrcode[pos] = 'H';
158 secstrcode[pos] = 'E';
166 * Convert any non-standard peptide codes to their standard code table
167 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
169 * @param threeLetterCode
173 protected void replaceNonCanonicalResidue(String threeLetterCode,
176 String canonical = ResidueProperties
177 .getCanonicalAminoAcid(threeLetterCode);
178 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
180 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
185 * Not implemented - returns null
188 public String print()
197 public void setCallbackFunction(String callbackType,
198 String callbackFunction)
203 public void notifyCallback(CBK cbType, Object[] data)
205 String strInfo = (data == null || data[1] == null ? null : data[1]
210 sendConsoleEcho(strInfo);
213 notifyScriptTermination((String) data[2],
214 ((Integer) data[3]).intValue());
217 String mystatus = (String) data[3];
218 if (mystatus.indexOf("Picked") >= 0
219 || mystatus.indexOf("Sequence") >= 0)
222 sendConsoleMessage(strInfo);
224 else if (mystatus.indexOf("Completed") >= 0)
226 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
227 strInfo.length() - 1));
231 sendConsoleMessage(data == null ? null : strInfo);
234 sendConsoleMessage(strInfo);
241 String lastConsoleEcho = "";
243 private void sendConsoleEcho(String string)
245 lastConsoleEcho += string;
246 lastConsoleEcho += "\n";
249 String lastConsoleMessage = "";
251 private void sendConsoleMessage(String string)
253 lastConsoleMessage += string;
254 lastConsoleMessage += "\n";
257 int lastScriptTermination = -1;
259 String lastScriptMessage = "";
261 private void notifyScriptTermination(String string, int intValue)
263 lastScriptMessage += string;
264 lastScriptMessage += "\n";
265 lastScriptTermination = intValue;
269 public boolean notifyEnabled(CBK callbackPick)
271 switch (callbackPick)
285 * Not implemented - returns null
288 public String eval(String strEval)
294 * Not implemented - returns null
297 public float[][] functionXY(String functionName, int x, int y)
303 * Not implemented - returns null
306 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
312 * Not implemented - returns null
315 public String createImage(String fileName, String imageType,
316 Object text_or_bytes, int quality)
322 * Not implemented - returns null
325 public Map<String, Object> getRegistryInfo()
334 public void showUrl(String url)
339 * Not implemented - returns null
342 public Dimension resizeInnerPanel(String data)
348 public Map<String, Object> getJSpecViewProperty(String arg0)
354 * Calls the Jmol library to parse the PDB file, and then inspects the
355 * resulting object model to generate Jalview-style sequences, with secondary
356 * structure annotation added where available (i.e. where it has been computed
357 * by Jmol using DSSP).
359 * @see jalview.io.AlignFile#parse()
362 public void parse() throws IOException
364 Viewer jmolModel = getJmolData();
365 jmolModel.openReader(getDataName(), getDataName(), getReader());
366 waitForScript(jmolModel);
369 * Convert one or more Jmol Model objects to Jalview sequences
371 if (jmolModel.ms.mc > 0)
373 parseBiopolymers(jmolModel.ms);
378 * Process the Jmol BioPolymer array and generate a Jalview sequence for each
379 * chain found (including any secondary structure annotation from DSSP)
382 * @throws IOException
384 public void parseBiopolymers(ModelSet ms) throws IOException
387 for (Model model : ms.am)
390 String modelTitle = (String) ms.getInfo(modelIndex, "title");
393 * Chains can span BioPolymers, so first make a flattened list,
394 * and then work out the lengths of chains present
396 List<Monomer> monomers = getMonomers(ms, (BioModel) model);
397 List<Integer> chainLengths = getChainLengths(monomers);
400 * now chop up the Monomer list to make Jalview Sequences
403 for (int length : chainLengths)
405 buildSequenceFromChain(monomers.subList(from, from + length), modelTitle);
412 * Helper method to construct a sequence for one chain and add it to the seqs
416 * a list of all monomers in the chain
419 protected void buildSequenceFromChain(List<Monomer> monomers, String modelTitle)
421 final int length = monomers.size();
424 * arrays to hold sequence and secondary structure
426 char[] seq = new char[length];
427 char[] secstr = new char[length];
428 char[] secstrcode = new char[length];
431 * populate the sequence and secondary structure arrays
433 extractJmolChainData(monomers, seq, secstr, secstrcode);
436 * grab chain code and start position from first residue;
438 String chainId = monomers.get(0).chain.getIDStr();
439 int firstResNum = monomers.get(0).getResno();
442 // Jalview doesn't like residue < 1, so force this to 1
443 System.err.println("Converting chain " + chainId + " first RESNUM ("
444 + firstResNum + ") to 1");
449 * convert any non-gap unknown residues to 'X'
451 convertNonGapCharacters(seq);
454 * construct and add the Jalview sequence
456 String seqName = "" + getDataName() + "|" + modelTitle + "|"
458 SequenceI sq = new Sequence(seqName, seq, firstResNum, firstResNum + length - 1);
462 * add secondary structure predictions (if any)
464 addSecondaryStructureAnnotation(modelTitle, sq, secstr, secstrcode,
465 chainId, firstResNum);
468 * record the PDB id for the sequence
470 addPdbid(sq, chainId);
474 * Scans the list of (Jmol) Monomer objects, and adds the residue for each to
475 * the sequence array, and any converted secondary structure prediction to the
476 * secondary structure arrays
483 protected void extractJmolChainData(List<Monomer> monomers, char[] seq,
484 char[] secstr, char[] secstrcode)
487 for (Monomer monomer : monomers)
489 seq[pos] = monomer.getGroup1();
492 * JAL-1828 replace a modified amino acid with its standard
493 * equivalent (e.g. MSE with MET->M) to maximise sequence matching
495 replaceNonCanonicalResidue(monomer.getGroup3(), seq, pos);
498 * if Jmol has derived a secondary structure prediction for
499 * this position, convert it to Jalview equivalent and save it
501 setSecondaryStructure(monomer.getProteinStructureSubType(), pos,
508 * Helper method that adds an AlignmentAnnotation for secondary structure to
509 * the sequence, provided at least one secondary structure prediction has been
520 protected void addSecondaryStructureAnnotation(String modelTitle,
521 SequenceI sq, char[] secstr, char[] secstrcode,
522 String chainId, int firstResNum)
524 char[] seq = sq.getSequence();
525 boolean ssFound = false;
526 Annotation asecstr[] = new Annotation[seq.length + firstResNum - 1];
527 for (int p = 0; p < seq.length; p++)
529 if (secstr[p] >= 'A' && secstr[p] <= 'z')
531 asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
532 secstrcode[p], Float.NaN);
539 String mt = modelTitle == null ? getDataName() : modelTitle;
541 AlignmentAnnotation ann = new AlignmentAnnotation(
542 "Secondary Structure", "Secondary Structure for " + mt,
544 ann.belowAlignment = true;
546 ann.autoCalculated = false;
547 ann.setCalcId(getClass().getName());
548 ann.adjustForAlignment();
549 ann.validateRangeAndDisplay();
550 annotations.add(ann);
551 sq.addAlignmentAnnotation(ann);
556 * Replace any non-gap miscellaneous characters with 'X'
561 protected void convertNonGapCharacters(char[] seq)
563 boolean isNa = Comparison.areNucleotide(new char[][] { seq });
564 int[] cinds = isNa ? ResidueProperties.nucleotideIndex
565 : ResidueProperties.aaIndex;
566 int nonGap = isNa ? ResidueProperties.maxNucleotideIndex
567 : ResidueProperties.maxProteinIndex;
569 for (int p = 0; p < seq.length; p++)
571 if (cinds[seq[p]] == nonGap)
579 * Add a PDBEntry giving the source of PDB data to the sequence
584 protected void addPdbid(SequenceI sq, String chainId)
586 PDBEntry pdbe = new PDBEntry();
587 pdbe.setFile(getDataName());
588 pdbe.setId(getDataName());
589 pdbe.setProperty(new Hashtable());
590 pdbe.setChainCode(chainId);
595 * Scans the list of Monomers (residue models), inspecting the chain id for
596 * each, and returns an array whose length is the number of chains, and values
597 * the length of each chain
602 protected List<Integer> getChainLengths(List<Monomer> monomers)
604 List<Integer> chainLengths = new ArrayList<Integer>();
605 int lastChainId = -1;
608 for (Monomer monomer : monomers)
610 int chainId = monomer.chain.chainID;
611 if (chainId != lastChainId && length > 0)
614 * change of chain - record the length of the last one
616 chainLengths.add(length);
619 lastChainId = chainId;
625 * record the length of the final chain
627 chainLengths.add(length);
634 * Returns a flattened list of Monomer (residues) in order, across all
635 * BioPolymers in the model. This simplifies assembling chains which span
636 * BioPolymers. The result omits any alternate residues reported for the same
637 * sequence position (RESNUM value).
643 protected List<Monomer> getMonomers(ModelSet ms, BioModel model)
645 List<Monomer> result = new ArrayList<Monomer>();
646 int lastResNo = Integer.MIN_VALUE;
648 for (BioPolymer bp : model.bioPolymers)
650 for (int groupLeadAtoms : bp.getLeadAtomIndices())
652 Group group = ms.at[groupLeadAtoms].group;
653 if (group instanceof Monomer)
656 * ignore alternate residue at same position
657 * example: 1ejg has residues A:LEU, B:ILE at RESNUM=25
659 int resNo = group.getResno();
660 if (lastResNo != resNo)
662 result.add((Monomer) group);