From: Jim Procter Date: Wed, 7 Mar 2018 14:40:59 +0000 (+0000) Subject: Merge branch 'releases/Release_2_10_4_Branch' into develop X-Git-Tag: Release_2_11_0~55 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=948bd3bcbacc509da0cefaae3eedd97300a6ccce;p=jalview.git Merge branch 'releases/Release_2_10_4_Branch' into develop check carefully that JAL-2885 (configurable Ensembl/Genomes Endpoints) functionality is still present --- 948bd3bcbacc509da0cefaae3eedd97300a6ccce diff --cc src/jalview/analysis/AlignmentUtils.java index 5e11446,343ebc7..d1217bf --- a/src/jalview/analysis/AlignmentUtils.java +++ b/src/jalview/analysis/AlignmentUtils.java @@@ -2049,16 -2004,16 +2059,18 @@@ public class AlignmentUtil * * @param cdsSeq * @param contig + * @param proteinProduct * @param mapping - * @return list of DBRefEntrys added. + * @return list of DBRefEntrys added */ - public static List propagateDBRefsToCDS(SequenceI cdsSeq, + protected static List propagateDBRefsToCDS(SequenceI cdsSeq, SequenceI contig, SequenceI proteinProduct, Mapping mapping) { + - // gather direct refs from contig congrent with mapping + // gather direct refs from contig congruent with mapping List direct = new ArrayList<>(); HashSet directSources = new HashSet<>(); ++ if (contig.getDBRefs() != null) { for (DBRefEntry dbr : contig.getDBRefs()) diff --cc src/jalview/analysis/Dna.java index ef05a58,d534c8f..2ad8487 --- a/src/jalview/analysis/Dna.java +++ b/src/jalview/analysis/Dna.java @@@ -392,26 -436,12 +436,13 @@@ public class Dn List proteinSeqs) { List skip = new ArrayList<>(); - int skipint[] = null; - ShiftList vismapping = new ShiftList(); // map from viscontigs to seqstring - // intervals - int vc; - int[] scontigs = new int[contigs.length]; + int[] skipint = null; ++ int npos = 0; - for (vc = 0; vc < contigs.length; vc += 2) - { - if (vc == 0) - { - vismapping.addShift(npos, contigs[vc]); - } - else - { - // hidden region - vismapping.addShift(npos, contigs[vc] - contigs[vc - 1] + 1); - } - scontigs[vc] = contigs[vc]; - scontigs[vc + 1] = contigs[vc + 1]; - } + int vc = 0; + + int[] scontigs = new int[startcontigs.length]; + System.arraycopy(startcontigs, 0, scontigs, 0, startcontigs.length); // allocate a roughly sized buffer for the protein sequence StringBuilder protein = new StringBuilder(seqstring.length() / 2); diff --cc src/jalview/datamodel/SequenceI.java index fb723e6,b22e48f..8dce31e --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@@ -21,9 -21,9 +21,10 @@@ package jalview.datamodel; import jalview.datamodel.features.SequenceFeaturesI; +import jalview.util.MapList; import java.util.BitSet; + import java.util.Iterator; import java.util.List; import java.util.Vector; @@@ -527,20 -534,23 +535,42 @@@ public interface SequenceI extends ASeq public int replace(char c1, char c2); /** + * Answers the GeneLociI, or null if not known + * + * @return + */ + GeneLociI getGeneLoci(); + + /** + * Sets the mapping to gene loci for the sequence + * + * @param speciesId + * @param assemblyId + * @param chromosomeId + * @param map + */ + void setGeneLoci(String speciesId, String assemblyId, + String chromosomeId, MapList map); ++ ++ ++ /** + * Returns the sequence string constructed from the substrings of a sequence + * defined by the int[] ranges provided by an iterator. E.g. the iterator + * could iterate over all visible regions of the alignment + * + * @param it + * the iterator to use + * @return a String corresponding to the sequence + */ + public String getSequenceStringFromIterator(Iterator it); + + /** + * Locate the first position in this sequence which is not contained in an + * iterator region. If no such position exists, return 0 + * + * @param it + * iterator over regions + * @return first residue not contained in regions + */ + public int firstResidueOutsideIterator(Iterator it); } diff --cc src/jalview/ext/ensembl/EnsemblInfo.java index de55a53,7668941..37dff51 --- a/src/jalview/ext/ensembl/EnsemblInfo.java +++ b/src/jalview/ext/ensembl/EnsemblInfo.java @@@ -1,185 -1,86 +1,185 @@@ -/* - * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) - * Copyright (C) $$Year-Rel$$ The Jalview Authors - * - * This file is part of Jalview. - * - * Jalview is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Jalview is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Jalview. If not, see . - * The Jalview Authors are detailed in the 'AUTHORS' file. - */ package jalview.ext.ensembl; -/** - * A data class to model the data and rest version of one Ensembl domain, - * currently for rest.ensembl.org and rest.ensemblgenomes.org - * - * @author gmcarstairs - */ -class EnsemblInfo +import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefSource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.json.simple.JSONArray; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +public class EnsemblInfo extends EnsemblRestClient { - /* - * The http domain this object is holding data values for - */ - String domain; /* - * The latest version Jalview has tested for, e.g. "4.5"; a minor version change should be - * ok, a major version change may break stuff + * cached results of REST /info/divisions service, currently + *
 +   * { 
 +   *  { "ENSEMBLFUNGI", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLBACTERIA", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLPROTISTS", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLMETAZOA", "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBLPLANTS",  "http://rest.ensemblgenomes.org"},
 +   *    "ENSEMBL", "http://rest.ensembl.org" }
 +   *  }
 +   * 
+ * The values for EnsemblGenomes are retrieved by a REST call, that for + * Ensembl is added programmatically for convenience of lookup */ - String expectedRestVersion; + private static Map divisions; - /* - * Major / minor / point version e.g. "4.5.1" - * @see http://rest.ensembl.org/info/rest/?content-type=application/json - */ - String restVersion; + @Override + public String getDbName() + { + return "ENSEMBL"; + } - /* - * data version - * @see http://rest.ensembl.org/info/data/?content-type=application/json - */ - String dataVersion; + @Override + public AlignmentI getSequenceRecords(String queries) throws Exception + { + return null; + } - /* - * true when http://rest.ensembl.org/info/ping/?content-type=application/json - * returns response code 200 and not {"error":"Database is unavailable"} + @Override + protected URL getUrl(List ids) throws MalformedURLException + { + return null; + } + + @Override + protected boolean useGetRequest() + { + return true; + } + + @Override + protected String getRequestMimeType(boolean multipleIds) + { + return "application/json"; + } + + @Override + protected String getResponseMimeType() + { + return "application/json"; + } + + /** + * Answers the domain (http://rest.ensembl.org or + * http://rest.ensemblgenomes.org) for the given division, or null if not + * recognised by Ensembl. + * + * @param division + * @return */ - boolean restAvailable; + public String getDomain(String division) + { + if (divisions == null) + { + fetchDivisions(); + } + return divisions.get(division.toUpperCase()); + } - /* - * absolute time when availability was last checked + /** + * On first request only, populate the lookup map by fetching the list of + * divisions known to EnsemblGenomes. */ - long lastAvailableCheckTime; + void fetchDivisions() + { + divisions = new HashMap<>(); - /* - * absolute time when version numbers were last checked + /* + * for convenience, pre-fill ensembl.org as the domain for "ENSEMBL" + */ - divisions.put(DBRefSource.ENSEMBL.toUpperCase(), ENSEMBL_REST); ++ divisions.put(DBRefSource.ENSEMBL.toUpperCase(), ensemblDomain); + + BufferedReader br = null; + try + { - URL url = getDivisionsUrl(ENSEMBL_GENOMES_REST); ++ URL url = getDivisionsUrl(ensemblGenomesDomain); + if (url != null) + { + br = getHttpResponse(url, null); + } - parseResponse(br, ENSEMBL_GENOMES_REST); ++ parseResponse(br, ensemblGenomesDomain); + } catch (IOException e) + { + // ignore + } finally + { + if (br != null) + { + try + { + br.close(); + } catch (IOException e) + { + // ignore + } + } + } + } + + /** + * Parses the JSON response to /info/divisions, and add each to the lookup map + * + * @param br + * @param domain */ - long lastVersionCheckTime; + void parseResponse(BufferedReader br, String domain) + { + JSONParser jp = new JSONParser(); + + try + { + JSONArray parsed = (JSONArray) jp.parse(br); - // flag set to true if REST major version is not the one expected - boolean restMajorVersionMismatch; + Iterator rvals = parsed.iterator(); + while (rvals.hasNext()) + { + String division = rvals.next().toString(); + divisions.put(division.toUpperCase(), domain); + } + } catch (IOException | ParseException | NumberFormatException e) + { + // ignore + } + } /** - * Constructor given expected REST version number e.g 4.5 or 3.4.3 + * Constructs the URL for the EnsemblGenomes /info/divisions REST service + * @param domain TODO * - * @param restExpected + * @return + * @throws MalformedURLException */ - EnsemblInfo(String theDomain, String restExpected) + URL getDivisionsUrl(String domain) throws MalformedURLException { - domain = theDomain; - expectedRestVersion = restExpected; - lastAvailableCheckTime = -1; - lastVersionCheckTime = -1; + return new URL(domain + + "/info/divisions?content-type=application/json"); } + /** + * Returns the set of 'divisions' recognised by Ensembl or EnsemblGenomes + * + * @return + */ + public Set getDivisions() { + if (divisions == null) + { + fetchDivisions(); + } + + return divisions.keySet(); + } } diff --cc src/jalview/ext/ensembl/EnsemblLookup.java index f6b3a47,0ddef2b..e55605d --- a/src/jalview/ext/ensembl/EnsemblLookup.java +++ b/src/jalview/ext/ensembl/EnsemblLookup.java @@@ -46,7 -41,18 +46,18 @@@ import org.json.simple.parser.ParseExce */ public class EnsemblLookup extends EnsemblRestClient { - + private static final String SPECIES = "species"; + private static final String OBJECT_TYPE_TRANSLATION = "Translation"; + private static final String PARENT = "Parent"; + private static final String OBJECT_TYPE_TRANSCRIPT = "Transcript"; + private static final String ID = "id"; + private static final String OBJECT_TYPE_GENE = "Gene"; + private static final String OBJECT_TYPE = "object_type"; + + /** + * keep track of last identifier retrieved to break loops + */ + private String lastId; /** * Default constructor (to use rest.ensembl.org) diff --cc src/jalview/ext/ensembl/EnsemblRestClient.java index e3d1215,b19f557..9dea886 --- a/src/jalview/ext/ensembl/EnsemblRestClient.java +++ b/src/jalview/ext/ensembl/EnsemblRestClient.java @@@ -85,11 -82,11 +85,11 @@@ abstract class EnsemblRestClient extend static { + domainData = new HashMap<>(); - domainData.put(ENSEMBL_REST, - new EnsemblData(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION)); - domainData.put(ENSEMBL_GENOMES_REST, new EnsemblData( - ENSEMBL_GENOMES_REST, LATEST_ENSEMBLGENOMES_REST_VERSION)); + domainData.put(DEFAULT_ENSEMBL_BASEURL, - new EnsemblInfo(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION)); - domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL, - new EnsemblInfo( ++ new EnsemblData(DEFAULT_ENSEMBL_BASEURL, LATEST_ENSEMBL_REST_VERSION)); ++ domainData.put(DEFAULT_ENSEMBL_GENOMES_BASEURL, new EnsemblData( + DEFAULT_ENSEMBL_GENOMES_BASEURL, LATEST_ENSEMBLGENOMES_REST_VERSION)); } protected volatile boolean inProgress = false; @@@ -99,7 -96,21 +99,21 @@@ */ public EnsemblRestClient() { - this(ENSEMBL_REST); + super(); + + /* + * initialise domain info lazily + */ + if (!domainData.containsKey(ensemblDomain)) + { + domainData.put(ensemblDomain, - new EnsemblInfo(ensemblDomain, LATEST_ENSEMBL_REST_VERSION)); ++ new EnsemblData(ensemblDomain, LATEST_ENSEMBL_REST_VERSION)); + } + if (!domainData.containsKey(ensemblGenomesDomain)) + { - domainData.put(ensemblGenomesDomain, new EnsemblInfo( ++ domainData.put(ensemblGenomesDomain, new EnsemblData( + ensemblGenomesDomain, LATEST_ENSEMBLGENOMES_REST_VERSION)); + } } /** diff --cc test/jalview/analysis/AlignmentUtilsTests.java index 37b93fd,35196fa..92bf0ce --- a/test/jalview/analysis/AlignmentUtilsTests.java +++ b/test/jalview/analysis/AlignmentUtilsTests.java @@@ -2082,6 -2034,6 +2083,7 @@@ public class AlignmentUtilsTest List codon1Variants = new ArrayList<>(); List codon2Variants = new ArrayList<>(); List codon3Variants = new ArrayList<>(); ++ List codonVariants[] = new ArrayList[3]; codonVariants[0] = codon1Variants; codonVariants[1] = codon2Variants; diff --cc test/jalview/gui/AlignFrameTest.java index 1ee25c7,dd1a4de..b0aaab9 --- a/test/jalview/gui/AlignFrameTest.java +++ b/test/jalview/gui/AlignFrameTest.java @@@ -31,6 -30,6 +31,7 @@@ import jalview.bin.Cache import jalview.bin.Jalview; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; ++import jalview.datamodel.HiddenColumns; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceGroup; @@@ -92,52 -85,37 +93,60 @@@ public class AlignFrameTes */ assertFalse(alignFrame.hideFeatureColumns("exon", true)); assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); - assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenColumnsCopy().isEmpty()); ++ + assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns() + .getNumberOfRegions(), 0); ++ assertFalse(alignFrame.hideFeatureColumns("exon", false)); assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); - assertTrue(alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenColumnsCopy().isEmpty()); ++ + assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns() + .getNumberOfRegions(), 0); /* * hiding a feature in all columns does nothing */ assertFalse(alignFrame.hideFeatureColumns("Metal", true)); assertTrue(alignFrame.getViewport().getColumnSelection().isEmpty()); - List hidden = alignFrame.getViewport().getAlignment() - .getHiddenColumns().getHiddenColumnsCopy(); - assertTrue(hidden.isEmpty()); ++ + assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns() + .getNumberOfRegions(), 0); + + + /* + * threshold Metal to hide features where score < 5 + * seq1 feature in columns 1-5 is hidden + * seq2 feature in columns 6-10 is shown + */ + FeatureColourI fc = new FeatureColour(Color.red, Color.blue, 0f, 10f); + fc.setAboveThreshold(true); + fc.setThreshold(5f); + alignFrame.getFeatureRenderer().setColour("Metal", fc); + assertTrue(alignFrame.hideFeatureColumns("Metal", true)); - hidden = alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenColumnsCopy(); - assertEquals(hidden.size(), 1); - assertEquals(hidden.get(0)[0], 5); - assertEquals(hidden.get(0)[1], 9); ++ HiddenColumns hidden = alignFrame.getViewport().getAlignment().getHiddenColumns(); ++ assertEquals(hidden.getNumberOfRegions(), 1); ++ Iterator regions = hidden.iterator(); ++ int[] next = regions.next(); ++ assertEquals(next[0], 5); ++ assertEquals(next[1], 9); + /* * hide a feature present in some columns * sequence positions [2-4], [7-9] are column positions * [1-3], [6-8] base zero */ + alignFrame.getViewport().showAllHiddenColumns(); assertTrue(alignFrame.hideFeatureColumns("Turn", true)); - hidden = alignFrame.getViewport().getAlignment().getHiddenColumns() - .getHiddenColumnsCopy(); - assertEquals(hidden.size(), 2); - assertEquals(hidden.get(0)[0], 1); - assertEquals(hidden.get(0)[1], 3); - assertEquals(hidden.get(1)[0], 6); - assertEquals(hidden.get(1)[1], 8); - Iterator regions = alignFrame.getViewport().getAlignment() ++ regions = alignFrame.getViewport().getAlignment() + .getHiddenColumns().iterator(); + assertEquals(alignFrame.getViewport().getAlignment().getHiddenColumns() + .getNumberOfRegions(), 2); - int[] next = regions.next(); ++ next = regions.next(); + assertEquals(next[0], 1); + assertEquals(next[1], 3); + next = regions.next(); + assertEquals(next[0], 6); + assertEquals(next[1], 8); } @BeforeClass(alwaysRun = true) diff --cc test/jalview/gui/PopupMenuTest.java index 40e624d,8f60021..6f60588 --- a/test/jalview/gui/PopupMenuTest.java +++ b/test/jalview/gui/PopupMenuTest.java @@@ -30,9 -29,12 +30,13 @@@ import jalview.bin.Cache import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; + import jalview.datamodel.ColumnSelection; import jalview.datamodel.DBRefEntry; import jalview.datamodel.DBRefSource; + import jalview.datamodel.HiddenColumns; + import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceFeature; + import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; import jalview.io.FileFormat; @@@ -45,7 -44,7 +49,8 @@@ import jalview.util.UrlConstants import java.awt.Component; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; + import java.util.Iterator; import java.util.List; import javax.swing.JMenu; @@@ -589,4 -563,117 +594,117 @@@ public class PopupMenuTes assertFalse(linkMenu.isEnabled()); } + + /** + * Test for adding feature links + */ + @Test(groups = { "Functional" }) + public void testHideInsertions() + { + // get sequences from the alignment + List seqs = parentPanel.getAlignment().getSequences(); + + // add our own seqs to avoid problems with changes to existing sequences + // (gap at end of sequences varies depending on how tests are run!) + Sequence seqGap1 = new Sequence("GappySeq", + "AAAA----AA-AAAAAAA---AAA-----------AAAAAAAAAA--"); + seqGap1.createDatasetSequence(); + seqs.add(seqGap1); + Sequence seqGap2 = new Sequence("LessGappySeq", + "AAAAAA-AAAAA---AAA--AAAAA--AAAAAAA-AAAAAA"); + seqGap2.createDatasetSequence(); + seqs.add(seqGap2); + Sequence seqGap3 = new Sequence("AnotherGapSeq", + "AAAAAA-AAAAAA--AAAAAA-AAAAAAAAAAA---AAAAAAAA"); + seqGap3.createDatasetSequence(); + seqs.add(seqGap3); + Sequence seqGap4 = new Sequence("NoGaps", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + seqGap4.createDatasetSequence(); + seqs.add(seqGap4); + + ColumnSelection sel = new ColumnSelection(); + parentPanel.av.getAlignment().getHiddenColumns() + .revealAllHiddenColumns(sel); + + // get the Popup Menu for 7th sequence - no insertions - testee = new PopupMenu(parentPanel, (Sequence) seqs.get(7), null); ++ testee = new PopupMenu(parentPanel, seqs.get(7), null); + testee.hideInsertions_actionPerformed(null); + + HiddenColumns hidden = parentPanel.av.getAlignment().getHiddenColumns(); + Iterator it = hidden.iterator(); + assertFalse(it.hasNext()); + + // get the Popup Menu for GappySeq - this time we have insertions - testee = new PopupMenu(parentPanel, (Sequence) seqs.get(4), null); ++ testee = new PopupMenu(parentPanel, seqs.get(4), null); + testee.hideInsertions_actionPerformed(null); + hidden = parentPanel.av.getAlignment().getHiddenColumns(); + it = hidden.iterator(); + + assertTrue(it.hasNext()); + int[] region = it.next(); + assertEquals(region[0], 4); + assertEquals(region[1], 7); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 10); + assertEquals(region[1], 10); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 18); + assertEquals(region[1], 20); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 24); + assertEquals(region[1], 34); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 45); + assertEquals(region[1], 46); + + assertFalse(it.hasNext()); + + sel = new ColumnSelection(); + hidden.revealAllHiddenColumns(sel); + + // make a sequence group and hide insertions within the group + SequenceGroup sg = new SequenceGroup(); + sg.setStartRes(8); + sg.setEndRes(42); + sg.addSequence(seqGap2, false); + sg.addSequence(seqGap3, false); + parentPanel.av.setSelectionGroup(sg); + + // hide columns outside and within selection + // only hidden columns outside the collection will be retained (unless also + // gaps in the selection) + hidden.hideColumns(1, 10); + hidden.hideColumns(31, 40); + + // get the Popup Menu for LessGappySeq in the sequence group - testee = new PopupMenu(parentPanel, (Sequence) seqs.get(5), null); ++ testee = new PopupMenu(parentPanel, seqs.get(5), null); + testee.hideInsertions_actionPerformed(null); + hidden = parentPanel.av.getAlignment().getHiddenColumns(); + it = hidden.iterator(); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 1); + assertEquals(region[1], 7); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 13); + assertEquals(region[1], 14); + + assertTrue(it.hasNext()); + region = it.next(); + assertEquals(region[0], 34); + assertEquals(region[1], 34); + } + }