Merge branch 'bug/JAL-2609attemptedmerge' into develop
authorJim Procter <jprocter@issues.jalview.org>
Mon, 23 Oct 2017 16:17:09 +0000 (17:17 +0100)
committerJim Procter <jprocter@issues.jalview.org>
Mon, 23 Oct 2017 16:17:09 +0000 (17:17 +0100)
25 files changed:
.checkstyle
examples/groovy/PIDmatrix.groovy [new file with mode: 0644]
help/html/releases.html
src/ext/edu/ucsf/rbvi/strucviz2/ChimeraResidue.java
src/jalview/datamodel/Sequence.java
src/jalview/ext/ensembl/EnsemblGene.java
src/jalview/ext/ensembl/EnsemblGenomes.java
src/jalview/ext/ensembl/EnsemblLookup.java
src/jalview/ext/ensembl/EnsemblProtein.java
src/jalview/ext/ensembl/EnsemblRestClient.java
src/jalview/ext/ensembl/EnsemblSymbol.java
src/jalview/ext/ensembl/EnsemblXref.java
src/jalview/ext/jmol/JalviewJmolBinding.java
src/jalview/ext/rbvi/chimera/AtomSpecModel.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/FeatureSettings.java
src/jalview/renderer/seqfeatures/FeatureRenderer.java
test/jalview/datamodel/SequenceTest.java
test/jalview/ext/ensembl/EnsemblGeneTest.java
test/jalview/ext/ensembl/EnsemblSeqProxyTest.java
test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java
test/jalview/gui/AlignmentPanelTest.java
test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java
utils/checkstyle/checkstyle-suppress.xml
utils/checkstyle/import-control.xml

index 0329bb7..a87fa04 100644 (file)
@@ -8,5 +8,4 @@
     <file-match-pattern match-pattern="src/.*.java" include-pattern="true"/>
     <file-match-pattern match-pattern="resources/.*.properties" include-pattern="true"/>
   </fileset>
-  <filter name="NonSrcDirs" enabled="false"/>
 </fileset-config>
