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)
133 if (secstr[pos] == 0)
138 if (secstr[pos] == 0)
143 if (secstr[pos] == 0)
148 if (secstr[pos] == 0)
152 secstrcode[pos] = 'H';
156 secstrcode[pos] = 'E';
165 * Convert any non-standard peptide codes to their standard code table
166 * equivalent. (Initial version only does Selenomethionine MSE->MET.)
168 * @param threeLetterCode
172 protected void replaceNonCanonicalResidue(String threeLetterCode,
175 String canonical = ResidueProperties
176 .getCanonicalAminoAcid(threeLetterCode);
177 if (canonical != null && !canonical.equalsIgnoreCase(threeLetterCode))
179 seq[pos] = ResidueProperties.getSingleCharacterCode(canonical);
184 * Not implemented - returns null
187 public String print()
196 public void setCallbackFunction(String callbackType,
197 String callbackFunction)
202 public void notifyCallback(CBK cbType, Object[] data)
204 String strInfo = (data == null || data[1] == null ? null : data[1]
209 sendConsoleEcho(strInfo);
212 notifyScriptTermination((String) data[2],
213 ((Integer) data[3]).intValue());
216 String mystatus = (String) data[3];
217 if (mystatus.indexOf("Picked") >= 0
218 || mystatus.indexOf("Sequence") >= 0)
221 sendConsoleMessage(strInfo);
223 else if (mystatus.indexOf("Completed") >= 0)
225 sendConsoleEcho(strInfo.substring(strInfo.lastIndexOf(",") + 2,
226 strInfo.length() - 1));
230 sendConsoleMessage(data == null ? null : strInfo);
233 sendConsoleMessage(strInfo);
240 String lastConsoleEcho = "";
242 private void sendConsoleEcho(String string)
244 lastConsoleEcho += string;
245 lastConsoleEcho += "\n";
248 String lastConsoleMessage = "";
250 private void sendConsoleMessage(String string)
252 lastConsoleMessage += string;
253 lastConsoleMessage += "\n";
256 int lastScriptTermination = -1;
258 String lastScriptMessage = "";
260 private void notifyScriptTermination(String string, int intValue)
262 lastScriptMessage += string;
263 lastScriptMessage += "\n";
264 lastScriptTermination = intValue;
268 public boolean notifyEnabled(CBK callbackPick)
270 switch (callbackPick)
284 * Not implemented - returns null
287 public String eval(String strEval)
293 * Not implemented - returns null
296 public float[][] functionXY(String functionName, int x, int y)
302 * Not implemented - returns null
305 public float[][][] functionXYZ(String functionName, int nx, int ny, int nz)
311 * Not implemented - returns null
314 public String createImage(String fileName, String imageType,
315 Object text_or_bytes, int quality)
321 * Not implemented - returns null
324 public Map<String, Object> getRegistryInfo()
333 public void showUrl(String url)
338 * Not implemented - returns null
341 public Dimension resizeInnerPanel(String data)
347 public Map<String, Object> getJSpecViewProperty(String arg0)
353 * Calls the Jmol library to parse the PDB file, and then inspects the
354 * resulting object model to generate Jalview-style sequences, with secondary
355 * structure annotation added where available (i.e. where it has been computed
356 * by Jmol using DSSP).
358 * @see jalview.io.AlignFile#parse()
361 public void parse() throws IOException
363 Viewer jmolModel = getJmolData();
364 jmolModel.openReader(getDataName(), getDataName(), getReader());
365 waitForScript(jmolModel);
368 * Convert one or more Jmol Model objects to Jalview objects.
370 if (jmolModel.ms.mc > 0)
372 parseBiopolymers(jmolModel.ms);
377 * Process the Jmol BioPolymer array and generate a Jalview sequence for each
378 * chain found (including any secondary structure annotation from DSSP)
381 * @throws IOException
383 public void parseBiopolymers(ModelSet ms) throws IOException
386 for (Model model : ms.am)
389 String modelTitle = (String) ms.getInfo(modelIndex, "title");
392 * as chains can span BioPolymers, we first make a flattened list,
393 * and then work out the lengths of chains present
395 List<Monomer> monomers = getMonomers(ms, (BioModel) model);
396 List<Integer> chainLengths = getChainLengths(monomers);
399 * now chop up the Monomer list to make Jalview Sequences
402 for (int length : chainLengths)
404 buildSequenceFromChain(monomers.subList(from, from + length), modelTitle);
411 * Helper method to construct a sequence for one chain and add it to the seqs
415 * a list of all monomers in the chain
418 protected void buildSequenceFromChain(List<Monomer> monomers, String modelTitle)
420 final int length = monomers.size();
423 * arrays to hold sequence and secondary structure
425 char[] seq = new char[length];
426 char[] secstr = new char[length];
427 char[] secstrcode = new char[length];
430 * populate the sequence and secondary structure arrays
432 extractJmolChainData(monomers, seq, secstr, secstrcode);
435 * grab chain code and start position from first residue;
437 String chainId = monomers.get(0).chain.getIDStr();
438 int firstResNum = monomers.get(0).getResno();
441 // Jalview doesn't like residue < 1, so force this to 1
442 System.err.println("Converting chain " + chainId + " first RESNUM ("
443 + firstResNum + ") to 1");
448 * convert any non-gap unknown residues to 'X'
450 convertNonGapCharacters(seq);
453 * construct and add the Jalview sequence
455 SequenceI sq = new Sequence("" + getDataName() + "|" + modelTitle + "|"
456 + chainId, seq, firstResNum, firstResNum + length - 1);
460 * add secondary structure predictions (if any)
462 addSecondaryStructureAnnotation(modelTitle, sq, secstr, secstrcode,
463 chainId, firstResNum);
466 * record the PDB id for the sequence
468 addPdbid(sq, chainId);
472 * Scans the list of (Jmol) Monomer objects, and adds the residue for each to
473 * the sequence array, and any converted secondary structure prediction to the
474 * secondary structure arrays
481 protected void extractJmolChainData(List<Monomer> monomers, char[] seq,
482 char[] secstr, char[] secstrcode)
485 for (Monomer monomer : monomers)
487 seq[pos] = monomer.getGroup1();
490 * JAL-1828 replace a modified amino acid with its standard
491 * equivalent (e.g. MSE with MET->M) to maximise sequence matching
493 replaceNonCanonicalResidue(monomer.getGroup3(), seq, pos);
496 * if Jmol has derived a secondary structure prediction for
497 * this position, convert it to Jalview equivalent and save it
499 setSecondaryStructure(monomer.getProteinStructureSubType(), pos,
506 * Helper method that adds an AlignmentAnnotation for secondary structure to
507 * the sequence, provided at least one secondary structure prediction has been
518 protected void addSecondaryStructureAnnotation(String modelTitle,
519 SequenceI sq, char[] secstr, char[] secstrcode,
520 String chainId, int firstResNum)
522 char[] seq = sq.getSequence();
523 boolean ssFound = false;
524 Annotation asecstr[] = new Annotation[seq.length + firstResNum - 1];
525 for (int p = 0; p < seq.length; p++)
527 if (secstr[p] >= 'A' && secstr[p] <= 'z')
529 asecstr[p] = new Annotation(String.valueOf(secstr[p]), null,
530 secstrcode[p], Float.NaN);
537 String mt = modelTitle == null ? getDataName() : modelTitle;
539 AlignmentAnnotation ann = new AlignmentAnnotation(
540 "Secondary Structure", "Secondary Structure for " + mt,
542 ann.belowAlignment = true;
544 ann.autoCalculated = false;
545 ann.setCalcId(getClass().getName());
546 ann.adjustForAlignment();
547 ann.validateRangeAndDisplay();
548 annotations.add(ann);
549 sq.addAlignmentAnnotation(ann);
554 * Replace any non-gap miscellaneous characters with 'X'
559 protected void convertNonGapCharacters(char[] seq)
561 boolean isNa = Comparison.areNucleotide(new char[][] { seq });
562 int[] cinds = isNa ? ResidueProperties.nucleotideIndex
563 : ResidueProperties.aaIndex;
564 int nonGap = isNa ? ResidueProperties.maxNucleotideIndex
565 : ResidueProperties.maxProteinIndex;
567 for (int p = 0; p < seq.length; p++)
569 if (cinds[seq[p]] == nonGap)
580 protected void addPdbid(SequenceI sq, String chainId)
582 PDBEntry pdbe = new PDBEntry();
583 pdbe.setFile(getDataName());
584 pdbe.setId(getDataName());
585 pdbe.setProperty(new Hashtable());
586 // pdbe.getProperty().put("CHAIN", "" + _lastChainId);
587 pdbe.setChainCode(chainId);
592 * Scans the list of Monomers (residue models), inspecting the chain id for
593 * each, and returns an array whose length is the number of chains, and values
594 * the length of each chain
599 protected List<Integer> getChainLengths(List<Monomer> monomers)
601 List<Integer> chainLengths = new ArrayList<Integer>();
602 int lastChainId = -1;
605 for (Monomer monomer : monomers)
607 int chainId = monomer.chain.chainID;
608 if (chainId != lastChainId && length > 0)
611 * change of chain - record the length of the last one
613 chainLengths.add(length);
616 lastChainId = chainId;
622 * record the length of the final chain
624 chainLengths.add(length);
631 * Returns a flattened list of Monomer (residue) in order, across all
632 * BioPolymers in the model. This simplifies assembling chains which span
633 * BioPolymers. The result does not include any alternate residues reported
634 * for the same sequence position (RESNUM value).
640 protected List<Monomer> getMonomers(ModelSet ms, BioModel model)
642 List<Monomer> result = new ArrayList<Monomer>();
643 String lastSeqCode = "";
645 for (BioPolymer bp : model.bioPolymers) {
646 for (int groupLeadAtoms : bp.getLeadAtomIndices())
648 Group group = ms.at[groupLeadAtoms].group;
649 if (group instanceof Monomer)
652 * ignore alternate residue at same position
653 * example: 1ejg has residues A:LEU, B:ILE, C:ILE at RESNUM=25
655 String seqcodeString = group.getSeqcodeString();
656 if (!lastSeqCode.equals(seqcodeString))
658 result.add((Monomer) group);
662 System.out.println("skipping");
664 lastSeqCode = seqcodeString;