From: Jim Procter Date: Tue, 2 Aug 2016 10:05:53 +0000 (+0100) Subject: Merge branch 'develop' into efficiency/JAL-2034_JAL-1421 X-Git-Tag: Release_2_10_0~120^2~2 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=0f40a8334651302a74a223ecd3e583451302bb42;hp=ebca5f7880f838a5564dd419274ad6025b456dab;p=jalview.git Merge branch 'develop' into efficiency/JAL-2034_JAL-1421 --- diff --git a/help/html/features/sifts_mapping_output.png b/help/html/features/sifts_mapping_output.png index 3c28b81..ddc8a1f 100644 Binary files a/help/html/features/sifts_mapping_output.png and b/help/html/features/sifts_mapping_output.png differ diff --git a/help/html/features/siftsmapping.html b/help/html/features/siftsmapping.html index c344a20..08089ad 100644 --- a/help/html/features/siftsmapping.html +++ b/help/html/features/siftsmapping.html @@ -9,8 +9,8 @@

SIFTS Mapping

- SIFTS (Structure integration with function, taxonomy - and sequences) provides an up-to-date resource for residue-level + SIFTS (Structure Integration with Function, Taxonomy + and Sequences) provides an up-to-date resource for residue-level mapping between Uniprot and PDB entries. The information is updated and released weekly simultaneously with the release of new PDB entries. SIFTS Entries are published as XML files and made publicly available via an FTP @@ -18,7 +18,8 @@

- At the point of viewing a PDB structure, Jalview downloads a SIFTS file + At the point of viewing a PDB structure, if the default mapping method is set as 'SIFTS', + Jalview will download a SIFTS file for the target entry and uses it to accurately map the sequence residues with the structure residue. Prior to SIFTS integration, Jalview uses Needleman and Wunsch Alignment algorithm to map sequence residues to structure residues, and that may not @@ -26,18 +27,38 @@

- The default method for 'Sequence ↔ Structure' mapping can be configured - in the Structure tab in the Tools → Preferences dialog box. When 'SIFTS' - is enabled as the default, all mappings between 'Sequence ↔ Structure' is - performed via SIFTS provided that there is a valid SIFTS entry for PDB structure. If no - valid SIFTS resource is available, then the 'Sequence ↔ Structure' mapping falls - back to Needleman and Wunsch Alignment algorithm. + Configuration
+ The default mapping method can be configured via Tools → Preferences → + Structure tab Then scroll to the 'Sequence ↔ Structure method' section of + the dialog box and change the default method. When 'SIFTS' is enabled as the default, all + mappings between 'Sequence ↔ Structure' is performed via SIFTS provided that there + is a valid SIFTS resource for the PDB entry. If no valid SIFTS resource is available, then + the 'Sequence ↔ Structure' mapping falls back to Needleman and Wunsch Alignment algorithm.

-

To verify the mapping method used, you can view the mapping output via the structure viewer menu File → View mapping. A sample mapping output can be seen in the screenshot below. The highlighted position shows the method used.

+

Multi-Chain Mappings +
One of the main merits of SIFTS is the ability to accurately achieve multi-chain mapping + (one-to-many) between a single Uniprot sequence and its corresponding multiple chains in + PDB. Consequently, mousing over the uniprot sequence in the alignment window results + to highlighting multiple corresponding positions in the structure viewer for the mapped chains. +

- SIFTS mapping output + To see this in action, load uniprot sequence for FER1_MAIZE then veiw PDB structure for 3B2F, you + will notice that mousing over the sequence results to two positions being highlighted in the + structure, also colouring the sequence transfers the color to all the mapped chains in the structure.

+ +

+ Viewing Mapping Output
+ The mapping output is accessible via File → View mapping menu of the structure + viewers. The screenshot below is the mapping output for the {FER1_MAIZE ↔ 3B2F} + example described above. Observe that all the two chains were mapped. The mapping method used can be + seen within the area highlighted with red boarder. This is useful for visually ascertaining the + mapping method when in doubt. +

+ +  SIFTS mapping output +

SIFTS Mapping integration was added in Jalview 2.9.1