diff --git a/examples/groovy/PIDmatrix.groovy b/examples/groovy/PIDmatrix.groovy
new file mode 100644 (file)
index 0000000..b97abcc
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+
+import jalview.analysis.scoremodels.ScoreModels
+import jalview.analysis.scoremodels.SimilarityParams
+
+// generate matrix for current selection using standard Jalview PID
+
+printSimilarityMatrix(true,true,SimilarityParams.Jalview)
+
+/** 
+ * this function prints a sequence similarity matrix in PHYLIP format. 
+ * printSimilarityMatrix(selected-only, include-ids, pidMethod)
+ * 
+ * Allowed values for pidMethod:
+ * 
+ * Jalview's Comparison.PID method includes matching gaps 
+ * and counts over the length of the shorter gapped sequence
+ * SimilarityParams.Jalview;
+ *
+ * 'SeqSpace' mode PCA calculation does not count matching 
+ * gaps but uses longest gapped sequence length
+ *  SimilarityParams.SeqSpace;
+ *
+ * PID calcs from the Raghava-Barton paper
+ * SimilarityParams.PID1: ignores gap-gap, does not score gap-residue,
+ * includes gap-residue in lengths, matches on longer of two sequences.
+ * 
+ * SimilarityParams.PID2: ignores gap-gap,ignores gap-residue, 
+ * matches on longer of two sequences
+ * 
+ * SimilarityParams.PID3: ignores gap-gap,ignores gap-residue, 
+ * matches on shorter of sequences only
+ * 
+ * SimilarityParams.PID4: ignores gap-gap,does not score gap-residue,
+ * includes gap-residue in lengths,matches on shorter of sequences only.
+ */
+
+void printSimilarityMatrix(boolean selview=false, boolean includeids=true, SimilarityParams pidMethod) {
+
+  def currentAlignFrame = jalview.bin.Jalview.getCurrentAlignFrame()
+
+  jalview.gui.AlignViewport av = currentAlignFrame.getCurrentView()
+
+  jalview.datamodel.AlignmentView seqStrings = av.getAlignmentView(selview)
+
+  if (!selview || av.getSelectionGroup()==null) {
+    start = 0
+    end = av.getAlignment().getWidth()
+    seqs = av.getAlignment().getSequencesArray()
+  } else {
+    start = av.getSelectionGroup().getStartRes()
+    end = av.getSelectionGroup().getEndRes() + 1
+    seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment())
+  }
+
+  distanceCalc = ScoreModels.getInstance().getScoreModel("PID",
+      (jalview.api.AlignmentViewPanel) currentAlignFrame.alignPanel)
+
+  def distance=distanceCalc.findSimilarities(
+      seqStrings.getSequenceStrings(jalview.util.Comparison.GAP_DASH),pidMethod)
+
+  // output the PHYLIP Matrix
+
+  print distance.width()+" "+distance.height()+"\n"
+
+  p = 0
+
+  for (v in 1..distance.height()) {
+
+    if (includeids) {
+      print seqs[p++].getDisplayId(false)+" "
+    }
+
+    for (r in 1..distance.width()) {
+      print distance.getValue(v-1,r-1)+" "
+    }
+
+    print "\n"
+  }
+}
\ No newline at end of file
index 38a18ec..cb2b278 100755 (executable)
@@ -86,18 +86,24 @@ li:before {
               429 rate limit request hander
             </li>
             <li>
-              <!-- JAL-2777 -->Structure views don't get updated unless
+              <!-- JAL-2773 -->Structure views don't get updated unless
               their colours have changed
             </li>
           </ul>
+          <ul><li>Example groovy script for generating a matrix of percent identity scores for current alignment.</li></ul>
       </td>
       <td><div align="left">
           <em></em>
           <ul>
+            <li><!-- JAL-2777 -->Structures with whitespace chainCode cannot be viewed in Chimera</li>
             <li><!-- JAL-2728 -->Protein annotation panel too high in CDS/Protein view
             </li> 
             <li><!-- JAL-2757 -->Can't edit the query after the server error warning icon is shown in Uniprot and PDB Free Text Search Dialogs
             </li> 
+            <li><!-- JAL-2253 -->Slow EnsemblGenome ID lookup</li>
+            <li><!-- JAL-2682 -->Race condition when parsing sequence ID strings in parallel</li>
+            <li><!-- JAL-2739 -->Hidden column marker in last column not rendered when switching back from Wrapped to normal view</li> 
+            <li><!-- JAL-2768 -->Annotation display corrupted when scrolling right in unwapped alignment view</li> 
           </ul>
       </td>
     </tr>
index 0045e97..3abbe75 100644 (file)
@@ -383,6 +383,8 @@ public class ChimeraResidue implements ChimeraStructuralObject,
   public void splitInsertionCode(String residue)
   {
     // OK, split the index into number and insertion code
+    // JBPNote - m.matches() can be true even if there is no resnum - this can
+    // cause NumberFormatExceptions below
     Pattern p = Pattern.compile("(\\d*)([A-Z]?)");
     Matcher m = p.matcher(residue);
     if (m.matches())
index 2ee412b..96b0757 100755 (executable)
@@ -38,8 +38,6 @@ import java.util.List;
 import java.util.ListIterator;
 import java.util.Vector;
 
-import com.stevesoft.pat.Regex;
-
 import fr.orsay.lri.varna.models.rna.RNA;
 
 /**
@@ -51,11 +49,6 @@ import fr.orsay.lri.varna.models.rna.RNA;
  */
 public class Sequence extends ASequence implements SequenceI
 {
-  private static final Regex limitrx = new Regex(
-          "[/][0-9]{1,}[-][0-9]{1,}$");
-
-  private static final Regex endrx = new Regex("[0-9]{1,}$");
-
   SequenceI datasetSequence;
 
   String name;
@@ -151,6 +144,10 @@ public class Sequence extends ASequence implements SequenceI
     checkValidRange();
   }
 
+  /**
+   * If 'name' ends in /i-j, where i >= j > 0 are integers, extracts i and j as
+   * start and end respectively and removes the suffix from the name
+   */
   void parseId()
   {
     if (name == null)
@@ -159,17 +156,37 @@ public class Sequence extends ASequence implements SequenceI
               "POSSIBLE IMPLEMENTATION ERROR: null sequence name passed to constructor.");
       name = "";
     }
-    // Does sequence have the /start-end signature?
-    if (limitrx.search(name))
+    int slashPos = name.lastIndexOf('/');
+    if (slashPos > -1 && slashPos < name.length() - 1)
     {
-      name = limitrx.left();
-      endrx.search(limitrx.stringMatched());
-      setStart(Integer.parseInt(limitrx.stringMatched().substring(1,
-              endrx.matchedFrom() - 1)));
-      setEnd(Integer.parseInt(endrx.stringMatched()));
+      String suffix = name.substring(slashPos + 1);
+      String[] range = suffix.split("-");
+      if (range.length == 2)
+      {
+        try
+        {
+          int from = Integer.valueOf(range[0]);
+          int to = Integer.valueOf(range[1]);
+          if (from > 0 && to >= from)
+          {
+            name = name.substring(0, slashPos);
+            setStart(from);
+            setEnd(to);
+            checkValidRange();
+          }
+        } catch (NumberFormatException e)
+        {
+          // leave name unchanged if suffix is invalid
+        }
+      }
     }
   }
 
+  /**
+   * Ensures that 'end' is not before the end of the sequence, that is,
+   * (end-start+1) is at least as long as the count of ungapped positions. Note
+   * that end is permitted to be beyond the end of the sequence data.
+   */
   void checkValidRange()
   {
     // Note: JAL-774 :
@@ -178,7 +195,7 @@ public class Sequence extends ASequence implements SequenceI
       int endRes = 0;
       for (int j = 0; j < sequence.length; j++)
       {
-        if (!jalview.util.Comparison.isGap(sequence[j]))
+        if (!Comparison.isGap(sequence[j]))
         {
           endRes++;
         }
@@ -453,15 +470,15 @@ public class Sequence extends ASequence implements SequenceI
   }
 
   /**
-   * DOCUMENT ME!
+   * Sets the sequence name. If the name ends in /start-end, then the start-end
+   * values are parsed out and set, and the suffix is removed from the name.
    * 
-   * @param name
-   *          DOCUMENT ME!
+   * @param theName
    */
   @Override
-  public void setName(String name)
+  public void setName(String theName)
   {
-    this.name = name;
+    this.name = theName;
     this.parseId();
   }
 
index ad01324..50dfa90 100644 (file)
@@ -161,8 +161,9 @@ public class EnsemblGene extends EnsemblSeqProxy
   }
 
   /**
-   * Converts a query, which may contain one or more gene or transcript
-   * identifiers, into a non-redundant list of gene identifiers.
+   * Converts a query, which may contain one or more gene, transcript, or
+   * external (to Ensembl) identifiers, into a non-redundant list of gene
+   * identifiers.
    * 
    * @param accessions
    * @return
@@ -173,54 +174,30 @@ public class EnsemblGene extends EnsemblSeqProxy
 
     for (String acc : accessions.split(getAccessionSeparator()))
     {
-      if (isGeneIdentifier(acc))
-      {
-        if (!geneIds.contains(acc))
-        {
-          geneIds.add(acc);
-        }
-      }
-
       /*
-       * if given a transcript id, look up its gene parent
+       * First try lookup as an Ensembl (gene or transcript) identifier
        */
-      else if (isTranscriptIdentifier(acc))
+      String geneId = new EnsemblLookup(getDomain()).getGeneId(acc);
+      if (geneId != null)
       {
-        String geneId = new EnsemblLookup(getDomain()).getParent(acc);
-        if (geneId != null && !geneIds.contains(geneId))
+        if (!geneIds.contains(geneId))
         {
           geneIds.add(geneId);
         }
       }
-      else if (isProteinIdentifier(acc))
-      {
-        String tscriptId = new EnsemblLookup(getDomain()).getParent(acc);
-        if (tscriptId != null)
-        {
-          String geneId = new EnsemblLookup(getDomain())
-                  .getParent(tscriptId);
-
-          if (geneId != null && !geneIds.contains(geneId))
-          {
-            geneIds.add(geneId);
-          }
-        }
-        // NOTE - acc is lost if it resembles an ENS.+ ID but isn't actually
-        // resolving to one... e.g. ENSMICP00000009241
-      }
-      /*
-       * if given a gene or other external name, lookup and fetch 
-       * the corresponding gene for all model organisms 
-       */
       else
       {
+        /*
+         * if given a gene or other external name, lookup and fetch 
+         * the corresponding gene for all model organisms 
+         */
         List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
-                getDbVersion()).getIds(acc);
-        for (String geneId : ids)
+                getDbVersion()).getGeneIds(acc);
+        for (String id : ids)
         {
-          if (!geneIds.contains(geneId))
+          if (!geneIds.contains(id))
           {
-            geneIds.add(geneId);
+            geneIds.add(id);
           }
         }
       }
@@ -229,30 +206,6 @@ public class EnsemblGene extends EnsemblSeqProxy
   }
 
   /**
-   * Attempts to get Ensembl stable identifiers for model organisms for a gene
-   * name by calling the xrefs symbol REST service to resolve the gene name.
-   * 
-   * @param query
-   * @return
-   */
-  protected String getGeneIdentifiersForName(String query)
-  {
-    List<String> ids = new EnsemblSymbol(getDomain(), getDbSource(),
-            getDbVersion()).getIds(query);
-    if (ids != null)
-    {
-      for (String id : ids)
-      {
-        if (isGeneIdentifier(id))
-        {
-          return id;
-        }
-      }
-    }
-    return null;
-  }
-
-  /**
    * Constructs all transcripts for the gene, as identified by "transcript"
    * features whose Parent is the requested gene. The coding transcript
    * sequences (i.e. with introns omitted) are added to the alignment.
index ef46a5b..b40df50 100644 (file)
@@ -39,17 +39,12 @@ public class EnsemblGenomes extends EnsemblGene
   }
 
   @Override
-  public boolean isGeneIdentifier(String query)
-  {
-    return true;
-  }
-
-  @Override
   public String getDbName()
   {
     return "EnsemblGenomes";
   }
 
+  private String Wrong[];
   @Override
   public String getTestQuery()
   {
index 6483401..31da9c0 100644 (file)
@@ -43,6 +43,13 @@ import org.json.simple.parser.ParseException;
 public class EnsemblLookup extends EnsemblRestClient
 {
 
+  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";
+
   /**
    * Default constructor (to use rest.ensembl.org)
    */