diff --git a/src/MCview/Atom.java b/src/MCview/Atom.java index 68a7c21..ab038a0 100755 --- a/src/MCview/Atom.java +++ b/src/MCview/Atom.java @@ -109,6 +109,23 @@ public class Atom } } + @Override + public boolean equals(Object that) + { + if (this == that || that == null) + { + return true; + } + if (that instanceof Atom) + { + Atom other = (Atom) that; + return other.resName.equals(this.resName) + && other.resNumber == this.resNumber + && other.resNumIns.equals(this.resNumIns) + && other.chain.equals(this.chain); + } + return false; + } public Atom(float x, float y, float z) { this.x = x; diff --git a/src/MCview/PDBfile.java b/src/MCview/PDBfile.java index 9acc2e7..2746807 100755 --- a/src/MCview/PDBfile.java +++ b/src/MCview/PDBfile.java @@ -25,7 +25,6 @@ import jalview.datamodel.DBRefSource; import jalview.datamodel.SequenceI; import jalview.io.FileParse; import jalview.io.StructureFile; -import jalview.structure.StructureImportSettings; import jalview.util.MessageManager; import java.io.IOException; @@ -132,8 +131,7 @@ public class PDBfile extends StructureFile break; } if (line.indexOf("ATOM") == 0 - || (StructureImportSettings.isProcessHETATMs() - && line.indexOf("HETATM") == 0 && !terFlag)) + || (line.indexOf("HETATM") == 0 && !terFlag)) { terFlag = false; diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java index 3d61b11..fb49541 100755 --- a/src/jalview/analysis/AAFrequency.java +++ b/src/jalview/analysis/AAFrequency.java @@ -624,8 +624,11 @@ public class AAFrequency String modalCodon = String.valueOf(CodingUtils .decodeCodon(modalCodonEncoded)); if (sortedCodonCounts.length > 1 - && sortedCodonCounts[codons.length - 2] == modalCodonEncoded) + && sortedCodonCounts[codons.length - 2] == sortedCodonCounts[codons.length - 1]) { + /* + * two or more codons share the modal count + */ modalCodon = "+"; } float pid = sortedCodonCounts[sortedCodonCounts.length - 1] * 100 diff --git a/src/jalview/analysis/Dna.java b/src/jalview/analysis/Dna.java index d1901c3..800cef2 100644 --- a/src/jalview/analysis/Dna.java +++ b/src/jalview/analysis/Dna.java @@ -852,13 +852,18 @@ public class Dna char[] originalSequence = sequence.toCharArray(); int length = originalSequence.length; char[] reversedSequence = new char[length]; - + int bases = 0; for (int i = 0; i < length; i++) { - reversedSequence[length - i - 1] = complement ? getComplement(originalSequence[i]) + char c = complement ? getComplement(originalSequence[i]) : originalSequence[i]; + reversedSequence[length - i - 1] = c; + if (!Comparison.isGap(c)) + { + bases++; + } } - SequenceI reversed = new Sequence(newName, reversedSequence, 1, length); + SequenceI reversed = new Sequence(newName, reversedSequence, 1, bases); return reversed; } @@ -874,6 +879,10 @@ public class Dna { char result = c; switch (c) { + case '-': + case '.': + case ' ': + break; case 'a': result = 't'; break; diff --git a/src/jalview/api/DBRefEntryI.java b/src/jalview/api/DBRefEntryI.java index 2ce7e4a..32245b3 100644 --- a/src/jalview/api/DBRefEntryI.java +++ b/src/jalview/api/DBRefEntryI.java @@ -48,32 +48,6 @@ public interface DBRefEntryI public void setVersion(String version); /** - * - * @param startRes - * index of start residue in the source DB - */ - public void setStartRes(int startRes); - - /** - * - * @return index of start residue in the source DB - */ - public int getStartRes(); - - /** - * - * @param endRes - * index of end residue in the source DB - */ - public void setEndRes(int endRes); - - /** - * - * @return index of end residue in the source DB - */ - public int getEndRes(); - - /** * access a mapping, if present that can be used to map positions from the * associated dataset sequence to the DBRef's sequence frame. * diff --git a/src/jalview/bin/Cache.java b/src/jalview/bin/Cache.java index cf251dc..4be7830 100755 --- a/src/jalview/bin/Cache.java +++ b/src/jalview/bin/Cache.java @@ -20,7 +20,7 @@ */ package jalview.bin; -import jalview.datamodel.DBRefSource; +import jalview.datamodel.PDBEntry; import jalview.structure.StructureImportSettings; import jalview.ws.dbsources.das.api.DasSourceRegistryI; import jalview.ws.dbsources.das.datamodel.DasSourceRegistry; @@ -227,8 +227,15 @@ public class Cache private final static String DEFAULT_CACHE_THRESHOLD_IN_DAYS = "2"; private final static String DEFAULT_FAIL_SAFE_PID_THRESHOLD = "30"; + + /** + * Allowed values are PDB or mmCIF + */ + private final static String DEFAULT_STRUCTURE_FORMAT = PDBEntry.Type.MMCIF + .toString(); - private final static String DEFAULT_STRUCTURE_FORMAT = DBRefSource.PDB; + private final static String DEFAULT_PDB_FILE_PARSER = StructureImportSettings.StructureParser.JMOL_PARSER + .toString(); /** * Initialises the Jalview Application Log @@ -426,12 +433,12 @@ public class Cache System.out .println("Jalview Version: " + codeVersion + codeInstallation); - StructureImportSettings.setCurrentDefaultFormat(jalview.bin.Cache + StructureImportSettings.setDefaultStructureFileFormat(jalview.bin.Cache .getDefault( "DEFAULT_STRUCTURE_FORMAT", DEFAULT_STRUCTURE_FORMAT)); - - StructureImportSettings.setProcessHETATMs(jalview.bin.Cache.getDefault( - "PROCESS_HETATM", false)); + StructureImportSettings + .setDefaultPDBFileParser(jalview.bin.Cache.getDefault( + "DEFAULT_PDB_FILE_PARSER", DEFAULT_PDB_FILE_PARSER)); // jnlpVersion will be null if we're using InstallAnywhere // Dont do this check if running in headless mode if (jnlpVersion == null diff --git a/src/jalview/datamodel/DBRefEntry.java b/src/jalview/datamodel/DBRefEntry.java index efdf0ac..a641b1b 100755 --- a/src/jalview/datamodel/DBRefEntry.java +++ b/src/jalview/datamodel/DBRefEntry.java @@ -25,8 +25,6 @@ import jalview.api.DBRefEntryI; public class DBRefEntry implements DBRefEntryI { String source = "", version = "", accessionId = ""; - - private int startRes, endRes; /** * maps from associated sequence to the database sequence's coordinate system */ @@ -282,28 +280,4 @@ public class DBRefEntry implements DBRefEntryI { return getSrcAccString(); } - - @Override - public int getStartRes() - { - return startRes; - } - - @Override - public void setStartRes(int startRes) - { - this.startRes = startRes; - } - - @Override - public int getEndRes() - { - return endRes; - } - - @Override - public void setEndRes(int endRes) - { - this.endRes = endRes; - } } diff --git a/src/jalview/datamodel/DBRefSource.java b/src/jalview/datamodel/DBRefSource.java index cf15ff8..fba9211 100755 --- a/src/jalview/datamodel/DBRefSource.java +++ b/src/jalview/datamodel/DBRefSource.java @@ -57,11 +57,6 @@ public class DBRefSource public static String PDB = "PDB"; /** - * mmCIF Entry Code - */ - public static String MMCIF = "mmCIF"; - - /** * EMBL ID */ public static String EMBL = "EMBL"; diff --git a/src/jalview/ext/jmol/JmolParser.java b/src/jalview/ext/jmol/JmolParser.java index a791558..ea969ff 100644 --- a/src/jalview/ext/jmol/JmolParser.java +++ b/src/jalview/ext/jmol/JmolParser.java @@ -22,7 +22,6 @@ package jalview.ext.jmol; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; -import jalview.datamodel.DBRefSource; import jalview.datamodel.SequenceI; import jalview.io.FileParse; import jalview.io.StructureFile; @@ -32,6 +31,7 @@ import jalview.util.MessageManager; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; @@ -87,15 +87,6 @@ public class JmolParser extends StructureFile implements JmolStatusListener @Override public void parse() throws IOException { - String dataName = getDataName(); - if (dataName.endsWith(".cif")) - { - setDbRefType(DBRefSource.MMCIF); - } - else - { - setDbRefType(DBRefSource.PDB); - } setChains(new Vector()); Viewer jmolModel = getJmolData(); jmolModel.openReader(getDataName(), getDataName(), getReader()); @@ -186,7 +177,7 @@ public class JmolParser extends StructureFile implements JmolStatusListener prot.add(chainseq); } - if (StructureImportSettings.isPredictSecondaryStructure()) + if (StructureImportSettings.isProcessSecondaryStructure()) { createAnnotation(chainseq, chain, ms.at); } @@ -204,21 +195,22 @@ public class JmolParser extends StructureFile implements JmolStatusListener private List convertSignificantAtoms(ModelSet ms) { List significantAtoms = new ArrayList(); + HashMap chainTerMap = new HashMap(); + org.jmol.modelset.Atom prevAtom = null; for (org.jmol.modelset.Atom atom : ms.at) { - // System.out.println("Seq Id : " + atom.getSeqID()); - // System.out.println("To String : " + atom.toString()); - if (!StructureImportSettings.isProcessHETATMs() && atom.isHetero()) - { - continue; - } if (atom.getAtomName().equalsIgnoreCase("CA") || atom.getAtomName().equalsIgnoreCase("P")) { + if (!atomValidated(atom, prevAtom, chainTerMap)) + { + continue; + } Atom curAtom = new Atom(atom.x, atom.y, atom.z); curAtom.atomIndex = atom.getIndex(); curAtom.chain = atom.getChainIDStr(); - curAtom.insCode = atom.group.getInsertionCode(); + curAtom.insCode = atom.group.getInsertionCode() == '\000' ? ' ' + : atom.group.getInsertionCode(); curAtom.name = atom.getAtomName(); curAtom.number = atom.getAtomNumber(); curAtom.resName = atom.getGroup3(true); @@ -228,12 +220,65 @@ public class JmolParser extends StructureFile implements JmolStatusListener curAtom.resNumIns = "" + curAtom.resNumber + curAtom.insCode; curAtom.tfactor = atom.getBfactor100() / 100f; curAtom.type = 0; - significantAtoms.add(curAtom); + // significantAtoms.add(curAtom); + // ignore atoms from subsequent models + if (!significantAtoms.contains(curAtom)) + { + significantAtoms.add(curAtom); + } + prevAtom = atom; } } return significantAtoms; } + private boolean atomValidated(org.jmol.modelset.Atom curAtom, + org.jmol.modelset.Atom prevAtom, + HashMap chainTerMap) + { + // System.out.println("Atom: " + curAtom.getAtomNumber() + // + " Last atom index " + curAtom.group.lastAtomIndex); + if (chainTerMap == null || prevAtom == null) + { + return true; + } + String curAtomChId = curAtom.getChainIDStr(); + String prevAtomChId = prevAtom.getChainIDStr(); + // new chain encoutered + if (!prevAtomChId.equals(curAtomChId)) + { + // On chain switch add previous chain termination to xTerMap if not exists + if (!chainTerMap.containsKey(prevAtomChId)) + { + chainTerMap.put(prevAtomChId, prevAtom); + } + // if current atom belongs to an already terminated chain and the resNum + // diff < 5 then mark as valid and update termination Atom + if (chainTerMap.containsKey(curAtomChId)) + { + if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5) + { + chainTerMap.put(curAtomChId, curAtom); + return true; + } + return false; + } + } + // atom with previously terminated chain encountered + else if (chainTerMap.containsKey(curAtomChId)) + { + if ((curAtom.getResno() - chainTerMap.get(curAtomChId).getResno()) < 5) + { + chainTerMap.put(curAtomChId, curAtom); + return true; + } + return false; + } + // HETATM with resNum jump > 2 + return !(curAtom.isHetero() && ((curAtom.getResno() - prevAtom + .getResno()) > 2)); + } + private void createAnnotation(SequenceI sequence, PDBChain chain, org.jmol.modelset.Atom[] jmolAtoms) { diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index e11c0ec..b0debf8 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -2840,7 +2840,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void expandViews_actionPerformed(ActionEvent e) { - Desktop.instance.explodeViews(this); + Desktop.explodeViews(this); } /** diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index d54e553..7811e41 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -1806,7 +1806,7 @@ public class Desktop extends jalview.jbgui.GDesktop implements * * @param af */ - public void explodeViews(AlignFrame af) + public static void explodeViews(AlignFrame af) { int size = af.alignPanels.size(); if (size < 2) diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index ac85aad..68245b6 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -108,6 +108,7 @@ import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -165,7 +166,9 @@ public class Jalview2XML */ Map seqRefIds = null; - Vector frefedSequence = null; + Map incompleteSeqs = null; + + List frefedSequence = null; boolean raiseGUI = true; // whether errors are raised in dialog boxes or not @@ -220,6 +223,10 @@ public class Jalview2XML { seqsToIds.clear(); } + if (incompleteSeqs != null) + { + incompleteSeqs.clear(); + } // seqRefIds = null; // seqsToIds = null; } @@ -242,6 +249,14 @@ public class Jalview2XML { seqRefIds = new HashMap(); } + if (incompleteSeqs == null) + { + incompleteSeqs = new HashMap(); + } + if (frefedSequence == null) + { + frefedSequence = new ArrayList(); + } } public Jalview2XML() @@ -253,78 +268,169 @@ public class Jalview2XML this.raiseGUI = raiseGUI; } - public void resolveFrefedSequences() + /** + * base class for resolving forward references to sequences by their ID + * + * @author jprocter + * + */ + abstract class SeqFref { - if (frefedSequence.size() > 0) + String sref; + + String type; + + public SeqFref(String _sref, String type) + { + sref = _sref; + this.type = type; + } + + public String getSref() + { + return sref; + } + + public SequenceI getSrefSeq() + { + return seqRefIds.get(sref); + } + + public boolean isResolvable() + { + return seqRefIds.get(sref) != null; + } + + public SequenceI getSrefDatasetSeq() { - int r = 0, rSize = frefedSequence.size(); - while (r < rSize) + SequenceI sq = seqRefIds.get(sref); + if (sq != null) { - Object[] ref = frefedSequence.elementAt(r); - if (ref != null) + while (sq.getDatasetSequence() != null) { - String sref = (String) ref[0]; - if (seqRefIds.containsKey(sref)) - { - if (ref[1] instanceof jalview.datamodel.Mapping) - { - SequenceI seq = seqRefIds.get(sref); - while (seq.getDatasetSequence() != null) - { - seq = seq.getDatasetSequence(); - } - ((jalview.datamodel.Mapping) ref[1]).setTo(seq); - } - else - { - if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame) - { - SequenceI seq = seqRefIds.get(sref); - while (seq.getDatasetSequence() != null) - { - seq = seq.getDatasetSequence(); - } - if (ref[2] != null - && ref[2] instanceof jalview.datamodel.Mapping) - { - jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2]; - ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap( - seq, mp.getTo(), mp.getMap()); - } - else - { - System.err - .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving " - + ref[2].getClass() + " type objects."); - } - } - else - { - System.err - .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for " - + ref[1].getClass() + " type objects."); - } - } - frefedSequence.remove(r); - rSize--; - } - else + sq = sq.getDatasetSequence(); + } + } + return sq; + } + /** + * @return true if the forward reference was fully resolved + */ + abstract boolean resolve(); + + @Override + public String toString() + { + return type + " reference to " + sref; + } + } + + /** + * create forward reference for a mapping + * + * @param sref + * @param _jmap + * @return + */ + public SeqFref newMappingRef(final String sref, + final jalview.datamodel.Mapping _jmap) + { + SeqFref fref = new SeqFref(sref, "Mapping") + { + public jalview.datamodel.Mapping jmap = _jmap; + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + jmap.setTo(seq); + return true; + } + }; + return fref; + } + + public SeqFref newAlcodMapRef(final String sref, + final AlignedCodonFrame _cf, final jalview.datamodel.Mapping _jmap) + { + + SeqFref fref = new SeqFref(sref, "Codon Frame") + { + AlignedCodonFrame cf = _cf; + + public jalview.datamodel.Mapping mp = _jmap; + + @Override + boolean resolve() + { + SequenceI seq = getSrefDatasetSeq(); + if (seq == null) + { + return false; + } + cf.addMap(seq, mp.getTo(), mp.getMap()); + return true; + } + }; + return fref; + } + + public void resolveFrefedSequences() + { + Iterator nextFref=frefedSequence.iterator(); + int toresolve=frefedSequence.size(); + int unresolved=0,failedtoresolve=0; + while (nextFref.hasNext()) { + SeqFref ref = nextFref.next(); + if (ref.isResolvable()) + { + try { + if (ref.resolve()) { - System.err - .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string " - + ref[0] - + " with objecttype " - + ref[1].getClass()); - r++; + nextFref.remove(); + } else { + failedtoresolve++; } - } - else + } catch (Exception x) { + System.err.println("IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "+ref.getSref()); + x.printStackTrace(); + failedtoresolve++; + } + } else { + unresolved++; + } + } + if (unresolved>0) + { + System.err.println("Jalview Project Import: There were " + unresolved + + " forward references left unresolved on the stack."); + } + if (failedtoresolve>0) + { + System.err.println("SERIOUS! " + failedtoresolve + + " resolvable forward references failed to resolve."); + } + if (incompleteSeqs != null && incompleteSeqs.size() > 0) + { + System.err.println("Jalview Project Import: There are " + + incompleteSeqs.size() + + " sequences which may have incomplete metadata."); + if (incompleteSeqs.size() < 10) + { + for (SequenceI s : incompleteSeqs.values()) { - // empty reference - frefedSequence.remove(r); - rSize--; + System.err.println(s.toString()); } } + else + { + System.err + .println("Too many to report. Skipping output of incomplete sequences."); + } } } @@ -396,7 +502,20 @@ public class Jalview2XML { return; } + saveAllFrames(Arrays.asList(frames), jout); + } + /** + * core method for storing state for a set of AlignFrames. + * + * @param frames + * - frames involving all data to be exported (including containing + * splitframes) + * @param jout + * - project output stream + */ + private void saveAllFrames(List frames, JarOutputStream jout) + { Hashtable dsses = new Hashtable(); /* @@ -416,9 +535,9 @@ public class Jalview2XML List viewIds = new ArrayList(); // REVERSE ORDER - for (int i = frames.length - 1; i > -1; i--) + for (int i = frames.size() - 1; i > -1; i--) { - AlignFrame af = frames[i]; + AlignFrame af = frames.get(i); // skip ? if (skipList != null && skipList @@ -521,30 +640,20 @@ public class Jalview2XML { try { - int ap = 0; - int apSize = af.alignPanels.size(); FileOutputStream fos = new FileOutputStream(jarFile); JarOutputStream jout = new JarOutputStream(fos); - Hashtable dsses = new Hashtable(); - List viewIds = new ArrayList(); + List frames = new ArrayList(); - for (AlignmentPanel apanel : af.alignPanels) + // resolve splitframes + if (af.getViewport().getCodingComplement() != null) { - String jfileName = apSize == 1 ? fileName : fileName + ap; - ap++; - if (!jfileName.endsWith(".xml")) - { - jfileName = jfileName + ".xml"; - } - saveState(apanel, jfileName, jout, viewIds); - String dssid = getDatasetIdRef(af.getViewport().getAlignment() - .getDataset()); - if (!dsses.containsKey(dssid)) - { - dsses.put(dssid, af); - } + frames = ((SplitFrame) af.getSplitViewContainer()).getAlignFrames(); + } + else + { + frames.add(af); } - writeDatasetFor(dsses, fileName, jout); + saveAllFrames(frames, jout); try { jout.flush(); @@ -892,16 +1001,17 @@ public class Jalview2XML jal = av.getAlignment(); } // SAVE MAPPINGS - if (jal.getCodonFrames() != null) + // FOR DATASET + if (storeDS && jal.getCodonFrames() != null) { List jac = jal.getCodonFrames(); for (AlignedCodonFrame acf : jac) { AlcodonFrame alc = new AlcodonFrame(); - vamsasSet.addAlcodonFrame(alc); if (acf.getProtMappings() != null && acf.getProtMappings().length > 0) { + boolean hasMap = false; SequenceI[] dnas = acf.getdnaSeqs(); jalview.datamodel.Mapping[] pmaps = acf.getProtMappings(); for (int m = 0; m < pmaps.length; m++) @@ -911,6 +1021,11 @@ public class Jalview2XML alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null, false)); alc.addAlcodMap(alcmap); + hasMap = true; + } + if (hasMap) + { + vamsasSet.addAlcodonFrame(alc); } } // TODO: delete this ? dead code from 2.8.3->2.9 ? @@ -1935,16 +2050,17 @@ public class Jalview2XML if (jds.getDatasetSequence() != null) { vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence())); - if (jds.getDatasetSequence().getDBRefs() != null) - { - dbrefs = jds.getDatasetSequence().getDBRefs(); - } } else { - vamsasSeq.setDsseqid(id); // so we can tell which sequences really are + // seqId==dsseqid so we can tell which sequences really are // dataset sequences only + vamsasSeq.setDsseqid(id); dbrefs = jds.getDBRefs(); + if (parentseq == null) + { + parentseq = jds; + } } if (dbrefs != null) { @@ -1996,38 +2112,32 @@ public class Jalview2XML if (jmp.getTo() != null) { MappingChoice mpc = new MappingChoice(); - if (recurse - && (parentseq != jmp.getTo() || parentseq - .getDatasetSequence() != jmp.getTo())) + + // check/create ID for the sequence referenced by getTo() + + String jmpid = ""; + SequenceI ps = null; + if (parentseq != jmp.getTo() + && parentseq.getDatasetSequence() != jmp.getTo()) { - mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()), - jmp.getTo(), jds)); + // chaining dbref rather than a handshaking one + jmpid = seqHash(ps = jmp.getTo()); } else { - String jmpid = ""; - SequenceI ps = null; - if (parentseq != jmp.getTo() - && parentseq.getDatasetSequence() != jmp.getTo()) - { - // chaining dbref rather than a handshaking one - jmpid = seqHash(ps = jmp.getTo()); - } - else - { - jmpid = seqHash(ps = parentseq); - } - mpc.setDseqFor(jmpid); - if (!seqRefIds.containsKey(mpc.getDseqFor())) - { - jalview.bin.Cache.log.debug("creatign new DseqFor ID"); - seqRefIds.put(mpc.getDseqFor(), ps); - } - else - { - jalview.bin.Cache.log.debug("reusing DseqFor ID"); - } + jmpid = seqHash(ps = parentseq); + } + mpc.setDseqFor(jmpid); + if (!seqRefIds.containsKey(mpc.getDseqFor())) + { + jalview.bin.Cache.log.debug("creatign new DseqFor ID"); + seqRefIds.put(mpc.getDseqFor(), ps); + } + else + { + jalview.bin.Cache.log.debug("reusing DseqFor ID"); } + mp.setMappingChoice(mpc); } } @@ -2236,14 +2346,10 @@ public class Jalview2XML } if (seqRefIds == null) { - seqRefIds = new HashMap(); - } - if (frefedSequence == null) - { - frefedSequence = new Vector(); + initSeqRefs(); } - AlignFrame af = null, _af = null; + IdentityHashMap importedDatasets = new IdentityHashMap(); Map gatherToThisFrame = new HashMap(); final String file = jprovider.getFilename(); try @@ -2271,13 +2377,24 @@ public class Jalview2XML if (true) // !skipViewport(object)) { _af = loadFromObject(object, file, true, jprovider); - if (object.getJalviewModelSequence().getViewportCount() > 0) + if (_af != null + && object.getJalviewModelSequence().getViewportCount() > 0) { - af = _af; - if (af.viewport.isGatherViewsHere()) + if (af == null) + { + // store a reference to the first view + af = _af; + } + if (_af.viewport.isGatherViewsHere()) { - gatherToThisFrame.put(af.viewport.getSequenceSetId(), af); + // if this is a gathered view, keep its reference since + // after gathering views, only this frame will remain + af = _af; + gatherToThisFrame.put(_af.viewport.getSequenceSetId(), _af); } + // Save dataset to register mappings once all resolved + importedDatasets.put(af.viewport.getAlignment().getDataset(), + af.viewport.getAlignment().getDataset()); } } entryCount++; @@ -2333,11 +2450,6 @@ public class Jalview2XML e.printStackTrace(); } - if (Desktop.instance != null) - { - Desktop.instance.stopLoading(); - } - /* * Regather multiple views (with the same sequence set id) to the frame (if * any) that is flagged as the one to gather to, i.e. convert them to tabbed @@ -2351,11 +2463,24 @@ public class Jalview2XML } restoreSplitFrames(); - + for (AlignmentI ds : importedDatasets.keySet()) + { + if (ds.getCodonFrames() != null) + { + StructureSelectionManager.getStructureSelectionManager( + Desktop.instance).registerMappings(ds.getCodonFrames()); + } + } if (errorMessage != null) { reportErrors(); } + + if (Desktop.instance != null) + { + Desktop.instance.stopLoading(); + } + return af; } @@ -2661,7 +2786,7 @@ public class Jalview2XML // LOAD SEQUENCES List hiddenSeqs = null; - jalview.datamodel.Sequence jseq; + List tmpseqs = new ArrayList(); @@ -2673,21 +2798,36 @@ public class Jalview2XML { String seqId = jseqs[i].getId(); - if (seqRefIds.get(seqId) != null) + SequenceI tmpSeq = seqRefIds.get(seqId); + if (tmpSeq != null) { - tmpseqs.add(seqRefIds.get(seqId)); + if (!incompleteSeqs.containsKey(seqId)) + { + // may not need this check, but keep it for at least 2.9,1 release + if (tmpSeq.getStart()!=jseqs[i].getStart() || tmpSeq.getEnd()!=jseqs[i].getEnd()) + { + System.err + .println("Warning JAL-2154 regression: updating start/end for sequence " + + tmpSeq.toString()); + } + } else { + incompleteSeqs.remove(seqId); + } + tmpSeq.setStart(jseqs[i].getStart()); + tmpSeq.setEnd(jseqs[i].getEnd()); + tmpseqs.add(tmpSeq); multipleView = true; } else { - jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(), + tmpSeq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(), vamsasSeq[vi].getSequence()); - jseq.setDescription(vamsasSeq[vi].getDescription()); - jseq.setStart(jseqs[i].getStart()); - jseq.setEnd(jseqs[i].getEnd()); - jseq.setVamsasId(uniqueSetSuffix + seqId); - seqRefIds.put(vamsasSeq[vi].getId(), jseq); - tmpseqs.add(jseq); + tmpSeq.setDescription(vamsasSeq[vi].getDescription()); + tmpSeq.setStart(jseqs[i].getStart()); + tmpSeq.setEnd(jseqs[i].getEnd()); + tmpSeq.setVamsasId(uniqueSetSuffix + seqId); + seqRefIds.put(vamsasSeq[vi].getId(), tmpSeq); + tmpseqs.add(tmpSeq); vi++; } @@ -2703,7 +2843,7 @@ public class Jalview2XML hiddenSeqs = new ArrayList(); } - hiddenSeqs.add(seqRefIds.get(seqId)); + hiddenSeqs.add(tmpSeq); } } @@ -2713,7 +2853,39 @@ public class Jalview2XML SequenceI[] orderedSeqs = tmpseqs .toArray(new SequenceI[tmpseqs.size()]); - AlignmentI al = new Alignment(orderedSeqs); + AlignmentI al = null; + // so we must create or recover the dataset alignment before going further + // /////////////////////////////// + if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "") + { + // older jalview projects do not have a dataset - so creat alignment and + // dataset + al = new Alignment(orderedSeqs); + al.setDataset(null); + } + else + { + boolean isdsal = object.getJalviewModelSequence().getViewportCount() == 0; + if (isdsal) + { + // we are importing a dataset record, so + // recover reference to an alignment already materialsed as dataset + al = getDatasetFor(vamsasSet.getDatasetId()); + } + if (al == null) + { + // materialse the alignment + al = new Alignment(orderedSeqs); + } + if (isdsal) + { + addDatasetRef(vamsasSet.getDatasetId(), al); + } + + // finally, verify all data in vamsasSet is actually present in al + // passing on flag indicating if it is actually a stored dataset + recoverDatasetFor(vamsasSet, al, isdsal); + } if (referenceseqForView != null) { @@ -2726,22 +2898,6 @@ public class Jalview2XML al.setProperty(ssp.getKey(), ssp.getValue()); } - // / - // SequenceFeatures are added to the DatasetSequence, - // so we must create or recover the dataset before loading features - // /////////////////////////////// - if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "") - { - // older jalview projects do not have a dataset id. - al.setDataset(null); - } - else - { - // recover dataset - passing on flag indicating if this a 'viewless' - // sequence set (a.k.a. a stored dataset for the project) - recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence() - .getViewportCount() == 0); - } // /////////////////////////////// Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this?? @@ -2851,12 +3007,12 @@ public class Jalview2XML else { // defer to later - frefedSequence.add(new Object[] { maps[m].getDnasq(), cf, - mapping }); + frefedSequence.add(newAlcodMapRef(maps[m].getDnasq(), cf, + mapping)); } } + al.addCodonFrame(cf); } - al.addCodonFrame(cf); } } @@ -4424,7 +4580,7 @@ public class Jalview2XML } } af.setMenusFromViewport(af.viewport); - + af.setTitle(view.getTitle()); // TODO: we don't need to do this if the viewport is aready visible. /* * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it @@ -4995,7 +5151,7 @@ public class Jalview2XML } else { - frefedSequence.add(new Object[] { dsfor, jmap }); + frefedSequence.add(newMappingRef(dsfor, jmap)); } } else @@ -5033,6 +5189,7 @@ public class Jalview2XML djs.setEnd(jmap.getMap().getToHighest()); djs.setVamsasId(uniqueSetSuffix + sqid); jmap.setTo(djs); + incompleteSeqs.put(sqid, djs); seqRefIds.put(sqid, djs); } diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index 3b96be8..c1039ee 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -37,6 +37,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.beans.PropertyVetoException; +import java.util.Arrays; +import java.util.List; import java.util.Map.Entry; import javax.swing.AbstractAction; @@ -710,6 +712,17 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI } /** + * return the AlignFrames held by this container + * + * @return { Top alignFrame (Usually CDS), Bottom AlignFrame (Usually + * Protein)} + */ + public List getAlignFrames() + { + return Arrays.asList(new AlignFrame[] { (AlignFrame) getTopFrame(), + (AlignFrame) getBottomFrame() }); + } + /** * Replace Cmd-F Find action with our version. This is necessary because the * 'default' Finder searches in the first AlignFrame it finds. We need it to * search in the half of the SplitFrame that has the mouse. diff --git a/src/jalview/io/AppletFormatAdapter.java b/src/jalview/io/AppletFormatAdapter.java index fb414f4..55bb03d 100755 --- a/src/jalview/io/AppletFormatAdapter.java +++ b/src/jalview/io/AppletFormatAdapter.java @@ -26,6 +26,7 @@ import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; +import jalview.datamodel.PDBEntry.Type; import jalview.structure.StructureImportSettings; import jalview.util.MessageManager; @@ -280,8 +281,10 @@ public class AppletFormatAdapter { // TODO obtain config value from preference settings. // Set value to 'true' to test PDB processing with Jmol: JAL-1213 - boolean isParseWithJMOL = !StructureImportSettings - .getCurrentDefaultFormat().equalsIgnoreCase("PDB"); + boolean isParseWithJMOL = StructureImportSettings + .getDefaultPDBFileParser().equalsIgnoreCase( + StructureImportSettings.StructureParser.JMOL_PARSER + .toString()); if (isParseWithJMOL) { StructureImportSettings.addSettings(annotFromStructure, @@ -299,13 +302,15 @@ public class AppletFormatAdapter localSecondaryStruct, serviceSecondaryStruct, inFile, type); } + ((StructureFile) alignFile).setDbRefType(format); } - else if (format.equals("mmCIF")) + else if (format.equalsIgnoreCase("mmCIF")) { StructureImportSettings.addSettings(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct); alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct, inFile, type); + ((StructureFile) alignFile).setDbRefType(format); } else if (format.equals("STH")) { @@ -447,13 +452,15 @@ public class AppletFormatAdapter alignFile = new MCview.PDBfile(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct, source); } + ((StructureFile) alignFile).setDbRefType(Type.PDB); } - else if (format.equals("mmCIF")) + else if (format.equalsIgnoreCase("mmCIF")) { StructureImportSettings.addSettings(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct); alignFile = new jalview.ext.jmol.JmolParser(annotFromStructure, localSecondaryStruct, serviceSecondaryStruct, source); + ((StructureFile) alignFile).setDbRefType(Type.MMCIF); } else if (format.equals("STH")) { diff --git a/src/jalview/io/StructureFile.java b/src/jalview/io/StructureFile.java index 97e11eb..fc0e207 100644 --- a/src/jalview/io/StructureFile.java +++ b/src/jalview/io/StructureFile.java @@ -8,6 +8,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceI; import jalview.structure.StructureImportSettings; @@ -25,7 +26,7 @@ public abstract class StructureFile extends AlignFile private String id; - private String dbRefType; + private PDBEntry.Type dbRefType; /** * set to true to add derived sequence annotations (temp factor read from @@ -70,7 +71,7 @@ public abstract class StructureFile extends AlignFile this.visibleChainAnnotation = StructureImportSettings .isVisibleChainAnnotation(); this.predictSecondaryStructure = StructureImportSettings - .isPredictSecondaryStructure(); + .isProcessSecondaryStructure(); this.externalSecondaryStructure = StructureImportSettings .isExternalSecondaryStructure(); @@ -116,8 +117,6 @@ public abstract class StructureFile extends AlignFile DBRefEntry sourceDBRef = new DBRefEntry(); sourceDBRef.setAccessionId(getId()); sourceDBRef.setSource(DBRefSource.PDB); - sourceDBRef.setStartRes(pdbSequence.getStart()); - sourceDBRef.setEndRes(pdbSequence.getEnd()); pdbSequence.setSourceDBRef(sourceDBRef); pdbSequence.addPDBId(entry); pdbSequence.addDBRef(sourceDBRef); @@ -273,7 +272,7 @@ public abstract class StructureFile extends AlignFile StructureImportSettings.setShowSeqFeatures(false); StructureImportSettings.setVisibleChainAnnotation(false); StructureImportSettings - .setPredictSecondaryStructure(predictSecondaryStructure); + .setProcessSecondaryStructure(predictSecondaryStructure); StructureImportSettings .setExternalSecondaryStructure(externalSecondaryStructure); Object jmf = constructor.newInstance(args); @@ -401,13 +400,18 @@ public abstract class StructureFile extends AlignFile this.chains = chains; } - public String getDbRefType() + public Type getDbRefType() { return dbRefType; } public void setDbRefType(String dbRefType) { + this.dbRefType = Type.valueOf(dbRefType); + } + + public void setDbRefType(Type dbRefType) + { this.dbRefType = dbRefType; } diff --git a/src/jalview/structure/StructureImportSettings.java b/src/jalview/structure/StructureImportSettings.java index b23fd53..82b5f69 100644 --- a/src/jalview/structure/StructureImportSettings.java +++ b/src/jalview/structure/StructureImportSettings.java @@ -1,7 +1,15 @@ package jalview.structure; -import jalview.datamodel.DBRefSource; - +import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; + +/** + * bean holding settings for structure IO. TODO: tests for validation of values + * TODO: tests for race conditions (all fields are static, is that correct ?) + * + * @author tcofoegbu + * + */ public class StructureImportSettings { /** @@ -14,7 +22,7 @@ public class StructureImportSettings * Set true to predict secondary structure (using JMol for protein, Annotate3D * for RNA) */ - private static boolean predictSecStr = false; + private static boolean processSecStr = false; /** * Set true (with predictSecondaryStructure=true) to predict secondary @@ -24,15 +32,28 @@ public class StructureImportSettings private static boolean showSeqFeatures = true; - private static boolean processHETATMs = false; + public enum StructureParser + { + JMOL_PARSER, JALVIEW_PARSER + } + - private static String currentDefaultFormat = DBRefSource.PDB; + /** + * Determines the default file format for structure files to be downloaded + * from the PDB sequence fetcher. Possible options include: PDB|mmCIF + */ + private static PDBEntry.Type defaultStructureFileFormat = Type.PDB; + /** + * Determines the parser used for parsing PDB format file. Possible options + * are : JMolParser|JalveiwParser + */ + private static StructureParser defaultPDBFileParser = StructureParser.JMOL_PARSER; public static void addSettings(boolean addAlignmentAnnotations, - boolean predictSecStr, boolean externalSecStr) + boolean processSecStr, boolean externalSecStr) { StructureImportSettings.visibleChainAnnotation = addAlignmentAnnotations; - StructureImportSettings.predictSecStr = predictSecStr; + StructureImportSettings.processSecStr = processSecStr; StructureImportSettings.externalSecondaryStructure = externalSecStr; StructureImportSettings.showSeqFeatures = true; } @@ -48,15 +69,15 @@ public class StructureImportSettings StructureImportSettings.visibleChainAnnotation = visibleChainAnnotation; } - public static boolean isPredictSecondaryStructure() + public static boolean isProcessSecondaryStructure() { - return predictSecStr; + return processSecStr; } - public static void setPredictSecondaryStructure( - boolean predictSecondaryStructure) + public static void setProcessSecondaryStructure( + boolean processSecondaryStructure) { - StructureImportSettings.predictSecStr = predictSecondaryStructure; + StructureImportSettings.processSecStr = processSecondaryStructure; } public static boolean isExternalSecondaryStructure() @@ -80,24 +101,33 @@ public class StructureImportSettings StructureImportSettings.showSeqFeatures = showSeqFeatures; } - public static String getCurrentDefaultFormat() + public static String getDefaultStructureFileFormat() + { + return defaultStructureFileFormat.toString(); + } + + public static void setDefaultStructureFileFormat( + String defaultStructureFileFormat) { - return currentDefaultFormat; + StructureImportSettings.defaultStructureFileFormat = PDBEntry.Type + .valueOf(defaultStructureFileFormat.toUpperCase()); } - public static void setCurrentDefaultFormat(String currentDefaultFormat) + public static String getDefaultPDBFileParser() { - StructureImportSettings.currentDefaultFormat = currentDefaultFormat; + return defaultPDBFileParser.toString(); } - public static boolean isProcessHETATMs() + public static void setDefaultPDBFileParser( + StructureParser defaultPDBFileParser) { - return processHETATMs; + StructureImportSettings.defaultPDBFileParser = defaultPDBFileParser; } - public static void setProcessHETATMs(boolean processHETATMs) + public static void setDefaultPDBFileParser(String defaultPDBFileParser) { - StructureImportSettings.processHETATMs = processHETATMs; + StructureImportSettings.defaultPDBFileParser = StructureParser + .valueOf(defaultPDBFileParser.toUpperCase()); } } diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index fb96b22..be042e6 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -385,7 +385,11 @@ public class StructureSelectionManager try { - if (pdbFile != null && isCIFFile(pdbFile)) + boolean isParseWithJMOL = StructureImportSettings + .getDefaultPDBFileParser().equalsIgnoreCase( + StructureImportSettings.StructureParser.JMOL_PARSER + .toString()); + if (isParseWithJMOL || (pdbFile != null && isCIFFile(pdbFile))) { pdb = new jalview.ext.jmol.JmolParser(addTempFacAnnot, parseSecStr, secStructServices, pdbFile, protocol); diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index d3df56c..19ebf45 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -844,14 +844,22 @@ public abstract class AlignmentViewport implements AlignViewportI, && !al.getCodonFrames().isEmpty()) { /* - * fudge - check first mapping is protein-to-nucleotide + * fudge - check first for protein-to-nucleotide mappings * (we don't want to do this for protein-to-protein) */ - AlignedCodonFrame mapping = al.getCodonFrames().iterator().next(); - // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? - MapList[] mapLists = mapping.getdnaToProt(); - // mapLists can be empty if project load has not finished resolving seqs - if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + boolean doConsensus = false; + for (AlignedCodonFrame mapping : al.getCodonFrames()) + { + // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? + MapList[] mapLists = mapping.getdnaToProt(); + // mapLists can be empty if project load has not finished resolving seqs + if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + { + doConsensus = true; + break; + } + } + if (doConsensus) { if (calculator .getRegisteredWorkersOfClass(ComplementConsensusThread.class) == null) @@ -1872,12 +1880,20 @@ public abstract class AlignmentViewport implements AlignViewportI, .getCodonFrames(); if (codonMappings != null && !codonMappings.isEmpty()) { - // fudge: check mappings are not protein-to-protein - // TODO: nicer - AlignedCodonFrame mapping = codonMappings.iterator().next(); - MapList[] mapLists = mapping.getdnaToProt(); - // mapLists can be empty if project load has not finished resolving seqs - if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + boolean doConsensus = false; + for (AlignedCodonFrame mapping : codonMappings) + { + // TODO hold mapping type e.g. dna-to-protein in AlignedCodonFrame? + MapList[] mapLists = mapping.getdnaToProt(); + // mapLists can be empty if project load has not finished resolving + // seqs + if (mapLists.length > 0 && mapLists[0].getFromRatio() == 3) + { + doConsensus = true; + break; + } + } + if (doConsensus) { complementConsensus = new AlignmentAnnotation("cDNA Consensus", "PID for cDNA", new Annotation[1], 0f, 100f, diff --git a/src/jalview/workers/AlignCalcWorker.java b/src/jalview/workers/AlignCalcWorker.java index 7719c88..771c492 100644 --- a/src/jalview/workers/AlignCalcWorker.java +++ b/src/jalview/workers/AlignCalcWorker.java @@ -26,6 +26,7 @@ import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; +import jalview.datamodel.Annotation; import java.util.List; @@ -103,4 +104,33 @@ public abstract class AlignCalcWorker implements AlignCalcWorkerI return false; } + /** + * Calculate min and max values of annotations and set as graphMin, graphMax + * on the AlignmentAnnotation. This is needed because otherwise, well, bad + * things happen. + * + * @param ann + * @param anns + */ + protected void setGraphMinMax(AlignmentAnnotation ann, Annotation[] anns) + { + // TODO feels like this belongs inside AlignmentAnnotation! + float max = Float.MIN_VALUE; + float min = Float.MAX_VALUE; + boolean set = false; + for (Annotation a : anns) { + if (a != null) { + set = true; + float val = a.value; + max = Math.max(max, val); + min = Math.min(min, val); + } + } + if (set) + { + ann.graphMin = min; + ann.graphMax = max; + } + } + } diff --git a/src/jalview/workers/AnnotationWorker.java b/src/jalview/workers/AnnotationWorker.java index efe707a..4d81307 100644 --- a/src/jalview/workers/AnnotationWorker.java +++ b/src/jalview/workers/AnnotationWorker.java @@ -79,7 +79,7 @@ class AnnotationWorker extends AlignCalcWorker return; } - removeAnnotations(); + // removeAnnotation(); AlignmentI alignment = alignViewport.getAlignment(); if (alignment != null) { @@ -89,10 +89,19 @@ class AnnotationWorker extends AlignCalcWorker alignment, new FeatureRenderer(alignViewport)); for (AlignmentAnnotation ann : anns) { - ann.showAllColLabels = true; - ann.graph = AlignmentAnnotation.BAR_GRAPH; - ourAnnots.add(ann); - alignment.addAnnotation(ann); + AlignmentAnnotation theAnn = alignment.findOrCreateAnnotation( + ann.label, ann.description, false, null, null); + theAnn.showAllColLabels = true; + theAnn.graph = AlignmentAnnotation.BAR_GRAPH; + theAnn.scaleColLabel = true; + theAnn.annotations = ann.annotations; + setGraphMinMax(theAnn, theAnn.annotations); + theAnn.validateRangeAndDisplay(); + if (!ourAnnots.contains(theAnn)) + { + ourAnnots.add(theAnn); + } + // alignment.addAnnotation(ann); } } catch (IndexOutOfBoundsException x) { @@ -114,19 +123,6 @@ class AnnotationWorker extends AlignCalcWorker ap.adjustAnnotationHeight(); ap.paintAlignment(true); } - - } - - /** - * Remove all our annotations before re-calculating them - */ - void removeAnnotations() - { - for (AlignmentAnnotation ann : ourAnnots) - { - alignViewport.getAlignment().deleteAnnotation(ann); - } - ourAnnots.clear(); } @Override diff --git a/src/jalview/workers/ColumnCounterWorker.java b/src/jalview/workers/ColumnCounterWorker.java index 5f61525..2f73cb5 100644 --- a/src/jalview/workers/ColumnCounterWorker.java +++ b/src/jalview/workers/ColumnCounterWorker.java @@ -89,7 +89,6 @@ class ColumnCounterWorker extends AlignCalcWorker return; } - removeAnnotation(); if (alignViewport.getAlignment() != null) { try @@ -159,21 +158,29 @@ class ColumnCounterWorker extends AlignCalcWorker { Color color = ColorUtils.getGraduatedColour(count, 0, Color.cyan, max, Color.blue); - anns[i] = new Annotation(String.valueOf(count), - String.valueOf(count), '0', count, color); + String str = String.valueOf(count); + anns[i] = new Annotation(str, str, '0', count, color); } } /* - * construct the annotation, save it and add it to the displayed alignment + * construct or update the annotation */ - AlignmentAnnotation ann = new AlignmentAnnotation(counter.getName(), - counter.getDescription(), anns); + AlignmentAnnotation ann = alignViewport.getAlignment() + .findOrCreateAnnotation(counter.getName(), + counter.getDescription(), false, null, + null); + ann.description = counter.getDescription(); ann.showAllColLabels = true; ann.scaleColLabel = true; ann.graph = AlignmentAnnotation.BAR_GRAPH; - ourAnnots.add(ann); - alignViewport.getAlignment().addAnnotation(ann); + ann.annotations = anns; + setGraphMinMax(ann, anns); + ann.validateRangeAndDisplay(); + if (!ourAnnots.contains(ann)) + { + ourAnnots.add(ann); + } } /** diff --git a/src/jalview/ws/dbsources/Pdb.java b/src/jalview/ws/dbsources/Pdb.java index d945699..61c5c04 100644 --- a/src/jalview/ws/dbsources/Pdb.java +++ b/src/jalview/ws/dbsources/Pdb.java @@ -27,6 +27,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; import jalview.datamodel.PDBEntry; +import jalview.datamodel.PDBEntry.Type; import jalview.datamodel.SequenceI; import jalview.io.FormatAdapter; import jalview.io.PDBFeatureSettings; @@ -132,12 +133,11 @@ public class Pdb extends EbiFileRetrievedProxy stopQuery(); return null; } - String ext = StructureImportSettings.getCurrentDefaultFormat() - .equalsIgnoreCase("mmcif") ? ".cif" - : ".xml"; + String ext = StructureImportSettings.getDefaultStructureFileFormat() + .equalsIgnoreCase(Type.MMCIF.toString()) ? ".cif" : ".xml"; EBIFetchClient ebi = new EBIFetchClient(); file = ebi.fetchDataAsFile("pdb:" + id, - StructureImportSettings.getCurrentDefaultFormat().toLowerCase(), + StructureImportSettings.getDefaultStructureFileFormat().toLowerCase(), ext) .getAbsolutePath(); stopQuery(); @@ -150,7 +150,7 @@ public class Pdb extends EbiFileRetrievedProxy pdbAlignment = new FormatAdapter().readFile(file, jalview.io.AppletFormatAdapter.FILE, - StructureImportSettings.getCurrentDefaultFormat()); + StructureImportSettings.getDefaultStructureFileFormat()); if (pdbAlignment != null) { List toremove = new ArrayList(); diff --git a/test/jalview/analysis/DnaTest.java b/test/jalview/analysis/DnaTest.java index 9a4c357..0142ab5 100644 --- a/test/jalview/analysis/DnaTest.java +++ b/test/jalview/analysis/DnaTest.java @@ -29,6 +29,7 @@ import jalview.datamodel.AlignedCodon; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.gui.AlignViewport; import jalview.io.FormatAdapter; @@ -498,17 +499,44 @@ public class DnaTest @Test(groups = "Functional") public void testReverseSequence() { - String seq = "AcGtUrYkMbVdHNX"; + String seq = "-Ac-GtU--rYkMbVdHNX-"; + String seqRev = new StringBuilder(seq).reverse().toString(); // reverse: SequenceI reversed = Dna.reverseSequence("Seq1", seq, false); - assertEquals(new StringBuilder(seq).reverse() - .toString(), reversed.getSequenceAsString()); + assertEquals(1, reversed.getStart()); + assertEquals(15, reversed.getEnd()); + assertEquals(20, reversed.getLength()); + assertEquals(seqRev, reversed.getSequenceAsString()); assertEquals("Seq1|rev", reversed.getName()); // reverse complement: SequenceI revcomp = Dna.reverseSequence("Seq1", seq, true); - assertEquals("XNDhBvKmRyAaCgT", revcomp.getSequenceAsString()); + assertEquals("-XNDhBvKmRy--AaC-gT-", revcomp.getSequenceAsString()); assertEquals("Seq1|revcomp", revcomp.getName()); } + + @Test(groups = "Functional") + public void testReverseCdna() + { + String seq = "-Ac-GtU--rYkMbVdHNX-"; + String seqRev = new StringBuilder(seq).reverse().toString(); + String seqDs = seq.replaceAll("-", ""); + String seqDsRev = new StringBuilder(seqDs).reverse().toString(); + + SequenceI dna = new Sequence("Seq1", seq); + Alignment al = new Alignment(new SequenceI[] {dna}); + al.createDatasetAlignment(); + assertEquals(seqDs, al.getSequenceAt(0).getDatasetSequence() + .getSequenceAsString()); + + ColumnSelection cs = new ColumnSelection(); + AlignViewportI av = new AlignViewport(al, cs); + Dna testee = new Dna(av, new int[] { 0, al.getWidth() - 1 }); + AlignmentI reversed = testee.reverseCdna(false); + assertEquals(1, reversed.getHeight()); + assertEquals(seqRev, reversed.getSequenceAt(0).getSequenceAsString()); + assertEquals(seqDsRev, reversed.getSequenceAt(0).getDatasetSequence() + .getSequenceAsString()); + } } diff --git a/test/jalview/ext/jmol/JmolParserTest.java b/test/jalview/ext/jmol/JmolParserTest.java index 0627a4a..7ab058e 100644 --- a/test/jalview/ext/jmol/JmolParserTest.java +++ b/test/jalview/ext/jmol/JmolParserTest.java @@ -31,6 +31,7 @@ import jalview.gui.AlignFrame; import jalview.io.AppletFormatAdapter; import jalview.io.FileLoader; import jalview.structure.StructureImportSettings; +import jalview.structure.StructureImportSettings.StructureParser; import java.util.Vector; @@ -89,6 +90,9 @@ public class JmolParserTest Boolean.TRUE.toString()); Cache.applicationProperties.setProperty("ADD_SS_ANN", Boolean.TRUE.toString()); + StructureImportSettings.setDefaultStructureFileFormat("PDB"); + StructureImportSettings + .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER); } @Test(groups = { "Functional" }) @@ -106,7 +110,6 @@ public class JmolParserTest @Test(groups = { "Functional" }) public void testFileParser() throws Exception { - StructureImportSettings.setProcessHETATMs(false); for (String pdbStr : testFile) { PDBfile mctest = new PDBfile(false, false, false, pdbStr, @@ -130,30 +133,7 @@ public class JmolParserTest validateSecStrRows(al); } } - StructureImportSettings.setProcessHETATMs(true); - for (String pdbStr : testFile) - { - PDBfile mctest = new PDBfile(false, false, false, pdbStr, - AppletFormatAdapter.FILE); - JmolParser jtest = new JmolParser(false, false, false, pdbStr, - jalview.io.AppletFormatAdapter.FILE); - Vector seqs = jtest.getSeqs(), mcseqs = mctest.getSeqs(); - assertTrue( - "No sequences extracted from testfile\n" - + (jtest.hasWarningMessage() ? jtest.getWarningMessage() - : "(No warnings raised)"), seqs != null - && seqs.size() > 0); - for (SequenceI sq : seqs) - { - assertEquals("JMol didn't process " + pdbStr - + " to the same sequence as MCView", - sq.getSequenceAsString(), mcseqs.remove(0) - .getSequenceAsString()); - AlignmentI al = new Alignment(new SequenceI[] { sq }); - validateSecStrRows(al); - } - } } private void validateSecStrRows(AlignmentI al) diff --git a/test/jalview/ext/jmol/JmolVsJalviewPDBParserEndToEndTest.java b/test/jalview/ext/jmol/JmolVsJalviewPDBParserEndToEndTest.java new file mode 100644 index 0000000..8a89830 --- /dev/null +++ b/test/jalview/ext/jmol/JmolVsJalviewPDBParserEndToEndTest.java @@ -0,0 +1,102 @@ +package jalview.ext.jmol; + +import jalview.datamodel.SequenceI; +import jalview.io.AppletFormatAdapter; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; + +import MCview.PDBfile; + +/** + * This is not a unit test, rather it is a bulk End-to-End scan for sequences + * consistency for PDB files parsed with JmolParser vs. Jalview's PDBfile + * parser. The directory of PDB files to test must be provided in the launch + * args. + * + * @author tcnofoegbu + * + */ +public class JmolVsJalviewPDBParserEndToEndTest +{ + + public static void main(String[] args) + { + if (args == null || args[0] == null) + { + System.err + .println("You must provide a PDB directory in the launch argument"); + return; + } + + // args[0] must provide the directory of PDB files to run the test with + String testDir = args[0]; + System.out.println("PDB directory : " + testDir); + File pdbDir = new File(testDir); + String testFiles[] = pdbDir.list(); + testFileParser(testDir, testFiles); + } + + public static void testFileParser(String testDir, String[] testFiles) + { + Set failedFiles = new HashSet(); + int totalSeqScanned = 0, totalFail = 0; + for (String pdbStr : testFiles) + { + String testFile = testDir + "/" + pdbStr; + PDBfile mctest = null; + JmolParser jtest = null; + try + { + mctest = new PDBfile(false, false, false, testFile, + AppletFormatAdapter.FILE); + jtest = new JmolParser(false, false, false, testFile, + jalview.io.AppletFormatAdapter.FILE); + } catch (IOException e) + { + System.err.println("Exception thrown while parsing : " + pdbStr); + } + Vector seqs = jtest.getSeqs(); + Vector mcseqs = mctest.getSeqs(); + + for (SequenceI sq : seqs) + { + try + { + String testSeq = mcseqs.remove(0).getSequenceAsString(); + if (!sq.getSequenceAsString().equals(testSeq)) + { + ++totalFail; + System.err.println("Test Failed for " + pdbStr + ". Diff:"); + System.err.println(sq.getSequenceAsString()); + System.err.println(testSeq); + failedFiles.add(pdbStr); + } + ++totalSeqScanned; + } catch (Exception e) + { + e.printStackTrace(); + } + } + } + int count = 0; + + System.out.println("\n\nTotal sequence Scanned : " + totalSeqScanned); + System.out.println("Total sequence passed : " + + (totalSeqScanned - totalFail)); + System.out.println("Total sequence failed : " + totalFail); + System.out + .println("Success rate: " + + ((totalSeqScanned - totalFail) * 100) + / totalSeqScanned + "%"); + System.out.println("\nList of " + failedFiles.size() + + " file(s) with sequence diffs:"); + for (String problemFile : failedFiles) + { + System.out.println(++count + ". " + problemFile); + } + } +} diff --git a/test/jalview/io/AnnotatedPDBFileInputTest.java b/test/jalview/io/AnnotatedPDBFileInputTest.java index 3524a88..e6019aa 100644 --- a/test/jalview/io/AnnotatedPDBFileInputTest.java +++ b/test/jalview/io/AnnotatedPDBFileInputTest.java @@ -32,6 +32,7 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.structure.StructureImportSettings; +import jalview.structure.StructureImportSettings.StructureParser; import java.io.File; @@ -66,7 +67,9 @@ public class AnnotatedPDBFileInputTest al = af.getViewport().getAlignment(); pdbId = al.getSequenceAt(0).getDatasetSequence().getAllPDBEntries() .get(0).getId(); - StructureImportSettings.setCurrentDefaultFormat("PDB"); + StructureImportSettings.setDefaultStructureFileFormat("PDB"); + StructureImportSettings + .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER); } @Test(groups = { "Functional" }) diff --git a/test/jalview/io/Jalview2xmlTests.java b/test/jalview/io/Jalview2xmlTests.java index 38153df..e1bc3ae 100644 --- a/test/jalview/io/Jalview2xmlTests.java +++ b/test/jalview/io/Jalview2xmlTests.java @@ -41,10 +41,13 @@ import jalview.gui.Desktop; import jalview.gui.Jalview2XML; import jalview.schemes.AnnotationColourGradient; import jalview.schemes.ColourSchemeI; +import jalview.structure.StructureImportSettings; import jalview.viewmodel.AlignmentViewport; import java.io.File; +import java.time.Instant; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,6 +70,9 @@ public class Jalview2xmlTests { jalview.bin.Jalview.main(new String[] { "-props", "test/jalview/io/testProps.jvprops" }); + jalview.bin.Cache.setProperty("JALVIEW_NEWS_RSS_LASTMODIFIED", + Cache.date_format.format(Date.from(Instant.now().plusSeconds( + 3600)))); } /** @@ -270,10 +276,11 @@ public class Jalview2xmlTests @Test(groups = { "Functional" }) public void viewRefPdbAnnotation() throws Exception { - Cache.applicationProperties.setProperty("STRUCT_FROM_PDB", - Boolean.TRUE.toString()); - Cache.applicationProperties.setProperty("ADD_SS_ANN", - Boolean.TRUE.toString()); + // TODO: Make this pass without setting StructureParser.JALVIEW_PARSER + // StructureImportSettings + // .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER); + StructureImportSettings.setProcessSecondaryStructure(true); + StructureImportSettings.setVisibleChainAnnotation(true); AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded( "examples/exampleFile_2_7.jar", FormatAdapter.FILE); assertTrue("Didn't read in the example file correctly.", af != null); @@ -364,37 +371,29 @@ public class Jalview2xmlTests } /** - * test store and recovery of expanded views - currently this is disabled - * since the Desktop.explodeViews method doesn't seem to result in the views - * being expanded to distinct align frames when executed programmatically. + * test store and recovery of expanded views * * @throws Exception */ @Test(groups = { "Functional" }, enabled = true) public void testStoreAndRecoverExpandedviews() throws Exception { + Desktop.instance.closeAll_actionPerformed(null); + AlignFrame af = new jalview.io.FileLoader().LoadFileWaitTillLoaded( "examples/exampleFile_2_7.jar", FormatAdapter.FILE); assertTrue("Didn't read in the example file correctly.", af != null); + Assert.assertEquals(Desktop.getAlignFrames().length, 1); String afid = af.getViewport().getSequenceSetId(); - { - final AlignFrame xaf = af; - af = null; - new Thread(new Runnable() - { - @Override - public void run() - { - Desktop.instance.explodeViews(xaf); - } - }).start(); - Thread.sleep(1000); - } - // int times = 0; - // while (++times < 5 && Desktop.getAlignFrames().length < ) - // { - // Thread.sleep(300); - // } + + // check FileLoader returned a reference to the one alignFrame that is + // actually on the Desktop + assertTrue( + "Jalview2XML.loadAlignFrame() didn't return correct AlignFrame reference for multiple view window", + af == Desktop.getAlignFrameFor(af.getViewport())); + + Desktop.explodeViews(af); + int oldviews = Desktop.getAlignFrames().length; Assert.assertEquals(Desktop.getAlignFrames().length, Desktop.getAlignmentPanels(afid).length); diff --git a/test/jalview/ws/PDBSequenceFetcherTest.java b/test/jalview/ws/PDBSequenceFetcherTest.java index 0c810a3..fda0198 100644 --- a/test/jalview/ws/PDBSequenceFetcherTest.java +++ b/test/jalview/ws/PDBSequenceFetcherTest.java @@ -26,6 +26,7 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.structure.StructureImportSettings; +import jalview.structure.StructureImportSettings.StructureParser; import jalview.ws.seqfetcher.DbSourceProxy; import java.util.List; @@ -86,7 +87,9 @@ public class PDBSequenceFetcherTest { Cache.applicationProperties.setProperty("STRUCT_FROM_PDB", Boolean.TRUE.toString()); - StructureImportSettings.setCurrentDefaultFormat("PDB"); + StructureImportSettings.setDefaultStructureFileFormat("PDB"); + StructureImportSettings + .setDefaultPDBFileParser(StructureParser.JALVIEW_PARSER); testRetrieveProteinSeqFromPDB(); } @@ -96,7 +99,7 @@ public class PDBSequenceFetcherTest { Cache.applicationProperties.setProperty("STRUCT_FROM_PDB", Boolean.TRUE.toString()); - StructureImportSettings.setCurrentDefaultFormat("mmCIF"); + StructureImportSettings.setDefaultStructureFileFormat("mmCIF"); testRetrieveProteinSeqFromPDB(); } diff --git a/test/jalview/ws/sifts/SiftsClientTest.java b/test/jalview/ws/sifts/SiftsClientTest.java index 9141bad..6f9a864 100644 --- a/test/jalview/ws/sifts/SiftsClientTest.java +++ b/test/jalview/ws/sifts/SiftsClientTest.java @@ -251,8 +251,6 @@ public class SiftsClientTest // TODO delete when auto-fetching of DBRefEntry is implemented DBRefEntry dbRef = new DBRefEntry("uniprot", "", "P00221"); - dbRef.setStartRes(1); - dbRef.setEndRes(147); testSeq.addDBRef(dbRef); // testSeq.setSourceDBRef(dbRef); @@ -327,8 +325,6 @@ public class SiftsClientTest DBRefEntryI expectedDBRef = new DBRefEntry(); expectedDBRef.setSource(DBRefSource.UNIPROT); expectedDBRef.setAccessionId("P00221"); - expectedDBRef.setStartRes(1); - expectedDBRef.setEndRes(147); expectedDBRef.setVersion(""); Assert.assertEquals(actualValidSrcDBRef, expectedDBRef); } catch (Exception e) @@ -376,8 +372,6 @@ public class SiftsClientTest DBRefEntryI validDBRef = new DBRefEntry(); validDBRef.setSource(DBRefSource.UNIPROT); validDBRef.setAccessionId("P00221"); - validDBRef.setStartRes(1); - validDBRef.setEndRes(147); validDBRef.setVersion(""); Assert.assertTrue(siftsClient.isValidDBRefEntry(validDBRef)); }