@@ -87,7 +94,7 @@ public class EnsemblLookup extends EnsemblRestClient
   protected URL getUrl(String identifier)
   {
     String url = getDomain() + "/lookup/id/" + identifier
-            + "?content-type=application/json";
+            + CONTENT_TYPE_JSON;
     try
     {
       return new URL(url);
@@ -122,7 +129,7 @@ public class EnsemblLookup extends EnsemblRestClient
    * @param identifier
    * @return
    */
-  public String getParent(String identifier)
+  public String getGeneId(String identifier)
   {
     List<String> ids = Arrays.asList(new String[] { identifier });
 
@@ -155,8 +162,10 @@ public class EnsemblLookup extends EnsemblRestClient
   }
 
   /**
-   * Parses "Parent" from the JSON response and returns the value, or null if
-   * not found
+   * Parses the JSON response and returns the gene identifier, or null if not
+   * found. If the returned object_type is Gene, returns the id, if Transcript
+   * returns the Parent. If it is Translation (peptide identifier), then the
+   * Parent is the transcript identifier, so we redo the search with this value.
    * 
    * @param br
    * @return
@@ -164,17 +173,42 @@ public class EnsemblLookup extends EnsemblRestClient
    */
   protected String parseResponse(BufferedReader br) throws IOException
   {
-    String parent = null;
+    String geneId = null;
     JSONParser jp = new JSONParser();
     try
     {
       JSONObject val = (JSONObject) jp.parse(br);
-      parent = val.get("Parent").toString();
+      String type = val.get(OBJECT_TYPE).toString();
+      if (OBJECT_TYPE_GENE.equalsIgnoreCase(type))
+      {
+        geneId = val.get(ID).toString();
+      }
+      else if (OBJECT_TYPE_TRANSCRIPT.equalsIgnoreCase(type))
+      {
+        geneId = val.get(PARENT).toString();
+      }
+      else if (OBJECT_TYPE_TRANSLATION.equalsIgnoreCase(type))
+      {
+        String transcriptId = val.get(PARENT).toString();
+        try
+        {
+          geneId = getGeneId(transcriptId);
+        } catch (StackOverflowError e)
+        {
+          /*
+           * unlikely data condition error!
+           */
+          System.err
+                  .println("** Ensembl lookup "
+                          + getUrl(transcriptId).toString()
+                          + " looping on Parent!");
+        }
+      }
     } catch (ParseException e)
     {
       // ignore
     }
-    return parent;
+    return geneId;
   }
 
 }
index 1554a0b..99006aa 100644 (file)
@@ -23,8 +23,6 @@ package jalview.ext.ensembl;
 import jalview.datamodel.AlignmentI;
 import jalview.datamodel.SequenceFeature;
 
-import java.util.List;
-
 import com.stevesoft.pat.Regex;
 
 /**
index c06d13e..b1bc8e5 100644 (file)
@@ -43,8 +43,6 @@ import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.json.simple.parser.JSONParser;
 
-import com.stevesoft.pat.Regex;
-
 /**
  * Base class for Ensembl REST service clients
  * 
@@ -68,9 +66,9 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
    * @see https://github.com/Ensembl/ensembl-rest/wiki/Change-log
    * @see http://rest.ensembl.org/info/rest?content-type=application/json
    */
-  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "5.0";
+  private static final String LATEST_ENSEMBLGENOMES_REST_VERSION = "6.0";
 
-  private static final String LATEST_ENSEMBL_REST_VERSION = "5.0";
+  private static final String LATEST_ENSEMBL_REST_VERSION = "6.1";
 
   private static final String REST_CHANGE_LOG = "https://github.com/Ensembl/ensembl-rest/wiki/Change-log";
 
@@ -83,18 +81,11 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 
   private final static long VERSION_RETEST_INTERVAL = 1000L * 3600; // 1 hr
 
-  private static final Regex PROTEIN_REGEX = new Regex(
-          "(ENS)([A-Z]{3}|)P[0-9]{11}$");
-
-  private static final Regex TRANSCRIPT_REGEX = new Regex(
-          "(ENS)([A-Z]{3}|)T[0-9]{11}$");
-
-  private static final Regex GENE_REGEX = new Regex(
-          "(ENS)([A-Z]{3}|)G[0-9]{11}$");
+  protected static final String CONTENT_TYPE_JSON = "?content-type=application/json";
 
   static
   {
-    domainData = new HashMap<String, EnsemblInfo>();
+    domainData = new HashMap<>();
     domainData.put(ENSEMBL_REST,
             new EnsemblInfo(ENSEMBL_REST, LATEST_ENSEMBL_REST_VERSION));
     domainData.put(ENSEMBL_GENOMES_REST, new EnsemblInfo(
@@ -121,42 +112,6 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     setDomain(d);
   }
 
-  /**
-   * Answers true if the query matches the regular expression pattern for an
-   * Ensembl transcript stable identifier
-   * 
-   * @param query
-   * @return
-   */
-  public boolean isTranscriptIdentifier(String query)
-  {
-    return query == null ? false : TRANSCRIPT_REGEX.search(query);
-  }
-
-  /**
-   * Answers true if the query matches the regular expression pattern for an
-   * Ensembl protein stable identifier
-   * 
-   * @param query
-   * @return
-   */
-  public boolean isProteinIdentifier(String query)
-  {
-    return query == null ? false : PROTEIN_REGEX.search(query);
-  }
-
-  /**
-   * Answers true if the query matches the regular expression pattern for an
-   * Ensembl gene stable identifier
-   * 
-   * @param query
-   * @return
-   */
-  public boolean isGeneIdentifier(String query)
-  {
-    return query == null ? false : GENE_REGEX.search(query);
-  }
-
   @Override
   public boolean queryInProgress()
   {
@@ -218,8 +173,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     {
       // note this format works for both ensembl and ensemblgenomes
       // info/ping.json works for ensembl only (March 2016)
-      URL ping = new URL(
-              getDomain() + "/info/ping?content-type=application/json");
+      URL ping = new URL(getDomain() + "/info/ping" + CONTENT_TYPE_JSON);
 
       /*
        * expect {"ping":1} if ok
@@ -228,6 +182,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
       br = getHttpResponse(ping, null, 2 * 1000);
       if (br == null)
       {
+        // error reponse status
         return false;
       }
       JSONParser jp = new JSONParser();
@@ -506,9 +461,12 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
     URL url = null;
     try
     {
-      url = new URL(
-              getDomain() + "/info/rest?content-type=application/json");
+      url = new URL(getDomain() + "/info/rest" + CONTENT_TYPE_JSON);
       BufferedReader br = getHttpResponse(url, null);
+      if (br == null)
+      {
+        return;
+      }
       JSONObject val = (JSONObject) jp.parse(br);
       String version = val.get("release").toString();
       String majorVersion = version.substring(0, version.indexOf("."));
@@ -571,8 +529,7 @@ abstract class EnsemblRestClient extends EnsemblSequenceFetcher
 
     try
     {
-      url = new URL(
-              getDomain() + "/info/data?content-type=application/json");
+      url = new URL(getDomain() + "/info/data" + CONTENT_TYPE_JSON);
       br = getHttpResponse(url, null);
       if (br != null)
       {
index e2e38b5..75598a0 100644 (file)
@@ -42,6 +42,10 @@ import org.json.simple.parser.ParseException;
  */
 public class EnsemblSymbol extends EnsemblXref
 {
+  private static final String GENE = "gene";
+  private static final String TYPE = "type";
+  private static final String ID = "id";
+
   /**
    * Constructor given the target domain to fetch data from
    * 
@@ -73,8 +77,9 @@ public class EnsemblSymbol extends EnsemblXref
       while (rvals.hasNext())
       {
         JSONObject val = (JSONObject) rvals.next();
-        String id = val.get("id").toString();
-        if (id != null && isGeneIdentifier(id))
+        String id = val.get(ID).toString();
+        String type = val.get(TYPE).toString();
+        if (id != null && GENE.equals(type))
         {
           result = id;
           break;
@@ -87,12 +92,31 @@ public class EnsemblSymbol extends EnsemblXref
     return result;
   }
 
-  protected URL getUrl(String id, Species species)
+  /**
+   * Constructs the URL for the REST symbol endpoint
+   * 
+   * @param id
+   *          the accession id (Ensembl or external)
+   * @param species
+   *          a species name recognisable by Ensembl
+   * @param type
+   *          an optional type to filter the response (gene, transcript,
+   *          translation)
+   * @return
+   */
+  protected URL getUrl(String id, Species species, String... type)
   {
-    String url = getDomain() + "/xrefs/symbol/" + species.toString() + "/"
-            + id + "?content-type=application/json";
+    StringBuilder sb = new StringBuilder();
+    sb.append(getDomain()).append("/xrefs/symbol/")
+            .append(species.toString()).append("/").append(id)
+            .append(CONTENT_TYPE_JSON);
+    for (String t : type)
+    {
+      sb.append("&object_type=").append(t);
+    }
     try
     {
+      String url = sb.toString();
       return new URL(url);
     } catch (MalformedURLException e)
     {
@@ -107,7 +131,7 @@ public class EnsemblSymbol extends EnsemblXref
    * @param identifier
    * @return
    */
-  public List<String> getIds(String identifier)
+  public List<String> getGeneIds(String identifier)
   {
     List<String> result = new ArrayList<String>();
     List<String> ids = new ArrayList<String>();
@@ -121,14 +145,15 @@ public class EnsemblSymbol extends EnsemblXref
       {
         for (Species taxon : Species.getModelOrganisms())
         {
-          URL url = getUrl(query, taxon);
+          URL url = getUrl(query, taxon, GENE);
           if (url != null)
           {
             br = getHttpResponse(url, ids);
             if (br != null)
             {
               String geneId = parseSymbolResponse(br);
-              if (geneId != null)
+              System.out.println(url + " returned " + geneId);
+              if (geneId != null && !result.contains(geneId))
               {
                 result.add(geneId);
               }
index c002c08..27c448e 100644 (file)
@@ -171,16 +171,13 @@ class EnsemblXref extends EnsemblRestClient
       while (rvals.hasNext())
       {
         JSONObject val = (JSONObject) rvals.next();
-        String dbname = val.get("dbname").toString();
-        if (GO_GENE_ONTOLOGY.equals(dbname))
-        {
-          continue;
-        }
+        String db = val.get("dbname").toString();
         String id = val.get("primary_id").toString();
-        if (dbname != null && id != null)
+        if (db != null && id != null
+                && !GO_GENE_ONTOLOGY.equals(db))
         {
-          dbname = DBRefUtils.getCanonicalName(dbname);
-          DBRefEntry dbref = new DBRefEntry(dbname, getXRefVersion(), id);
+          db = DBRefUtils.getCanonicalName(db);
+          DBRefEntry dbref = new DBRefEntry(db, getXRefVersion(), id);
           result.add(dbref);
         }
       }
@@ -214,7 +211,7 @@ class EnsemblXref extends EnsemblRestClient
   protected URL getUrl(String identifier)
   {
     String url = getDomain() + "/xrefs/id/" + identifier
-            + "?content-type=application/json&all_levels=1";
+            + CONTENT_TYPE_JSON + "&all_levels=1";
     try
     {
       return new URL(url);
index 96dfcfe..50aba62 100644 (file)
@@ -975,6 +975,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel
         notifyAtomPicked(((Integer) data[2]).intValue(), (String) data[1],
                 (String) data[0]);
         // also highlight in alignment
+        // deliberate fall through
       case HOVER:
         notifyAtomHovered(((Integer) data[2]).intValue(), (String) data[1],
                 (String) data[0]);
index f923f7f..39d6704 100644 (file)
@@ -120,7 +120,7 @@ public class AtomSpecModel
 
       for (String chain : modelData.keySet())
       {
-        chain = chain.trim();
+        chain = " ".equals(chain) ? chain : chain.trim();
 
         List<int[]> rangeList = modelData.get(chain);
 
@@ -192,9 +192,10 @@ public class AtomSpecModel
     {
       sb.append(start).append("-").append(end);
     }
-    if (chain.length() > 0)
-    {
-      sb.append(".").append(chain);
+
+    sb.append(".");
+    if (!" ".equals(chain)) {
+      sb.append(chain);
     }
   }
 }
index e7e1202..b2c4809 100644 (file)
@@ -632,9 +632,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
       }
       else
       {
-        int widthInRes = (canvasWidth / av.getCharWidth()) - 1;
+        int widthInRes = (canvasWidth / av.getCharWidth());
         int heightInSeq = (getSeqPanel().seqCanvas.getHeight()
-                / av.getCharHeight()) - 1;
+                / av.getCharHeight());
 
         vpRanges.setViewportWidth(widthInRes);
         vpRanges.setViewportHeight(heightInSeq);
index c5454ab..b768339 100644 (file)
@@ -131,6 +131,11 @@ public class FeatureSettings extends JPanel
   private static final int MIN_WIDTH = 400;
 
   private static final int MIN_HEIGHT = 400;
+  
+  /**
+   * when true, constructor is still executing - so ignore UI events
+   */
+  protected volatile boolean inConstruction = true;
 
   /**
    * Constructor
@@ -303,6 +308,7 @@ public class FeatureSettings extends JPanel
               };
             });
     frame.setLayer(JLayeredPane.PALETTE_LAYER);
+    inConstruction = false;
   }
 
   protected void popupSort(final int selectedRow, final String type,
@@ -1237,8 +1243,11 @@ public class FeatureSettings extends JPanel
       @Override
       public void stateChanged(ChangeEvent evt)
       {
-        fr.setTransparency((100 - transparency.getValue()) / 100f);
-        af.alignPanel.paintAlignment(true, true);
+        if (!inConstruction)
+        {
+          fr.setTransparency((100 - transparency.getValue()) / 100f);
+          af.alignPanel.paintAlignment(true,true);
+        }
       }
     });
 
index f16522f..1f47da3 100644 (file)
@@ -216,7 +216,8 @@ public class FeatureRenderer extends FeatureRendererModel
       return null;
     }
 
-    if (Comparison.isGap(seq.getCharAt(column)))
+    // column is 'base 1' but getCharAt is an array index (ie from 0)
+    if (Comparison.isGap(seq.getCharAt(column - 1)))
     {
       /*
        * returning null allows the colour scheme to provide gap colour
index 23e8cf7..c0cb09c 100644 (file)
@@ -1704,4 +1704,103 @@ public class SequenceTest
     found = sq.findFeatures(10, 11);
     assertEquals(0, found.size());
   }
+
+  @Test(groups = { "Functional" })
+  public void testSetName()
+  {
+    SequenceI sq = new Sequence("test", "-ABC---DE-F--");
+    assertEquals("test", sq.getName());
+    assertEquals(1, sq.getStart());
+    assertEquals(6, sq.getEnd());
+
+    sq.setName("testing");
+    assertEquals("testing", sq.getName());
+
+    sq.setName("test/8-10");
+    assertEquals("test", sq.getName());
+    assertEquals(8, sq.getStart());
+    assertEquals(13, sq.getEnd()); // note end is recomputed
+
+    sq.setName("testing/7-99");
+    assertEquals("testing", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd()); // end may be beyond physical end
+
+    sq.setName("/2-3");
+    assertEquals("", sq.getName());
+    assertEquals(2, sq.getStart());
+    assertEquals(7, sq.getEnd());
+
+    sq.setName("test/"); // invalid
+    assertEquals("test/", sq.getName());
+    assertEquals(2, sq.getStart());
+    assertEquals(7, sq.getEnd());
+
+    sq.setName("test/6-13/7-99");
+    assertEquals("test/6-13", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/0-5"); // 0 is invalid - ignored
+    assertEquals("test/0-5", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/a-5"); // a is invalid - ignored
+    assertEquals("test/a-5", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/6-5"); // start > end is invalid - ignored
+    assertEquals("test/6-5", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/5"); // invalid - ignored
+    assertEquals("test/5", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/-5"); // invalid - ignored
+    assertEquals("test/-5", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/5-"); // invalid - ignored
+    assertEquals("test/5-", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName("test/5-6-7"); // invalid - ignored
+    assertEquals("test/5-6-7", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+
+    sq.setName(null); // invalid, gets converted to space
+    assertEquals("", sq.getName());
+    assertEquals(7, sq.getStart());
+    assertEquals(99, sq.getEnd());
+  }
+
+  @Test(groups = { "Functional" })
+  public void testCheckValidRange()
+  {
+    Sequence sq = new Sequence("test/7-12", "-ABC---DE-F--");
+    assertEquals(7, sq.getStart());
+    assertEquals(12, sq.getEnd());
+
+    /*
+     * checkValidRange ensures end is at least the last residue position
+     */
+    PA.setValue(sq, "end", 2);
+    sq.checkValidRange();
+    assertEquals(12, sq.getEnd());
+
+    /*
+     * end may be beyond the last residue position
+     */
+    PA.setValue(sq, "end", 22);
+    sq.checkValidRange();
+    assertEquals(22, sq.getEnd());
+  }
 }
index a8c491c..5920b89 100644 (file)
@@ -296,4 +296,28 @@ public class EnsemblGeneTest
     assertEquals(-1, fc.compare("coding_exon", "feature_variant"));
     assertEquals(1f, fc.getTransparency());
   }
+
+  @Test(groups = "Network")
+  public void testGetGeneIds()
+  {
+    /*
+     * ENSG00000158828 gene id PINK1 human
+     * ENST00000321556 transcript for the same gene - should not be duplicated
+     * P30419 Uniprot identifier for ENSG00000136448
+     * ENST00000592782 transcript for Uniprot gene - should not be duplicated
+     * BRAF - gene name resolvabe (at time of writing) for 6 model species
+     */
+    String ids = "ENSG00000158828 ENST00000321556 P30419 ENST00000592782 BRAF";
+    EnsemblGene testee = new EnsemblGene();
+    List<String> geneIds = testee.getGeneIds(ids);
+    assertEquals(8, geneIds.size());
+    assertTrue(geneIds.contains("ENSG00000158828"));
+    assertTrue(geneIds.contains("ENSG00000136448"));
+    assertTrue(geneIds.contains("ENSG00000157764")); // BRAF human
+    assertTrue(geneIds.contains("ENSMUSG00000002413")); // mouse
+    assertTrue(geneIds.contains("ENSRNOG00000010957")); // rat
+    assertTrue(geneIds.contains("ENSXETG00000004845")); // xenopus
+    assertTrue(geneIds.contains("ENSDARG00000017661")); // zebrafish
+    assertTrue(geneIds.contains("ENSGALG00000012865")); // chicken
+  }
 }
index aa2c315..e2af26b 100644 (file)
@@ -191,34 +191,6 @@ public class EnsemblSeqProxyTest
 
   }
 
-  @Test(groups = "Functional")
-  public void testIsTranscriptIdentifier()
-  {
-    EnsemblSeqProxy testee = new EnsemblGene();
-    assertFalse(testee.isTranscriptIdentifier(null));
-    assertFalse(testee.isTranscriptIdentifier(""));
-    assertFalse(testee.isTranscriptIdentifier("ENSG00000012345"));
-    assertTrue(testee.isTranscriptIdentifier("ENST00000012345"));
-    assertTrue(testee.isTranscriptIdentifier("ENSMUST00000012345"));
-    assertFalse(testee.isTranscriptIdentifier("enst00000012345"));
-    assertFalse(testee.isTranscriptIdentifier("ENST000000123456"));
-    assertFalse(testee.isTranscriptIdentifier("ENST0000001234"));
-  }
-
-  @Test(groups = "Functional")
-  public void testIsGeneIdentifier()
-  {
-    EnsemblSeqProxy testee = new EnsemblGene();
-    assertFalse(testee.isGeneIdentifier(null));
-    assertFalse(testee.isGeneIdentifier(""));
-    assertFalse(testee.isGeneIdentifier("ENST00000012345"));
-    assertTrue(testee.isGeneIdentifier("ENSG00000012345"));
-    assertTrue(testee.isGeneIdentifier("ENSMUSG00000012345"));
-    assertFalse(testee.isGeneIdentifier("ensg00000012345"));
-    assertFalse(testee.isGeneIdentifier("ENSG000000123456"));
-    assertFalse(testee.isGeneIdentifier("ENSG0000001234"));
-  }
-
   /**
    * Test the method that appends a single allele's reverse complement to a
    * string buffer
index c9e1cad..63d5e4e 100644 (file)
@@ -29,6 +29,11 @@ public class AtomSpecModelTest
     assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
     model.addRange(0, 3, 10, "C"); // subsumes 5-9
     assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B");
+    model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology
+                                    // modelling
+    assertEquals(model.getAtomSpec(),
+            "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35.");
+
   }
 
 }
index b228ba1..2819dbf 100644 (file)
@@ -21,6 +21,7 @@
 package jalview.gui;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
 
 import jalview.bin.Cache;
 import jalview.bin.Jalview;
@@ -218,4 +219,31 @@ public class AlignmentPanelTest
             .getAlignment().getWidth() - 1 - 21); // 21 is the number of hidden
                                                   // columns
   }
+
+  /**
+   * Test that update layout reverts to original (unwrapped) values for endRes
+   * and endSeq when switching from wrapped to unwrapped mode (JAL-2739)
+   */
+  @Test(groups = "Functional")
+  public void TestUpdateLayout_endRes()
+  {
+    // get details of original alignment dimensions
+    ViewportRanges ranges = af.getViewport().getRanges();
+    int endres = ranges.getEndRes();
+
+    // wrap
+    af.alignPanel.getAlignViewport().setWrapAlignment(true);
+    af.alignPanel.updateLayout();
+
+    // endRes changes
+    assertNotEquals(ranges.getEndRes(), endres);
+
+    // unwrap
+    af.alignPanel.getAlignViewport().setWrapAlignment(false);
+    af.alignPanel.updateLayout();
+
+    // endRes and endSeq back to original values
+    assertEquals(ranges.getEndRes(), endres);
+
+  }
 }
index 7fd7abc..f6dfed6 100644 (file)
@@ -2,6 +2,7 @@ package jalview.renderer.seqfeatures;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
@@ -285,6 +286,28 @@ public class FeatureColourFinderTest
   }
 
   @Test(groups = "Functional")
+  public void testFindFeatureAtEnd()
+  {
+    /*
+     * terminal residue feature
+     */
+    seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1",
+            seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb"));
+    fr.setColour("PDBRESNUM", new FeatureColour(Color.red));
+    fr.featuresAdded();
+    av.setShowSequenceFeatures(true);
+
+    /*
+     * final column should have PDBRESNUM feature, the others not
+     */
+    Color c = finder.findFeatureColour(Color.blue, seq,
+            seq.getLength() - 2);
+    assertNotEquals(c, Color.red);
+    c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1);
+    assertEquals(c, Color.red);
+  }
+
+  @Test(groups = "Functional")
   public void testFindFeatureColour_graduatedFeatureColour()
   {
     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2,
index ac9e260..122b8d0 100644 (file)
     <!-- 
        Suppress check of externally sourced code 
     --> 
-    <suppress checks="[a-zA-Z0-9]*" files="com[\\/]*"/>
-    <suppress checks="[a-zA-Z0-9]*" files="ext[\\/]*"/>
-    <suppress checks="[a-zA-Z0-9]*" files="org[\\/]*"/>
-    <suppress checks="[a-zA-Z0-9]*" files="uk[\\/]*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]com[\\/]github*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]com[\\/]stevesoft*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]ext[\\/]edu*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]ext[\\/]vamsas*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]org[\\/]jibble*"/>
+    <suppress checks="[a-zA-Z0-9]*" files="[\\/]uk[\\/]ac*"/>
     
     <!-- 
        ImportControl can only handle one top level package
index 946f71c..c47aaec 100644 (file)
                <subpackage name="datamodel">
                <disallow pkg="jalview.gui"/>
                <allow pkg="fr.orsay.lri.varna"/>
-                       <subpackage name="xdb">
-                               <subpackage name="embl">
-                               <allow pkg="org.exolab.castor"/>
-                           </subpackage>
+                       <subpackage name="xdb.embl">
+                       <allow pkg="org.exolab.castor"/>
                    </subpackage>
            </subpackage>
                
+               <subpackage name="ext">
+                       <subpackage name="ensembl">
+                       <allow pkg="javax.ws"/>
+                       <allow pkg="org.json"/>
+                       </subpackage>
+                       <subpackage name="htsjdk">
+                       <allow pkg="htsjdk"/>
+                       </subpackage>
+                       <subpackage name="jmol">
+                       <allow pkg="MCview"/>
+                       <allow pkg="org.jmol"/>
+                       </subpackage>
+                       <subpackage name="paradise">
+                       <allow pkg="org.apache"/>
+                       <allow pkg="org.json"/>
+                       </subpackage>
+                       <subpackage name="rbvi">
+                       <allow pkg="ext.edu.ucsf"/>
+                       <allow pkg="javax.servlet"/>
+                       </subpackage>
+                       <subpackage name="so">
+                       <allow pkg="org.biojava"/>
+                       </subpackage>
+                       <subpackage name="varna">
+                       <allow pkg="fr.orsay"/>
+                       </subpackage>
+           </subpackage>
+               
                <subpackage name="fts">
                <allow pkg="javax.swing"/>
                <allow pkg="javax.ws"/>
                </subpackage>
 
                <subpackage name="schemes">
-                       <allow pkg="org.exolab.castor"/>
+                       <allow pkg="org.exolab.castor" class="jalview.schemes.ColourSchemeLoader"/>
                </subpackage>
 
                <subpackage name="structure">